// UNIBASE_XUL.JS - Javascript support for unibase xul applications
//
// Copyright 2005 - Zenucom Pty Ltd
//
// NOTES:	in production this will be renamed to <application>.js and live in the htm or htm/js directory
//		set the application name!
//
// FUNCTIONS (functions not mentioned are internal to script - usually indicated by name prefix of "_")
//
//////////////////////
// Initialisation
//
// ubinit()			First call that does all initialisation stuff
//
//////////////////////
// Many functions are about updating part of the DOM from a call to the server. These are named based on what they are updating and how to get the data.
//
// They can be this document (no prefix), parent, or opener; id, frame, window; and call CGI (ie ubcgi), HTML (pass the HTML directly), or URL (arbitrary).
//
// Do something and get success (OK) or failure (anything else) (synchronous)
//
// actionCGI(cgi)		run a ubcgi program and return result (true/false based on "OK" from URL)
// actionURL(url)		get contents of url and return result (true/false based on "OK" from URL)
//
// Do something and get the data returned (synchronous)
//
// dataCGI(cgi)			run a ubcgi program and return output
// dataURL(url)			return output from a url
// dataPostCGI(cgi, data)	run a ubpost program and return output
// dataPostURL(cgi, data)	return output from a url using post
//
// Save data from a field edit
//
// saveData()
//
// Do something and put output in a frame or some other element (asynchronous)
//
// docCGI(cgi, init)		run a ubcgi program and write it's output to the current document. init => run ubinit after writing document
// docURL(url, init)		get URL and write it's output to the current document
// frameCGI(frame, cgi)		run a ubcgi program (cgi) in frame
// frameURL(frame, url)		load a url into frame
// idCGI(id, cgi, sync)		get output of cgi program to update innerHTML of id. sync - true => wait, false => async
// idHTML(id, html)		write html string directly to innerHTML of id
// idURL(id, url, sync)		get output of URL to update innerHTML of id. sync - true => wait, false => async
// openerFrameCGI(frame, cgi)	run a ubcgi program in frame in opening window
// openerFrameURL(frame, url)	load a url into frame in opening window
// openerIdCGI(id, cgi)		put output of cgi into id of opener
// openerIdHTML(id, html)	write html string directly to innerHTML of id in opening window
// openerIdURL(id, url)		put output of url into id of opener
// parentFrameCGI(frame, cgi)	run a ubcgi program in frame in parent
// parentFrameURL(frame, url)	load a url into frame in parent
// parentIdCGI(id, cgi)		get output of cgi program to update innerHTML of id in parent
// parentIdHTML(id, html)	write html string directly to innerHTML of id in parent
// parentIdURL(id, url)		get output of url to update innerHTML of id in parent
// tableCGI(id, index, cgi)	get xml output of cgi program to add rows to a table before index (-1 for end, 0 for start)
// tableURL(id, index, url)	get xml output of url program to add rows to a table before index (-1 for end, 0 for start)
// windowCGI(cgi)		open a new window with cgi
// windowURL(url)		open a new window with URL
// xmlCGI(cgi)			get xml output of a cgi
// xmlURL(url)			get xml output of a url
//
//////////////////////
//
// Scrolling functions for tables, divs, and frames
// These functions are not called directly and are called on every window resize event
// In all cases these implement 2 additional attributes for div, frame, and table: ubScrollHeight and ubScrollWidth
//
// ubScrollHeight and ubScrollWidth are calculated as a percentage of the viewport from the top left corner of the object
// tables scroll the tbody section
//
//////////////////////
// _domFix()			call _domWalk on event
// _domWalk(node)		traverse the dom tree fixing height and width of nodes
// _objFindHPos(obj)		find position of table on screen
// _objFindVPos(obj)		find position of table on screen
// tableHeight(tab)		get the maximum height of a table
// _divFixHeight()		fix the height of scrollable divs
// _frameFixHeight()		fix the height of scrollable frames
// _tableCollapse()		set up table for collapsable rows
// _tableFixHeight()		fix the height of all the scrollable tables
//
//////////////////////
//
// Other stuff...
// idForm(id, cgi)		get output of cgi program to update innerHTML of id - post data from input and select items
// idPrint(id)			open a new window and print innerHTML of id
// loadCGI(event, frame)	when event fires, run its value as a ubcgi program in a frame
// loadURL(event, frame)	when event fires, load its value into frame
// mainCGI(id, frame, event)	run a ubcgi program from tree item id in frame
// mainURL(id, frame, event)	load a url from tree item id in frame
// numberFormat(numberString)	add commas to a number string
// parentLoadCGI(event, frame)	when event fires, run its value as a ubcgi program in a frame in parent document
// parentLoadURL(event, frame)	when event fires, load its value into frame in parent document
// parentMainCGI(id, frame, event)	run a ubcgi program from tree item id in frame in parent document
// parentMainURL(id, frame, event)	load a url from tree item id in frame in parent document
// TreeWindowURL(id, name, event)	open a new full screen window called name and run the ubcgi program from the value of tree id
// windowSizeCGI(name, width, height, cgi) open a new window of size centered on screen
//

// CONFIGURATION

// END OF CONFIGURATION

var browserVersion = "";
var cgi = "";
var correctTop = false;
var ReservedHeight = 0;			// reserved pixels at the bottom of the screen
var ReservedWidth = 0;			// reserved pixels on the right hand side of the screen
var ubcgi = '/cgi-bin/ubcgi?';

function Browser() {
	var ua, s, i;

	this.isIE	= false;	// Internet Explorer
	this.isOP	= false;	// Opera
	this.isNS	= false;	// Netscape
	this.isSAF	= false;	// Safari
	this.isOSX	= false;	// running on OSX
	this.version = null;

	ua = navigator.userAgent;
	this.isSAF = correctTop = navigator.userAgent.indexOf("Safari") >= 0;
	this.isOSX = navigator.userAgent.indexOf("OS X") >= 0;

	s = "Opera";
	if ((i = ua.indexOf(s)) >= 0) {
		this.isOP = true;
		this.version = parseFloat(ua.substr(i + s.length));
		return;
		}

	s = "Netscape6/";
	if ((i = ua.indexOf(s)) >= 0) {
		this.isNS = true;
		this.version = parseFloat(ua.substr(i + s.length));
		return;
		}

	// Treat any other "Gecko" browser as Netscape 6.1.

	s = "Gecko";
	if ((i = ua.indexOf(s)) >= 0) {
		this.isNS = true;
		this.version = 6.1;
		getBrowserVersion ();
		if (browserVersion.substr(0, 1) == "3") { correctTop = true; }
		return;
		}

	s = "MSIE";
	if ((i = ua.indexOf(s))) {
		this.isIE = true;
		this.version = parseFloat(ua.substr(i + s.length));
		return;
		}
	}

var browser = new Browser();

function getBrowserVersion () {
	var agent;

	if (browserVersion.length != 0) return;

	agent = navigator.userAgent.split(' ');

	for (var i = 0; browserVersion.length == 0 && i < agent.length; ++i) {
		var agentParts;

		agentParts = agent[i].split('/');
		if (agentParts[0] == "Firefox") { browserVersion = agentParts[1]; }
		}
	}

function getViewportSize(el) {
	var size = [0, 0];

	// follow parents of current element until IFRAME, FRAME, or WINDOW

	while (el.offsetParent && el.nodeName != 'IFRAME') { el = el.offsetParent; }

	if (typeof window.innerWidth != 'undefined') {
		size = [ window.innerWidth, window.innerHeight ];
	} else if (typeof document.documentElement != 'undefined'
		&&
		typeof document.documentElement.clientWidth != 'undefined'
		&&
		document.documentElement.clientWidth != 0
		) {
		size = [ document.documentElement.clientWidth, document.documentElement.clientHeight ];
	} else {
		size = [ document.getElementsByTagName('body')[0].clientWidth, document.getElementsByTagName('body')[0].clientHeight ];
		}

	size[0] -= ReservedWidth;
	size[1] -= ReservedHeight;

	return size;
	}

function getTreeValue(tree) {

	getBrowserVersion ();

	if (browserVersion.substr(0, 3) == "1.0") {
		cgi = tree.view.getCellValue(tree.currentIndex, 'col-0');
	} else if (browserVersion.substr(0, 3) == "1.5") {
		cgi = tree.view.getCellValue(tree.currentIndex, tree.columns.getNamedColumn('col-0'));
	} else if (browserVersion.substr(0, 3) == "2.0") {
		cgi = tree.view.getCellValue(tree.currentIndex, tree.columns.getNamedColumn('col-0'));
	} else if (browserVersion.substr(0, 1) == "3") {
		cgi = tree.view.getCellValue(tree.currentIndex, tree.columns.getNamedColumn('col-0'));
	} else {
		// assume old way
		cgi = tree.view.getCellValue(tree.currentIndex, 'col-0');
		}
	}

function isClickOnTreeCell(event, tree) {
	if (event && tree && event.type == "click") {
		var row = {}, col = {}, obj = {};
		tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, obj);
		if (obj.value)
			return true;
		}
	return false;
	}

function fullScreen(url, name) {
	var new_window;

	new_window = window.open(
		url,
		name,
		'screenX=0,'
			+
			'screenY=0,'
			+
			'left=0,'
			+
			'top=0,'
			+
			'width=' + window.screen.availWidth + ','
			+
			'height=' + window.screen.availHeight + ','
			+
			'resizable=1,'
			+
			'status=1,'
			+
			'menubar=0,'
			+
			'toolbar=0,'
			+
			'location=0,'
			+
			'scrollbars=1'
		);
	new_window.focus ();
	}

function sizeScreen(url, name, width, height) {
	var new_window;
	var topX;
	var topY;

	topX = (window.screen.availWidth - width) / 2;
	topY = (window.screen.availHeight - height) / 2;

	new_window = window.open(
		url,
		name,
		'screenX=' + topX +','
			+
			'screenY=' + topY + ','
			+
			'left=' + topX + ','
			+
			'top=' + topY + ','
			+
			'width=' + width + ','
			+
			'height=' + height + ','
			+
			'resizable=1,'
			+
			'status=1,'
			+
			'menubar=0,'
			+
			'toolbar=0,'
			+
			'location=0,'
			+
			'scrollbars=1'
		);
	new_window.focus ();
	}

// get size of something in the css

function styleSize(el, attr) {
	style = window.getComputedStyle(el, null);

	value =style.getPropertyCSSValue(attr);
	var valueType = value.primitiveType;
	switch (valueType) {
		case CSSPrimitiveValue.CSS_NUMBER:
			var floatValue = value.getFloatValue (CSSPrimitiveValue.CSS_NUMBER);
			return Math.ceil(parseFloat(floatValue));
			break;
		case CSSPrimitiveValue.CSS_PERCENTAGE:
			var floatValue = value.getFloatValue (CSSPrimitiveValue.CSS_PERCENTAGE);
			return Math.ceil(parseFloat(floatValue));
			break;
		default:
			if (CSSPrimitiveValue.CSS_EMS <= valueType && valueType <= CSSPrimitiveValue.CSS_DIMENSION) {
				var floatValue = value.getFloatValue (CSSPrimitiveValue.CSS_PX);
				return Math.ceil(parseFloat(floatValue));
			} else {
				return 0;
				}
		}
	}

// Initialisation

function ubinit(reservedHeight, reservedWidth) {
	if (reservedHeight) {ReservedHeight = reservedHeight}
	if (reservedWidth) {ReservedWidth = reservedWidth}

	if (browser.isNS) {
		window.addEventListener('resize',_domFix,false);
	} else if (browser.isIE) {
		window.attachEvent('resize',_domFix);
		}

	_tableCollapse();
	_domFix ();
	}

// CGI and URL functions

function actionCGI(cgi) { return actionURL(ubcgi + cgi); }
function actionURL(url) {
	var	req = null;

	// branch for native XMLHttpRequest object
	if (window.XMLHttpRequest) {
		req = new XMLHttpRequest();
		if (req) {
			req.open("GET", url, false);
			req.send(null);
			}

	// branch for IE/Windows ActiveX version
	} else if (window.ActiveXObject) {
		req = new ActiveXObject("Microsoft.XMLHTTP");
		if (req) {
			req.open("GET", url, false);
			req.send();
			}
		}

	if(req.status == 200) {
		if (req.responseText == "OK") {
			return (true);
		} else {
			alert ("action failed: " + req.responseText + req.responseText.length);
			return (false);
		}
	} else {
		alert ('action failed: ' + req.status);
		}
	}

function dataCGI(cgi) { return dataURL(ubcgi + cgi); }
function dataURL(url) {
	var	req = null;

	// branch for native XMLHttpRequest object
	if (window.XMLHttpRequest) {
		req = new XMLHttpRequest();
		if (req) {
			req.open("GET", url, false);
			req.send(null);
			}

	// branch for IE/Windows ActiveX version
	} else if (window.ActiveXObject) {
		req = new ActiveXObject("Microsoft.XMLHTTP");
		if (req) {
			req.open("GET", url, false);
			req.send();
			}
		}

	if(req.status == 200) { return(req.responseText); } else { return(null); }

	}

function dataUpdateCGI(cgi, data) { return dataUpdateURL(ubcgi + cgi, data); }
function dataUpdateURL(url, data) {
	var	req = null;

	url = url.replace(/ubcgi/, 'ubpostTest');
	data = data.replace(/\n/g, '%0A');

	// branch for native XMLHttpRequest object
	if (window.XMLHttpRequest) {
		req = new XMLHttpRequest();
		if (req) {
			req.open("POST", url, true);
			req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			req.send(data);
			}

	// branch for IE/Windows ActiveX version
	} else if (window.ActiveXObject) {
		req = new ActiveXObject("Microsoft.XMLHTTP");
		if (req) {
			req.open("POST", url, true);
			req.setRequestHeader('Content-type', 'text/plain');
			req.send(data);
			}
		}

	//if(req.status == 200) { return(req.responseText); } else { return(null); }

	}

function dataPostCGI(cgi, data) { return dataPostURL(ubcgi + cgi, data); }
function dataPostURL(url, data) {
	var	req = null;

	url = url.replace(/ubcgi/, 'ubpostTest');
	data = data.replace(/\n/g, '%0A');

	// branch for native XMLHttpRequest object
	if (window.XMLHttpRequest) {
		req = new XMLHttpRequest();
		if (req) {
			req.open("POST", url, false);
			req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			req.send(data);
			}

	// branch for IE/Windows ActiveX version
	} else if (window.ActiveXObject) {
		req = new ActiveXObject("Microsoft.XMLHTTP");
		if (req) {
			req.open("POST", url, false);
			req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			req.send(data);
			}
		}

	if(req.status == 200) { return(req.responseText); } else { return(null); }

	}

function docCGI(cgi, init) { docURL(ubcgi + cgi, init); }
function docURL(url, init) {

	function docWrite() {
		if (req.readyState == 4) {
			if(req.status == 200) {
				if (browser.isNS && browserVersion >= "3.0") {
					document.write(req.responseText);
				} else {
					document.write(req.responseText.substring(req.responseText.indexOf("\n\n"),req.responseText.length - 1));
					}
				document.close();
				if (init) {ubinit ();}
				}
			}
		}

	var	req = null;

	// branch for native XMLHttpRequest object
	if (window.XMLHttpRequest) {
		req = new XMLHttpRequest();
		if (req) {
			req.onreadystatechange = docWrite;
			req.open("GET", url, true);
			req.send(null);
			}

	// branch for IE/Windows ActiveX version
	} else if (window.ActiveXObject) {
		req = new ActiveXObject("Microsoft.XMLHTTP");
		if (req) {
			req.onreadystatechange = docWrite;
			req.open("GET", url, true);
			req.send();
			}
		}
	}

function _idInnerHTML (id, url, sync) {

	function idLoaded() {
		if (req.readyState == 4) {
			if(req.status == 200) {
				if (browser.isNS && browserVersion >= "3.0") {
					targetId.innerHTML = req.responseText;
				} else {
					targetId.innerHTML = req.responseText.substring(req.responseText.indexOf("\n\n"),req.responseText.length - 1);
					}
				ubinit ();
			} else {
				console.log("Error", req.statusText);
				}
			}
		}

	var	req = null;
	var	targetId = id;

	// branch for native XMLHttpRequest object
	if (window.XMLHttpRequest) {
		req = new XMLHttpRequest();
		if (req) {
			req.onreadystatechange = idLoaded;
			if (sync) {
				req.open('GET', url, false);
			} else {
				req.onreadystatechange = idLoaded;
				req.open("GET", url, true);
				}
			req.send(null);
			}

	// branch for IE/Windows ActiveX version
	} else if (window.ActiveXObject) {
		req = new ActiveXObject("Microsoft.XMLHTTP");
		if (req) {
			req.onreadystatechange = idLoaded;
			if (sync) {
				req.open('GET', url, false);
			} else {
				req.onreadystatechange = idLoaded;
				req.open("GET", url, true);
				}
			req.send();
			}
		}

	if(sync && req.status == 200) {
		if (browser.isNS && browserVersion >= "3.0") {
			targetId.innerHTML = req.responseText;
		} else {
			targetId.innerHTML = req.responseText.substring(req.responseText.indexOf("\n\n"),req.responseText.length - 1);
			}
		ubinit ();
	} else {
		console.log("Error", req.statusText);
		}

	}

function idCGI(id, cgi, sync) { idURL(id, ubcgi + cgi, sync); }
function idHTML(id, html) { document.getElementById(id).innerHTML = html; }
function idURL(id, url, sync) { _idInnerHTML(document.getElementById(id), url, sync); }

function _frameContent(contentFrame, url) { contentFrame.setAttribute('src', ''); contentFrame.setAttribute('src', url); }
function frameCGI(frame, cgi) { frameURL(frame, ubcgi + cgi); }
function frameURL(frame, url) { _frameContent(document.getElementById(frame), url); }

function openerIdCGI(id, cgi) { openerIdURL(id, ubcgi + cgi); }
function openerIdHTML(id, html) { window.opener.document.getElementById(id).innerHTML = html; }
function openerIdURL(id, url) { _idInnerHTML(window.opener.document.getElementById(id), url, true); }
function openerFrameCGI(frame, cgi) { openerFrameURL(frame, ubcgi + cgi); }
function openerFrameURL(frame, url) { _frameContent(window.opener.document.getElementById(frame), url); }

function parentIdCGI(id, cgi, sync) { parentIdURL(id, ubcgi + cgi, sync); }
function parentIdHTML(id, html) { parent.document.getElementById(id).innerHTML = html; }
function parentIdURL(id, url, sync) { _idInnerHTML(parent.document.getElementById(id), url, sync); }
function parentFrameCGI(frame, cgi) { parentFrameURL(frame, ubcgi + cgi); }
function parentFrameURL(frame, url) { _frameContent(parent.document.getElementById(frame), url); }

function _loadCell(cell, node) {
	var cellAttributes = node.attributes;
	for (var k = 0; k < cellAttributes.length; ++k) {
		cell.setAttribute(cellAttributes.item(k).nodeName, cellAttributes.item(k).nodeValue);
		}

	var cellHTML = "";
	for (var k = 0; k < node.childNodes.length; ++k) {
		var newNode = node.childNodes[k];
		if (newNode.nodeType == 1) { // ELEMENT NODE
			if (newNode.nodeName == 'script' ) {
				eval (newNode.childNodes[0].nodeValue);
			} else {
				cellHTML += "<" + newNode.nodeName + ' id="' + newNode.id + '"';
				var nodeAttributes = newNode.attributes;
				for (l = 0; l < nodeAttributes.length; ++l) {
				cellHTML += " " + nodeAttributes.item(l).nodeName + '="' + nodeAttributes.item(l).nodeValue + '"';
				}
			cellHTML += ">";
			}
		} else if (newNode.nodeType == 3) { // TEXT_NODE
			cellHTML += newNode.nodeValue;
			}
		}
	cell.innerHTML = cellHTML;
	//cell.childNodes[0].id = node.childNodes[0].id;
	}

function tableCGI(id, index, cgi) { tableURL (id, index, ubcgi + cgi); }
function tableURL(id, index, url) {
	var xmldata = xmlURL(url);
	var nodeElement = document.getElementById(id);

	// documentElement always represents the root node
	var xmlChildren = xmldata.documentElement;

	for (i = 0; i < xmlChildren.childNodes.length; i++) {
		if (xmlChildren.childNodes[i].nodeName == 'tr') {
			var newRow = nodeElement.insertRow(index);
			var rowChildren = xmlChildren.childNodes[i];

			// set attributes

			var rowAttributes = xmlChildren.childNodes[i].attributes;
			for (j = 0; j < rowAttributes.length; ++j) {
				newRow.setAttribute(rowAttributes.item(j).nodeName, rowAttributes.item(j).nodeValue);
				}

			// now row data

			for (j = 0 ; j < rowChildren.childNodes.length; j++) {
				if (rowChildren.childNodes[j].nodeName == 'td' || rowChildren.childNodes[j].nodeName == 'th') {
					var newCell = newRow.insertCell(-1);
					_loadCell(newCell, rowChildren.childNodes[j]);
					}
				}
		} else if (xmlChildren.childNodes[i].nodeName == 'script') {
			eval (xmlChildren.childNodes[i].childNodes[0].nodeValue);
			}
		}
	}

function windowCGI(cgi) { windowURL(ubcgi + cgi); }
function windowURL(url) { window.open(url); }

function xmlCGI(cgi) { return xmlURL(ubcgi + cgi); }
function xmlURL(url) {
	var	req = null;

	// branch for native XMLHttpRequest object
	if (window.XMLHttpRequest) {
		req = new XMLHttpRequest();
		if (req) {
			req.open("GET", url, false);
			req.send(null);
			}

	// branch for IE/Windows ActiveX version
	} else if (window.ActiveXObject) {
		req = new ActiveXObject("Microsoft.XMLHTTP");
		if (req) {
			req.open("GET", url, false);
			req.send();
			}
		}

	if(req.status == 200) { return(req.responseXML); } else { return(null); }

	}

// object scroll height and width fixing

function _domFix () {
	if (browser.isNS) {
		_domShrink (document.getElementsByTagName('BODY')[0]);
		_domWalk (document.getElementsByTagName('BODY')[0]);
		}
	}

function _domShrink (node) {
	if (node.nodeName == 'IFRAME') { _frameShrink(node); }
	if (node.nodeName == 'DIV') { _divShrink(node); }

	var	i;
	for (i = 0; i < node.childNodes.length; ++i) { _domShrink(node.childNodes[i]); }
	}

function _domWalk (node) {
	if (node.nodeName == 'IFRAME') { _frameFixHeight(node); }
	if (node.nodeName == 'DIV') { _divFixHeight(node); }
	if (node.nodeName == 'TABLE') { _tableFixHeight(node); }

	var	i;
	for (i = 0; i < node.childNodes.length; ++i) { _domWalk(node.childNodes[i]); }
	}

function _objFindHPos(obj) {
	var curleft = 0;
	if (obj.offsetParent) {
		curleft = obj.offsetLeft;
		while ((obj = obj.offsetParent) && (obj.nodeName != 'BODY') && (obj.nodeName != 'IFRAME')) { curleft += obj.offsetLeft }
		}
	return curleft;
	}

function _objFindVPos(obj) {
	var curtop = 0;

	if (obj.offsetParent) {
		curtop = obj.offsetTop;
		/* if the object is a table then we have to subtract the height of the content :( */
		if (!correctTop && obj.nodeName == 'TABLE') {
			var theight = tableHeight(obj);
			curtop -= parseInt(theight[0]) - parseInt(theight[1]);
			}
		while ((obj = obj.offsetParent) && (obj.nodeName != 'BODY') && (obj.nodeName != 'IFRAME')) { curtop += obj.offsetTop }
		}
	return curtop;
	}

function _divFixHeight(div) {
	var height;
	var pad = getViewportSize(div);
	var width;
	var winHeight = pad[1];
	var winWidth = pad[0];

	/* this is simple as all we have to do is put up a scroll bar and calculate the height as a percentage of the available height */
	if (height = parseInt(div.getAttribute('ubScrollHeight'))) {
		/* put up the scroll bar */
		if (!div.getAttribute('overflowSet')) { div.setAttribute('overflowSet', '1'); div.style.overflowY = 'scroll'; }

		var newHeight = Math.floor((winHeight - _objFindVPos(div)) * height / 200) * 2;
		newHeight -= styleSize(div, 'margin-top') + styleSize(div, 'margin-bottom') + styleSize(div, 'border-top-width') + styleSize(div, 'border-bottom-width');
		div.style.height = newHeight + "px";
		}

	if (width = parseInt(div.getAttribute('ubScrollWidth'))) {
		var newWidth = Math.floor((winWidth - _objFindHPos(div)) * width / 200) * 2;
		newWidth -= styleSize(div, 'margin-left') + styleSize(div, 'margin-right') + styleSize(div, 'border-left-width') + styleSize(div, 'border-right-width');
		div.style.width = newWidth + "px";
		}
	}

function _divShrink(div) {
	if (div.getAttribute('ubScrollHeight')) {
		div.style.height = "0px";
		}

	if (div.getAttribute('ubScrollWidth')) {
		div.style.width = "0px";
		}
	}

function _frameFixHeight(frame) {
	var height;
	var pad = getViewportSize(frame);
	var width;
	var winHeight = pad[1];
	var winWidth = pad[0];

	/* this is simple as all we have to do is calculate the height as a percentage of the available height */

	/*
	** For compliant browsers we subtract the margin, border, and padding to get the content width and height
	*/

	/* firefox iframes seem to have 4 pixels (2 per side) that must always be counted ! */
	if (height = parseInt(frame.getAttribute('ubScrollHeight'))) {
		var newHeight = Math.floor((winHeight - _objFindVPos(frame)) * height / 200) * 2;
		newHeight -= styleSize(frame, 'margin-top') + styleSize(frame, 'margin-bottom') + styleSize(frame, 'border-top-width') + styleSize(frame, 'border-bottom-width');
		frame.style.height = newHeight + "px";
		}

	if (width = parseInt(frame.getAttribute('ubScrollWidth'))) {
		var newWidth = Math.floor((winWidth - _objFindHPos(frame) - 1) * width / 200) * 2;
		newWidth -= styleSize(frame, 'margin-left') + styleSize(frame, 'margin-right') + styleSize(frame, 'border-left-width') + styleSize(frame, 'border-right-width');
		frame.style.width = newWidth + "px";
		}
	}

function _frameShrink(frame) {
	/* Shrink adjustable frame dimensions to 0 */

	if (frame.getAttribute('ubScrollHeight')) {
		frame.style.height = "0px";
		}

	if (frame.getAttribute('ubScrollWidth')) {
		frame.style.width = "0px";
		}
	}

function tableHeight(tab) {
	/* calculate height of table and add (or use) 2 new attributes:
		_ubHeight: total height of table before we played with it
		_ubReservedHeight: height of bits except tbody so we can calculate new tbody height
	 */

	var height;
	var reservedHeight;

	reservedHeight = height = parseInt(tab.scrollHeight);
	for (i = 0 ; i < tab.childNodes.length; ++i) {
		if (tab.childNodes[i].scrollHeight) {
			if (tab.childNodes[i].style.height != '') {
				height += (h = parseInt(tab.childNodes[i].style.height));
			} else {
				height += (h = parseInt(tab.childNodes[i].scrollHeight));
				}
			if (tab.childNodes[i].nodeName != "TBODY") { reservedHeight += h; }
			}
		}
	return [height, reservedHeight];
	}

/* Search all the tables for any that have attribute collapseLevel defined on a table row
** If found, put an extra column at the star tof every row in the table
*/

function _tableCollapse() {
	var tableIndex;
	var tables = document.getElementsByTagName('TABLE');

	/* Search every table */
	for (tableIndex = 0; tableIndex < tables.length; ++tableIndex) {
		var i;
		var found = false;
		var table = tables[tableIndex];

		/* Search TR nodes for an attribute 'collapseLevel' */
		for (i = 0; i < table.childNodes.length; ++i) {
			var node = table.childNodes[i];
			if (node.nodeName == 'THEAD' || node.nodeName == 'TFOOT' || node.nodeName == 'TBODY') {
				var n;
				for (n = 0; n < node.childNodes.length && !found; ++n) {
					var tr = node.childNodes[n];
					if (tr.nodeName == 'TR' && tr.getAttribute('collapseLevel')) { found = true; }
					}
				}
			}

		/* Add extra column to every row */
		if (found) {
			for (i = 0; i < table.childNodes.length; ++i) {
				var node = table.childNodes[i];
				if (node.nodeName == 'THEAD' || node.nodeName == 'TFOOT' || node.nodeName == 'TBODY') {
					var n;
					for (n = 0; n < node.childNodes.length; ++n) {
						var tr = node.childNodes[n];
						if (tr.nodeName == 'TR') {
							var tdNeeded = true;

							if (tr.childNodes[0].nodeName == 'TD' && tr.childNodes[0].getAttribute('collapseCell') == 'yes') {
								tdNeeded = false;
								}

							if (tdNeeded) {
								var newTD = document.createElement('td');
								newTD.style.width = "1em";
								newTD.setAttribute('collapseCell', 'yes');
								tr.insertBefore(newTD, tr.firstChild);
								if (tr.getAttribute('collapseLevel')) {
									newTD.setAttribute('class', 'collapse');
									newTD.setAttribute('onclick', '_trCollapse(event)');

									/* Add attribute 'collapseState' and give default value 'open'. Other value is 'hide' */
									if (!tr.getAttribute('collapseState')) { tr.setAttribute ('collapseState', 'open'); }
									/* Add attribute 'collapseDirection' and give default value 'after'. Other value is 'before'. */
									if (!tr.getAttribute('collapseDirection')) { tr.setAttribute ('collapseDirection', 'after'); }
									if (tr.getAttribute('collapseState') == 'open') {
										newTD.innerHTML = "-";
									} else {
										newTD.innerHTML = "+";
										_trHide (tr, tr.getAttribute('collapseDirection'));
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}

/* This will give us a % of the distance to the bottom of the page for all tables */
function _tableFixHeight(table) {
	var height;
	var i;
	var pad = getViewportSize(table);
	var tableReserved = 0;
	var winHeight = pad[1];
	var winWidth = pad[0];

	/* Adjust the width first as this affects the height */

	if (width = parseInt(table.getAttribute('ubScrollWidth'))) {
		table.style.width = "1px";	/* set width to 0 to get proper top left */
		var availWidth = winWidth - _objFindHPos(table) - styleSize(table, 'margin-right');	/* _objFindHPos should work for all objects */
		var newWidth = Math.floor(availWidth * width / 100);
		table.style.width = newWidth + "px";
		}

	if (table.getAttribute('ubScrollHeight') || table.getAttribute('ubScrollWidth')) {
		/* find every 'TR' and add a dummy 'TD' class="scrollbar" */
		for (i = 0; i < table.childNodes.length; ++i) {
			var node = table.childNodes[i];
			if (node.nodeName == 'THEAD' || node.nodeName == 'TFOOT' || node.nodeName == 'TBODY') {
				if (node.nodeName == 'THEAD' || node.nodeName == 'TFOOT') { tableReserved += node.scrollHeight; }
				for (n = 0; n < node.childNodes.length; ++n) {
					var tr = node.childNodes[n];
					/* only do this if the attribute "_scrollable" is not present */
					if (tr.nodeName == 'TR' && !tr.getAttribute('_scrollable')) {
						tr.setAttribute('_scrollable', '1');
						var newTD = document.createElement('td');
						newTD.setAttribute('class', 'scrollbar');
						tr.appendChild(newTD);
						}
					}
				}
			}
		}               

	if (height = parseInt(table.getAttribute('ubScrollHeight'))) {
		/* may need to scroll table body */
		/* table location is strange because the table ubScrollHeight does not include the height of the children */

		var availHeight = winHeight - _objFindVPos(table) - styleSize(table, 'margin-bottom') - tableReserved;
		var newHeight = Math.floor(availHeight * height / 100);
		var maxTableHeight = parseInt(tableHeight(table)[0]);
		var tbodyHeight = maxTableHeight < newHeight ? maxTableHeight : newHeight;
		//table.style.height = newHeight + "px";	/* 21.11.2010 */

		/* now find the tbody elements and change their height */
		var numTbody = 0;
		for (i = 0; i < table.childNodes.length; ++i) {
			if (table.childNodes[i].nodeName == 'TBODY') { ++numTbody; }
			}

		for (i = 0; i < table.childNodes.length; ++i) {
			if (table.childNodes[i].nodeName == 'TBODY') {
				var tbody = table.childNodes[i];
				var maxHeight = Math.floor(tbodyHeight / numTbody);

				/* need to store original offsetHeight _ubHeight */
				if (!tbody.getAttribute("_ubHeight")) {
					tbody.setAttribute("_ubHeight", tbody.offsetHeight);
					}
				var offsetHeight = parseInt(tbody.getAttribute("_ubHeight"));
				if (tbody.scrollHeight > maxHeight) tbody.style.height = maxHeight + 'px';
				}
			}
		}
	}

// turn tr collapse on or off - this is a flip-flop
function _trCollapse(e) {
	var tr = e.target.parentNode;
	var state = tr.getAttribute('collapseState');
	var direction = tr.getAttribute('collapseDirection');
	if (state == 'open') {
		_trHide(tr, direction);
		tr.setAttribute('collapseState', 'hide');
		tr.firstChild.innerHTML = "+";
	} else {
		_trShow(tr, direction);
		tr.setAttribute('collapseState', 'open');
		tr.firstChild.innerHTML = "-";
		}

	_domFix();
	}

// hide a row
function _trHide(tr, direction) {
	/* walk siblings while collapseLevel >= current level or collapseLevel is missing */
	var done = false;
	var level = parseInt(tr.getAttribute('collapseLevel'));

	while ((tr = (direction == 'after' ? tr.nextSibling : tr.previousSibling)) && !done) {
		if (tr.nodeType == 1) {
			var newLevel = level + 1;	// in case collapseLevel is missing
			var levelAttribute = tr.getAttribute('collapseLevel');
			if (levelAttribute) { newLevel = parseInt(levelAttribute); }
			if (newLevel > level) { tr.style.display = 'none'; } else { done = true; }
			}
		}
	}

// show a row
function _trShow(tr, direction) {
	/* walk siblings while collapseLevel >= current level or collapseLevel is missing */
	var level = parseInt(tr.getAttribute('collapseLevel'));
	var done = false;

	var data_cgi;
	if (data_cgi = tr.getAttribute('collapseCGI')) {
		var insertNode = tr;
		while (insertNode.nodeName == 'TR' || insertNode.id == '') { insertNode = insertNode.parentNode; }
		tableCGI(insertNode.id, tr.rowIndex - 1, data_cgi);
		tr.removeAttribute('collapseCGI');
		}

	_trShowChildren(tr);

	while ((tr = (direction == 'after' ? tr.nextSibling : tr.previousSibling)) && !done) {
		if (tr.nodeType == 1) {
			var levelAttribute = tr.getAttribute('collapseLevel');
			if (levelAttribute) {
				newLevel = parseInt(levelAttribute);
				if (newLevel <= level) {
					done = true;
				} else if (newLevel == level + 1) {
					tr.style.display = 'table-row';
					if (tr.getAttribute('collapseState') == 'open') { _trShow(tr); }
					}
			} else {
				tr.style.display = 'table-row';
				}
			}
		}
	}

// Show row siblings that don't have a collapse level
function _trShowChildren(tr) {
	var done = false;
	for (tr = tr.nextSibling; !done && tr.nextSibling; tr = tr.nextSibling) {
		if (tr.nodeType == 1) {
			if (!tr.getAttribute('collapseLevel')) { tr.style.display = 'table-row'; } else { done = true; }
			}
		}
	}

// Save data from field edit

function saveData (application, directory, table, key, match, attribute, id, type) {
	element = document.getElementById(id);

	if (element.getAttribute('oldvalue') != element.value) {
		update = '<?xml version="1.0"?><transaction application="'+application+'" directory="'+directory+'"><update table="'+table+'"><key>'+key+'</key><match>'+match+'</match>';
		switch (type) {
			case 'B':	// find checked value and test it against oldvalue. Save if different and then reset oldvalue on all elements
				update += '<value>['+attribute+']="'+element.value+'"</value></update></transaction>';
				var radioSave = document.getElementsByName(element.name);
				for (var i = 0; i < radioSave.length; ++i) { radioSave.item(i).setAttribute('oldvalue', element.value); }
				break;
			case 'N': update += '<value>['+attribute+']='+element.value+'</value></update></transaction>'; break;
			case 'D': update += '<value>['+attribute+']=@'+element.value+'</value></update></transaction>'; break;
			case 'T': update += '<value>['+attribute+']="'+element.value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")+'"</value></update></transaction>'; break;
			}
		dataUpdateCGI('PROC_NAME=updateTransaction', update);
		element.setAttribute('oldvalue', element.value);
		}
	}

// Other things

function idForm(id, cgi) {
	var inputs = document.getElementsByTagName('input');
	var sendString = "";
	var url = ubcgi + cgi;

	for (var n = 0; n < inputs.length; n++) {
		if (sendString.length>0) { sendString += "&"; }
		sendString = sendString + inputs[n].name + '=' + inputs[n].value;
		}

	inputs = document.getElementsByTagName('select');
	for (var n = 0; n < inputs.length; n++) {
		if (sendString.length>0) { sendString += "&"; }
		sendString = sendString + inputs[n].name + '=' + inputs[n].value;
		}

	// branch for native XMLHttpRequest object
	if (window.XMLHttpRequest) {
		req = new XMLHttpRequest();
		if (req) {
			req.open("POST", url, false);
			req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
			req.send(sendString);
		}

	// branch for IE/Windows ActiveX version
	} else if (window.ActiveXObject) {
		req = new ActiveXObject("Microsoft.XMLHTTP");
		if (req) {
			req.open("POST", url, false);
			req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
			req.send(sendString);
			}
		}

	if(req.status == 200)
		if (browser.isNS && browserVersion >= "3.0") {
			document.getElementById(id).innerHTML = req.responseText;
		} else {
			document.getElementById(id).innerHTML = req.responseText.substring(req.responseText.indexOf("\n\n"),req.responseText.length - 1);
			}
	}

function idPrint(id) {
	text = document.getElementById(id).innerHTML;
	winId = window.open('','newwin');
	with (winId.document) {
		write('<head><link rel="stylesheet" type="text/css" href="/' + application + '.css" title="stylesheet"></head>');
		write('<body onLoad="window.focus();window.print();">' + text + '<\/body>');
		close();
		}
	}

function idUpdate(id, html) {
	window.opener.document.getElementById(id).innerHTML=html;
	}

function loadCGI(event, frame) {
	var contentFrame = document.getElementById(frame);
	var url = event.target.getAttribute('value');

	if (url) {
		contentFrame.setAttribute('src', '');
		contentFrame.setAttribute('src', ubcgi + url);
		}
	}

function loadURL(event, frame) {
	var contentFrame = document.getElementById(frame);
	var url = event.target.getAttribute('value');

	if (url) {
		contentFrame.setAttribute('src', '');
		contentFrame.setAttribute('src', url);
		}
	}

function mainCGI(id, frame, event) {
	var contentFrame = document.getElementById(frame);

	tree = document.getElementById(id);
	if (isClickOnTreeCell(event, tree)) {
		getTreeValue(tree);
		contentFrame.setAttribute('src', '');

		if (cgi.length > 0) {
			contentFrame.setAttribute('src', ubcgi + cgi);
			}
		}
	}

function mainURL(id, frame, event) {
	var contentFrame = document.getElementById(frame);

	tree = document.getElementById(id);
	if (isClickOnTreeCell(event, tree)) {
		getTreeValue(tree);
		contentFrame.setAttribute('src', '');

		if (cgi.length > 0) {
			contentFrame.setAttribute('src', cgi);
			}
		}
	}

function numberFormat(numberString) {
	var newString = "";
	ns = numberString.split('.');

	if (ns.length > 1) { newString = '.' + ns[1]; }
	var l = ns[0].length;
	for (i = 1; i <= l; ++i) {
		newString = ns[0][l - i] + newString;
		if (i > 0 && i < l && (i % 3 == 0)) { newString = "," + newString; }
		}

	return (newString);
	}

function parentLoadCGI(event, frame) {
	var contentFrame = parent.document.getElementById(frame);
	var url = event.target.getAttribute('value');

	if (url) {
		contentFrame.setAttribute('src', '');
		contentFrame.setAttribute('src', ubcgi + url);
		}
	}

function parentLoadURL(event, frame) {
	var contentFrame = parent.document.getElementById(frame);
	var url = event.target.getAttribute('value');

	if (url) {
		contentFrame.setAttribute('src', '');
		contentFrame.setAttribute('src', url);
		}
	}

function parentMainCGI(id, frame, event) {
	var contentFrame = parent.document.getElementById(frame);

	tree = document.getElementById(id);
	if (isClickOnTreeCell(event, tree)) {
		getTreeValue(document.getElementById(id));
		contentFrame.setAttribute('src', '');

		if (cgi.length > 0) {
			contentFrame.setAttribute('src', ubcgi + cgi);
			}
		}
	}

function parentMainURL(id, frame, event) {
	var contentFrame = parent.document.getElementById(frame);

	tree = document.getElementById(id);
	if (isClickOnTreeCell(event, tree)) {
		getTreeValue(document.getElementById(id));
		contentFrame.setAttribute('src', '');

		if (cgi.length > 0) {
			contentFrame.setAttribute('src', cgi);
			}
		}
	}

function TreeWindowURL(id, name, event) {

	tree = document.getElementById(id);
	if (isClickOnTreeCell(event, tree)) {
		getTreeValue(document.getElementById(id));
		if (cgi.length > 0) { window.open(ubcgi + cgi, name); }
		}
	}

function windowSizeCGI(name, width, height, cgi) {
	if (cgi) { sizeScreen(ubcgi + cgi, name, width, height); }
	}

