/* =============================================================================================================
 * FileName:     CBORD_UI.js
 * Created On:   04/24/2003
 * $Revision: #11 $
 * $Date: 2007/11/30 $
 * See report for revision history.
 * =============================================================================================================
 */
 
 var contactCBORDMsg = "\nPlease select OK and try again. If this issue continues,\nplease contact the helpdesk.";

// changes the height of the dynamically resizeable divs (there always is one when this is called).
function resizeDivs()
{
	var myForm = GetCBORDForm();
	if (myForm != null)
	{
		if (myForm.elements != null)
		{
			var leftOverHeight = getDynamicHeight();
			var numResizableDIVs = document.getElementById('numResizableDIVs');
			if (numResizableDIVs != null && numResizableDIVs.value != 0)
			{
				var resizableDivListString = document.getElementById('resizableDIVs').value;
				var sizePercentListString = document.getElementById('rdSizePercents').value;
				if (numResizableDIVs.value > 1)
				{
					var resizableDivNameArray = resizableDivListString.split(',');
					var sizePercentListArray = sizePercentListString.split(',');
					for (var i=0; i < resizableDivNameArray.length; i++)
					{
						var div = document.getElementById(resizableDivNameArray[i]);
						var newHeight = leftOverHeight;
						if (div != null)
						{
							if (sizePercentListArray[i].length != '' && sizePercentListArray[i] != '*' && sizePercentListArray[i] != '100%')
							{
								if (sizePercentListArray[i].indexOf('%') > 0)
								{
									sizePercentListArray[i]=sizePercentListArray[i].substring(0,sizePercentListArray[i].length-1);
								}
								newHeight = leftOverHeight * (sizePercentListArray[i] / 100);
							}
						}
						if (newHeight > 0)
						{
							div.style.height = newHeight;
						}
					}
				}
				else
				{
					var divName = document.getElementById('resizableDIVs').value;
					if (divName.length > 0)
					{
					    var div = document.getElementById(divName);
					    if (leftOverHeight > 0) 
					    {
						    div.style.height = leftOverHeight;
						    if (div.clientHeight < div.scrollHeight)
						    {
							    applyScrollBarAdjustment(divName,"16px");
							    //applyScrollBarAdjustment(divName,"2px");
						    }
						    else
						    {
							    applyScrollBarAdjustment(divName,"1px");
						    }
					    }
					}
				}
			}
			var div = document.getElementById('navMenu');
			if (div != null)
			{
				var navMenuHeight = document.body.clientHeight - getBannerOffset();
				if (navMenuHeight  > 0)
				{
					div.style.height = navMenuHeight;
				}
			}
		}
	}
}

function applyScrollBarAdjustment(elemName, padding)
{
	//pad the grid header
	elemName="hdr_"+elemName;
	var elem = document.getElementById(elemName);
	if (elem != null)
	{
		if (elem.tagName == "DIV")
		{
			elem.style.marginRight = padding;
		}
		else
		{
			elem.style.width = padding;
		}
	}
	
	//pad any divs specified for scroll bar adjustment
	var  adjustForScrollDIVs = document.getElementById('adjustForScrollDIVs');
	if ( adjustForScrollDIVs != null && adjustForScrollDIVs.Value != null && adjustForScrollDIVs.Value != '')
	{
		var adjustForScrollString = adjustForScrollDIVs.Value;
		var adjustForScrollArray = adjustForScrollString.split(',');
		var i=0;
		var divName, rowCount;
		var column, colId;
		
		while (i < adjustForScrollArray.length)
		{
			divName = adjustForScrollArray[i];
			rowCount = adjustForScrollArray[i+1];
			
			for (var j=0; j<rowCount; j++)
			{
				colId = 'adjCol_' + divName + '_' + j;
				column = document.getElementById(colId);
				if (column != null)
				{
					column.style.paddingRight = "16px";
				}
			}
			i=i+2;
		}
	}
}

// return the height of the banner
function getBannerOffset()
{
	var banner = document.getElementById("NavBanner");
	var bannerHeight = 0;
	if (banner != null)
	{
		bannerHeight = document.getElementById("NavBanner").clientHeight;
	}
	return bannerHeight;
}

// return the leftover height after removing all the known div heights and the banner height
function getDynamicHeight()
{
	var leftOverHeight = document.body.clientHeight;
	var numNonAbsoluteDIVs = document.getElementById('numNonAbsoluteDIVs');

	if (numNonAbsoluteDIVs != null && numNonAbsoluteDIVs.value > 0)
	{
		var nonAbsoluteDivListString = document.getElementById('nonAbsoluteDIVs').value;
		var nADArray = nonAbsoluteDivListString.split(',');
		var totalFixedHeight = 0;
		for (var i=0; i < nADArray.length; i++)
		{
			var divElem = document.getElementById(nADArray[i]);
			if (divElem != null)
			{
				var divHeight = divElem.clientHeight;
				totalFixedHeight = totalFixedHeight + divHeight;
				
				//if (divElem.currentStyle.visibility != "hidden")
				//{
				//	var divHeight = divElem.clientHeight;
				//	totalFixedHeight = totalFixedHeight + divHeight;
				//}
			}
			else
			{
				reportClientException("Cannot find DIV element with name "+nADArray[i], "getDynamicHeight");
			}
		}
		leftOverHeight = leftOverHeight - totalFixedHeight;
	}
	leftOverHeight = leftOverHeight - getBannerOffset();
	return leftOverHeight;
}

// Find the submit form
function GetCBORDForm()
{
	// default to this form (the one, the only)
	var myForm = document.forms['CBORD'];
	return myForm;
}

function generateAlert(msgID)
{
	var pos = msgID.indexOf("_");
	if (pos > -1)
	{
		pos++;
		msgID = msgID.substr(pos);
	}
	var msg = messages[msgID];
	alert(msg);
}

// process a page link navigation menu click
function processRedirect(where)
{
	try
	{
		window.location=where;
	}
	catch(e)
	{
		reportClientException(e, "processRedirect");
	}
}

// process a hierarchical navigation menu click
function processSelection(id)
{
	try
	{
		var checkType = "RequiredCheck";	//OnlyRequiredCheck is the default behaviour
		if (arguments.length > 1)
		{
			checkType = arguments[1];
		}
		
		if (checkType == null || checkType == "")
		{
			checkType = "RequiredCheck";
		}
		
		var msg = "";
		var processForm = GetCBORDForm();
		if (processForm != null && processForm.elements != null)
		{
			processForm.elements["targetTopicID"].value = id;
			
			switch (checkType){
				case "NoCheck":
					break;
				case "AllCheck":
					msg = checkModifiedNoSubmit(processForm);			
					break;	
				case "RequiredCheck":						
					msg = checkRequiredNoSubmit(processForm);	
					break;
				case "default":
					msg = "Incorrect check type specified in processSelection";
					break;	
			}
			if (msg != null && msg != "")
			{
				alert(msg);
			}
			else
			{
				processAction("NavigationAction");
			}		
		}
	}
	catch(e)
	{
		reportClientException(e, "processSelection");
	}
}

// process a +/- click in a TreeSelect control 
// args:
//		control: id of the control that had the event (aka like a 'this')
//		menuItemId: the id of the menu item that had a +/- clicked
//		topicId: the id of the owning topic
//		panelId: the id of the panel to render (in the callback response)
function processTreeSelectorEvent(controlId, menuItemId, topicId, panelId)
{
	try
	{
		var panel = document.getElementById(panelId);
		if (panel != null)
		{
			var eventHandler = MakeEventHandler();
			// create post data
			var idValuePairs = new Object();
			// add the "topicAction" to the list of items to send back
			idValuePairs["controlID"] = controlId;
			idValuePairs["menuItemId"] = menuItemId;
			idValuePairs["topicID"] = topicId;
			idValuePairs["panelID"] = panelId;
			idValuePairs["topScroll"] = panel.scrollTop;
			idValuePairs["leftScroll"] = panel.scrollLeft;
			// call the server event handler
			eventHandler.call("TreeSelectorAction", idValuePairs, renderControlEventCB);
		}
	}
	catch(e)
	{
		reportClientException(e, "processTreeEvent");
	}
}

// call back function for handling a response to a control event that needs to be re-rendered
function renderControlEventCB(responseNode)
{
	var htmlNode = responseNode.selectSingleNode("HTML");
	if (htmlNode != null)
	{
		var id = htmlNode.getAttribute("id");
		var html = htmlNode.firstChild.data;
		var myElement = document.getElementById(id);
		if (myElement != null)
		{
			myElement.innerHTML = html;
		}
	}
	var oScript = responseNode.selectSingleNode("Script");
	var script = "";
	if (oScript != null)
	{
		script = oScript.firstChild.data;
	}
	if (script.length > 0)
	{
		eval(script);
	}
}

// process a 'select' event in a TreeSelect control
// action: copy the selection to the hidden element for this control so that when
// a 'submit' happens all will be well
function processTreeSelection(id, treeSelectionId, selectAction)
{
	try
	{
		var myElement = document.getElementById(treeSelectionId);
		if (myElement != null)
		{
			myElement.value = id;
			processAction(selectAction);
		}
	}
	catch(e)
	{
		reportClientException(e, "processTreeSelection");
	}
}

function setTreeSelectorToAll(elementId)
{
	try
	{
		var myElement = document.getElementById(elementId);
		if (myElement != null)
		{
			myElement.value = "-2";
		}
	}
	catch(e)
	{
		reportClientException(e, "setTreeSelectorToAll");
	}	
}

// process navigation menu +/- event.
function processMenuEvent(src)
{
	try
	{
		var eventHandler = MakeEventHandler();
		// create post data
		var idValuePairs = new Object();
		// add the "topicAction" to the list of items to send back
		idValuePairs["menuClick"] = src;
		// call the server event handler
		eventHandler.call("ProcessNavMenuClick", idValuePairs, renderControlEventCB);
	}
	catch(e)
	{
		reportClientException(e, "processMenuEvent");
	}
}

//process a hierarchial navigation menu click w/o checking for modified elements.
function processSelectionNoModDataCheck(id)
{
	try
	{
		var processForm = GetCBORDForm();
		if (processForm != null && processForm.elements != null)
		{
			processForm.elements["targetTopicID"].value = id;
			checkRequiredBeforeAction("NavigationAction");
		}
	}
	catch(e)
	{
		reportClientException(e, "processSelectionNoDataCheck");
	}
}

//procss an anchor tag click to direct to a process and set the parm value
function processActionWithParm(action, parm)
{
	try
	{
		// default to the normal search for the submit form
		var myForm = GetCBORDForm();
		if (myForm != null)
		{
			myForm.elements["setSelectedTarget"].value = parm;
			// TODO: should we do this instead?  
			// processSelection(topicId);
			processAction(action);			
		}
	}
	catch (e)
	{
		reportClientException(e, "processActionWithParm");
	}
}

// process an anchor tag click to direct to a topic and set the parm value
function processALink(topicId, parm)
{
	try
	{
		// default to the normal search for the submit form
		var myForm = GetCBORDForm();
		if (myForm != null)
		{
			myForm.elements["setSelectedTarget"].value = parm;
			// TODO: should we do this instead?  
			// processSelection(topicId);
			processSelectionNoModDataCheck(topicId);			
		}
	}
	catch (e)
	{
		reportClientException(e, "processALink");
	}
}

// process a button click on the nav bar (TODO: make this the same as others, if possible)
function checkRequiredBeforeActionFromNavBar(action)
{
	try
	{
		// default to the normal search for the submit form
		var myForm = GetCBORDForm();
		if (myForm != null)
		{
			// call the checkrequired routine
			var alertMessage = checkRequiredInSubmitForm(myForm);
			// if all is ok, just process this button normally (this uses the proper topic, which is not the same as the myForm here)
			if (alertMessage.length == 0)
			{
				processAction(action);
			}
			else
			{
				alert(alertMessage);
			}
		}
	}
	catch(e)
	{
		reportClientException(e, "checkRequiredBeforeActionFromNavBar");
	}
}

// process an action request, but check for any modified data on the form first
function checkModifiedNoSubmit(myForm)
{
	try
	{
		var isDirty = false;
		var returnStatus = "";
		
		//check if any required element is modified
		returnStatus = checkRequiredNoSubmit(myForm);
		if (returnStatus.Length > 0)
		{
			return returnStatus;
		}
		
		//check if any data on the form has changed
		isDirty = isDirtyNoSubmit(myForm);
		if (isDirty)
		{
			returnStatus = "Please Save or Cancel your data before exiting";
			return returnStatus;
		}
		
		//check if any buffer data has been marked as dirty
		for (var i=0; i<myForm.length; i++)
		{
			var myElement=myForm.elements[i];
			if (myElement.type == 'hidden' && myElement.name == 'IsDataModified' && myElement.value == '1')
			{
				isDirty = true;
				//alert("buffer dirty");
				break;
			}	
		}	
		
		if (isDirty)
		{
			returnStatus = "Please Save or Cancel your data before exiting";
		}
		return returnStatus;
	}
	catch(e)
	{
		reportClientException(e, "checkModifiedBeforeAction");
	}
}

// process an action request, but check for required elements first
function checkRequiredBeforeAction(action)
{
	try
	{
		var myForm = GetCBORDForm();
		if (myForm != null)
		{
			var alertMessage = checkRequiredInSubmitForm(myForm);	
			if (alertMessage.length == 0)
			{
				processAction(action);
			}
			else
			{
				alert(alertMessage);
			}
		}
	}
	catch(e)
	{
		reportClientException(e, "checkRequiredBeforeAction");
	}
}

// Check for required elements in the submit form.
function checkRequiredNoSubmit(myForm)
{
	try
	{
		if (myForm != null)
		{
			var alertMessage = checkRequiredInSubmitForm(myForm);
			return alertMessage;
		}
	}
	catch(e)
	{
		reportClientException(e, "CheckRequiredNoSubmit");
	}
}

// internal function to do the work of checking for required values
// the checking includes required values on this submit form.
// returns either a detailed message (something is missing) or the empty string (all ok)
function checkRequiredInSubmitForm(theForm)
{
	var alertMessage = "";
	var numRequired = 0;
	
	if (theForm.elements["numRequired"] != null)
	{
		numRequired = theForm.elements["numRequired"].value;
	}
	
	// if there are required fields, check them
	if (numRequired > 0)
	{
		var reqIDsValue = theForm.elements["requiredIDs"].value;
		var labelsValue = theForm.elements["requiredLabels"].value;
		var reqIDs = reqIDsValue.split(",");
		var labels = labelsValue.split(",");
		
		for (var i=0; i<numRequired; i++)
		{
			var curId = reqIDs[i];
			var reqElement = theForm.elements[curId];
			// element may not be rendered (empty data or some such), don't panic, just ignore it
			if (reqElement != null)
			{
				var reqValue = reqElement.value;
				if (reqElement.className == 'MultiSelect')
				{
				    reqValue = reqElement.innerText;
				}
				
				// trim trailing spaces for required values (biz logic will do this on retrieval!)
				// this will enforce the condition that a required value must be non-blank
				reqValue = trimTrailingChars(reqValue, ' ');
				
				// required value is not there, ERROR
				// for listbox, -1 corresponds to 'select a value' option which means there is no selection yet.
				if (reqValue.length == 0 || (reqElement.className == 'ListBox' && reqValue == -1 ))
				{
					// if this is the first time, setup the alert message, otherwise, add a comma
					if (alertMessage.length == 0)
					{
						alertMessage = messages[mJSRequiredValueMissing];
					}
					else
					{
						alertMessage += ", ";
					}
					alertMessage += labels[i];
				}
			}
		}
	}
	return alertMessage;
}

function processDDMenuActionWithConfirm(selectElem, msg)
{
	try
	{
		// Invariant: the value of the selected dropdown list entry is the action
		var action = selectElem.value;
		var alertMessage = messages[msg];
		if (action.length > 0)
		{
			if (confirm(alertMessage))
			{
				var temp = action.split(",");
				var strings  = temp.length;
				if (temp.length  == 1)
				{
					checkRequiredBeforeActionFromNavBar(action);
					// processAction(action);
				}
				else
				{
					processSelection(temp[0]);
				}
			}
			else
			{
				// user has cancelled, so set the list box back to the 0 index.
				selectElem.selectedIndex=0;
			}
		}
	}
	catch(e)
	{
		reportClientException(e, "processDDMenuAction");
	}
}
// process a drop down menu action
function processDDMenuAction(selectElem)
{
	try
	{
		// Invariant: the value of the selected dropdown list entry is the action
		var action = selectElem.value;
		if (action.length > 0)
		{
			var temp = action.split(",");
			var strings  = temp.length;
			if (temp.length  == 1)
			{
				checkRequiredBeforeActionFromNavBar(action);
				// processAction(action);
			}
			else
			{
				processSelection(temp[0]);
			}
		}
	}
	catch(e)
	{
		reportClientException(e, "processDDMenuAction");
	}
}

// process an action in a submitting frame
function processAction(action)
{
	try
	{
		var myForm = GetCBORDForm();
		if (myForm != null)
		{
			internalProcessAction(myForm,action);
		}
	}
	catch(e)
	{
		reportClientException(e, "processAction");
	}
}

// confirm and then process acton or do nothing
function confirmAction(action, msg)
{
    // if a server request is already in progress just return with wait message. 
	if (ActionHandler.isCallInProgress())
	{
		var msg = messages[mJSPleaseWait];
		//msg = "internalProcessAction in progress: "+action;
		alert(msg);
		return;		// do not submit twice
	}  
   if (confirm(msg))
	{
	     ActionHandler._endSubmit();
		 processAction(action);
	}
}

// function to set action in page, change status and submit the given form
function internalProcessAction(processForm, action)
{
	try
	{
		if (processForm.elements)
		{
			// keep track that a submit was done, and don't allow another one
			if (ActionHandler.isCallInProgress())
			{
				var msg = messages[mJSPleaseWait];
				//msg = "internalProcessAction in progress: "+action;
				alert(msg);
				return;		// do not submit twice
			}
			
			// send Div scroll positions to server
			sendDivsThatScrolled();
			
			// scrape the form for changes
			var idValuePairs = getChangedData(processForm);

			// do the postback call if optional validation succeeded
			if (idValuePairs != null)
			{
				// add the "topicAction" to the list of items to send back
				idValuePairs["topicAction"] = action;
				ActionHandler.call("PostbackAction", idValuePairs, processActionResponseHandler, "", "xml");
			}
		}
		else
		{
			alert("No elements in form");
		}
	}
	catch(e)
	{
		reportClientException(e, "internalProcessAction");
	}
}

// call back to process a response from a PostBack Action
function processActionResponseHandler(responseNode)
{
	// render new page elements (if present)
	var htmlChunkNode = responseNode.selectSingleNode("HTMLChunks");
	if (htmlChunkNode != null)
	{
		var htmlNodeList = htmlChunkNode.childNodes;
		for (var i=0; i<htmlNodeList.length; i++)
		{
			var htmlNode = htmlNodeList[i];
			var id = htmlNode.getAttribute("id");
			var html = htmlNode.firstChild.data;
			var myElement = document.getElementById(id);
			if (myElement != null)
			{
				myElement.innerHTML = html;
			}
			else
			{
				// time to add a new div
				createNewDiv(id, html);
			}
		}
	}
	
	// evaluate script (if present) DO ?
	var oScript = responseNode.selectSingleNode("Script");
	var script = "";
	if (oScript != null)
	{
		script = oScript.firstChild.data;
	}
	if (script.length > 0)
	{
		eval(script);
	}	
}

// helper function to put the passed value into the title tag
function setTitleTag(id)
{
	var titleTag = document.getElementsByTagName("title");
	titleTag[0].text=id;
	titleTag[0].normalize();
}

// helper function to create a div element with the given id and contents
function createNewDiv(id, contents)
{
	var myForm = GetCBORDForm();
	var newDiv = myForm.appendChild(document.createElement('div'));
	newDiv.id = id;
	newDiv.innerHTML = contents;
	return newDiv;
}

// helper function to create a div element with the given id and contents
function createDivIfNeeded(id, contents)
{
    var divElem = document.getElementById(id);
    if (divElem != null)
    {
        divElem.innerHTML = contents;
    }
    else
    {
        divElem = createNewDiv(id, contents);
    }

	return divElem;
}

// onUnloadPage event handler
function processExitPage()
{
	try
	{
		var myForm = GetCBORDForm();
		if (myForm != null)
		{
			if (myForm.elements != null)
			{	
				if (ActionHandler.isCallInProgress())
				{
					return; 
				}
				
				// on exit of page, synch any data changes
				if (isDirtyNoSubmit(myForm))
				{
					// TODO: use proper message
				    return "Unsaved data on page.  Continuing will result in data loss.";
					// internalProcessAction(myForm, 'SynchronizeData');
				}
			}
			else
			{
				// myForm.elements is null - should never get here...
				alert("CBORD error:  elements is null in processExitPage"+contactCBORDMsg);
			}
		}
		else
		{
			// myForm is null - should never get here, either....
			alert("An error has occured in page "+document.URL+contactCBORDMsg);
		}
	}
	catch(e)
	{
		// reportClientException(e, "processExitPage");
		// not safe to call reportClientException from here...
		alert("Error during processExitPage: "+e+contactCBORDMsg);
	}
}

// return true if the given form contains changed (aka dirty) data.
function isDirtyNoSubmit(myForm)
{
	try
	{
		var isDirty=false;
	
		for (var i=0; i<myForm.length; i++)
		{
			var myElement=myForm.elements[i];
			if (myElement.name.length > 0 || myElement.id.length > 0)
			{
			    if(myElement.type=='checkbox')
			    {
				    if(myElement.defaultChecked!=myElement.checked)
				    {
					    isDirty=true;
					    break;
				    }
			    }
			    else if(myElement.type=='radio')
			    {
				    if(myElement.defaultChecked!=myElement.checked)
				    {
					    isDirty=true;
					    break;
				    }
			    }
			    else if (myElement.type=='select-one')
			    {
				    for (j=0; j<myElement.length; j++)
				    {
					    var myOption=myElement.options[j];
					    if(myOption.defaultSelected != myOption.selected)
					    {
						    isDirty=true;
						    break;
					    }
				    }
			    }
			    else if (myElement.type=='select-multiple')
			    {
				    for (j=0; j<myElement.length; j++)
				    {
					    var myOption=myElement.options[j];
					    if(myOption.defaultSelected != myOption.selected)
					    {
						    isDirty=true;
						    break;
					    }
				    }
			    }
			    else if (myElement.type=='text')
			    {
				    if (myElement.value!=myElement.defaultValue)
				    {
					    isDirty=true;
					    break;
				    }
			    }
			}
		}
		return isDirty;			
	}
	catch(e)
	{
		reportClientException(e, "IsDirtyNoSubmit");
	}
}

// return id/value pairs of changed data for sending back to server.
// format = id=value& (where & is a seperator, not terminator)
function getChangedData(myForm)
{
	try
	{
		var idValuePairs=new Object();
	
		for (var i=0; i<myForm.length; i++)
		{
			var myElement=myForm.elements[i];
			switch (myElement.type)
			{
				case 'radio':
					if (myElement.defaultChecked != myElement.checked && myElement.checked)
					{
						// radio buttons should have a name attribute (as that is what makes them part of the same group); use id if name is not present
						var rbName = myElement.name ? myElement.name : myElement.id;
						idValuePairs[rbName] = myElement.value;		// this is critical for proper synch of a Radio button; it should use the name! and the value of the input control is what is returned (for the checked radio button ONLY)
					}
					break;
					
				case 'checkbox':
					if(myElement.defaultChecked != myElement.checked)
					{
						idValuePairs[myElement.id] = myElement.checked;
					}
					break;
					
				case 'select-one':
					for (var j=0; j<myElement.length; j++)
					{
						var myOption=myElement.options[j];
						if(myOption.defaultSelected != myOption.selected && myOption.selected)
						{
							idValuePairs[myElement.id] = encodeURIComponent(myOption.value);		// must encode values because of "special" characters that may be part of the 'value' field of a LB
							break;		// out of for loop
						}
					}
					break;
				
				case 'select-multiple':
					{
						var selectedValues = "";
						for (var j=0; j<myElement.length; j++)
						{
							var myOption=myElement.options[j];
							if(myOption.selected)
							{
								selectedValues += myOption.value + ",";
							}
						}
						if (selectedValues.length > 0)
						{
							idValuePairs[myElement.id] = encodeURIComponent(selectedValues.substr(0,selectedValues.length-1));		// must encode values because of "special" characters that may be typed into text boxes
						}
					}
					break;
					
				case 'text':
				case 'textarea':
				case 'password':
					if (myElement.value != myElement.defaultValue)
					{
						try
						{
							var validate = myElement.attributes["validate"];	// attribute name must match the one used in HTMLUIControl.cs
							if (validate)
							{
								var validationResponse = eval(validate.value);
								if (validationResponse == 1)
								{
									return null;
								}
							}
						}
						catch(e)
						{
							// ignore at this level because otherwise the submit never happens and we never see the internal exception
						}
						var cleansedValue = removeBadCharacters(myElement.value);
						idValuePairs[myElement.id] = encodeURIComponent(cleansedValue);	// must encode values because of "special" characters that may be typed into text boxes
					}
					break;
					
				case 'hidden':
					if (myElement.id != null && myElement.id.length > 0) // guard against non-CBORD hidden elements, like VIEW STATE
					{
						// w3c says no default value for hidden elements, so send 'em all back and check on the server
						var elemVal = myElement.value == null ? "" : myElement.value;
						idValuePairs[myElement.id] = elemVal;
					}
					break;
				default:	// ignore things we don't know about, as we didn't create them
					break;
			} // end swtich
		} // end for each form element
		
		return idValuePairs;
	}
	catch(e)
	{
		reportClientException(e, "getChangedData");
	}
}

// Remove Bad unprintable characters from the given value
// For now, just consider all characters less than x20; keep x09, x0A, and x0D
function removeBadCharacters(value)
{
	var result = "";
	var len = value.length;
	for (var i=0; i<len; i++)
	{
		var curChar = value.charCodeAt(i);
		if (curChar < 32)
		{
			if (curChar == 9 || curChar == 10 || curChar == 13)
			{
				result += value.charAt(i);
			}
			// ignore all others by removing them from the output
		}
		else
		{
			result += value.charAt(i);
		}
	}
	return result;
}

// placeholder for testing.
function validate(id,myForm)
{
	// alert("data change at "+id);
}

// set the status of the owning window from a modal dialog box
function setStatusFromModal(status)
{
	try
	{
		if (window.dialogArguments != null)
		{
			window.dialogArguments.top.status=status;
		}
	}
	catch(e)
	{
		reportClientException(e, "setStatusFromModal");
	}
}

// copy the value from the calendar element into the given element.
function copyFromCalendar(source)
{
	var target = source.parentNode.previousSibling.childNodes[0];
	if (target != null)
	{
		var targetElem = getById(target.name);
	}
	
	// the following conditional is to support the different DOM in Mozilla for schedule selection
	if (targetElem == null)
	{
		targetElem = source.parentNode.previousSibling.childNodes[1];
	}
	
	// the following conditional is to support the different DOM in Mozilla for advanced search selection
	if (targetElem == null)
	{
		targetElem = source.parentNode.previousSibling.previousSibling.childNodes[0];
	}
	
	if (targetElem != null)
	{
		if (messages[mJSDateFormat] == "mdy")
		{ 
			targetElem.value = fnGetMonth()+messages[mJSYearDelimiter]+fnGetDay()+messages[mJSYearDelimiter]+fnGetYear();
		}
		else if (messages[mJSDateFormat] == "dmy")
		{
			targetElem.value=fnGetDay()+messages[mJSYearDelimiter]+fnGetMonth()+messages[mJSYearDelimiter]+fnGetYear();
		}
		else if (messages[mJSDateFormat]  == "ymd") 
		{
			targetElem.value=fnGetYear()+messages[mJSYearDelimiter]+fnGetMonth()+messages[mJSYearDelimiter]+fnGetDay();
		}
		else if (messages[mJSDateFormat] == "ydm")
		{
			targetElem.value=fnGetYear()+messages[mJSYearDelimiter]+fnGetDay()+messages[mJSYearDelimiter]+fnGetMonth();
		}
	}
}

// report an exception during javacript processing (will re-direct to error page)
function reportClientException(e, location)
{
	var exceptionMsg = "";
	if (e.number != null)
	{
		exceptionMsg="script error ("+(e.number  &  0xFFFF)+") : "+e.description+" in "+location;
	}
	else
	{
		exceptionMsg="script error: "+e+" in "+location;
	}
	
	var myForm = GetCBORDForm();
	if (myForm != null)
	{
		var myElement = myForm.elements["clientException"];
		if (myElement != null)
		{
			myElement.value = exceptionMsg;
			//dumpMessageToScreen("clientException: "+exceptionMsg);
			internalProcessAction(myForm, "SynchronizeData");
		}
		else
		{
			alert (exceptionMsg);
		}
	}
	else
	{
		alert (exceptionMsg+" "+window.location);
	}
}

// take a value and stick it in an element, and then process the action
function processLinkAction(elemValue, elemName, action)
{
	try
	{
		var menuForm = GetCBORDForm();
		menuForm.elements[elemName].value = elemValue;
		internalProcessAction(menuForm,action);
	}	
	catch(e)
	{
		reportClientException(e, "processLinkAction - "+action);
	}	
}

// function to immediately go to a Frame topic (not a frameset)
// NOTE: this function will not work if page submits, so watch out for onOnLoadEvents.
function gotoTopic(topicID)
{
	var destination=pageName+'?topicID='+topicID;
	processRedirect(destination);
}

// function to simulate a navigation action for use by advanced search ONLY
function gotoAdvSearchTopic(topicID)
{
	try
	{
		var myForm = GetCBORDForm();
		if (myForm != null && myForm.elements != null)
		{
			myForm.elements["targetTopicID"].value=topicID;
			checkRequiredBeforeAction("NavigationAction");
		}
	}
	catch(e)
	{
		reportClientException(e, "gotoAdvSearchTopic");
	}
}

// go directly to the given topic if not dirty,
// otherwise, add the topicID to the given element and call the normal IsDirty function.
function gotoTopicIfNotDirty(topicID, locationElemID)
{
	var form = GetCBORDForm();
	if (IsDirtyNoSubmit(form))
	{
		// save goto location before submit
		var locationElem = form.elements[locationElemID];
		locationElem.value = topicID;
		
		// use normal IsDirty to get proper processing of actions.
		IsDirty(form);
	}
	else
	{
		// is dirty is false
		gotoTopic(topicID);
	}
}

// check for the enter key and ignore it
// the parameter is ignored
function ignoreEnterKey(ignored, evt)
{
	return checkForEnterKey(null, evt);
}

// check for the enter key and submit the action if pressed
function checkForEnterKey(action,evt)
{
	return checkKey(action,evt,13);
}

// function to check for the given key code and perform the given action if pressed.
function checkKey(action,evt,keyCode)
{
	try
	{
		if (evt)
		{
			var charCode = (evt.charCode) ? evt.charCode :
						((evt.keyCode) ? evt.keyCode :
						((evt.which) ? evt.which : 0));

			if (charCode == keyCode)
			{
				if (action) 
				{
					processAction(action);
				}
				return false;
			}
		}
	}
	catch(e)
	{
		reportClientException(e, "checkKey");
	}
	return true;
}

// remove a trailing colon from a label string
function removeTrailingChar(parm, charToRemove)
{
	if (parm == null) return parm;
	if (typeof(parm) != "string") return parm;
	
	var chrToCheck = parm.substr(parm.length-1,1);
	if (chrToCheck == charToRemove)
	{
		return parm.substr(0,parm.length-1);
	}
	return parm;
}

// Message with replaceable string parms.
function message(msgstr, parms)
{
	var parmsCount= new Number(parms.length);
	var msgLength = msgstr.length;
	var msg = "";
	var i = 0;
	while (i<=msgLength)
	{
		var chr = "";
		chr = msgstr.substr(i,1);
		var chr1 = "";
		if ( i != msgLength)
		{
			chr1 = msgstr.substr(i+1,1);
		}
		else
		{
			chr1 = "%";
		}
		
		if (chr == "%" && chr1 !="%")
		{
			if  (isNaN(Number(chr1)) || Number(chr1)> parmsCount )
			{
				alert("Message function called with invalid replaceable parm specified"+contactCBORDMsg);
			}
			else
			{
				msg = msg+parms[Number(chr1)];
			}
			i++; 
		}
		else if (chr == "%")
		{
			i++; 
		}
		else
		{
			msg = msg+chr;
		}
		i++;
	}
	alert(msg);
}

//TODO: refactor all "check" 
// validate based on regular expression:
//	id = id of input element on page
//	locale = current locale (unused)
//	what = (translated) label associated with this element
//	regExpr = regular expression to use for match
//  msgId = message resource id to use (must have 2 replaceable parms: label and the 'bad character')
// validationContext = whether this came from an onBlur or a submit (1 = onblur, 0 = submit) - must agree with HTMLUIControl.MakeJSValidationCall
// all validation functions MUST return a numeric value for onBlur function chaining (0 = success, 1 = failure)
function checkRegExprAlert(id, locale, what, regExpr, msgId, validationContext)
{
	var elem = getById(id);
	var val = elem.value;
	var re = new RegExp(regExpr);  
	var matched = re.exec(val);
	if (matched == null || matched[0].length != val.length)
	{
		if (validationContext == 0)
		{
			return 1; // failure without alert
		}
	    try
		{
			var parms = new Array(1);
			parms[0]= "'"+removeTrailingChar(what,":")+"'";
			if (typeof(msgId) == "string")
			{
				// convert message id into numeric index
			    msgId = eval(msgId);
			}
        	var alertMessage = messages[msgId];
        	message(alertMessage, parms);
		}
		catch (e)
		{
	    	// alert('exception: '+e);  // ignore exceptions during message generation
			alert("Invalid input: "+val);
		}
		elem.value = elem.defaultValue;
		elem.focus();
	}
	return 0;
}

// Alert user if "what" is not integer numeric and within the given range (inclusive).
// all validation functions MUST return a numeric value for onBlur function chaining
// note: locale argument is currently unused
function checkNumericRangeAlert(id, locale, what, min, minIncluded, max, maxIncluded, validationContext)
{
	var mini= Number(min);
	var maxi = Number(max);
	var ok = isNumericRange(locale, id, min, minIncluded, max, maxIncluded)
	if (!ok)
	{
		if (validationContext == 0)
		{
			return 1; // failure without alert
		}
		var parms = new Array(3);
		parms[0]= "'"+removeTrailingChar(what,":")+"'";
		parms[1]= min;
		parms[2]= max;
		var alertMessage = messages[mJSIntegerRange];
		message(alertMessage,parms);
		var elem = getById(id);
		elem.value = elem.defaultValue;
		elem.focus();
	}
	return 0;
}

// Alert user if "what" is not numeric and within the given range.  Range inclusivness is determined by parms.
// all validation functions MUST return a numeric value for onBlur function chaining
function checkDecimalRangeAlert(id, locale, what, dec, min, minIncluded, max, maxIncluded, validationContext)
{
	var mini= Number(min);
	var maxi = Number(max);
	var decimals = Number(dec);
	var ok = isDecimalRange(locale, id, min, minIncluded, max, maxIncluded, decimals);
	if (!ok)
	{
		if (validationContext == 0)
		{
			return 1; // failure without alert
		}
		var parms = new Array(4);
		parms[0]= "'"+removeTrailingChar(what,":")+"'";
		parms[1]= min;
		parms[2]= max;
		parms[3]= decimals;
		var alertMessage = messages[mJSDecimalRange];
		message(alertMessage,parms);
		var elem = getById(id);
		elem.value = elem.defaultValue;
		elem.focus();
	}
	return 0;
}

// check a value to make sure it is a positive number less than or equal to the given maximum.
// all validation functions MUST return a numeric value for onBlur function chaining
function checkPositiveNumericAlert(id, locale, what, max, validationContext)
{
	try
	{
		var min = "1";
		checkNumericRangeAlert(id, locale, what, min, true, max, true, validationContext);
	}
	catch(e)
	{
		reportClientException(e, "checkPositiveNumericAlert");
	}
	return 0;	
}

/// Alert user if "what" is not numeric having at most numDecimals after the decimal point, and positive (greater than zero).
// all validation functions MUST return a numeric value for onBlur function chaining
function checkPositiveDecimalAlert(id, locale, what, numDecimals, max, validationContext)
{
	try
	{
		var min= "0";
		checkDecimalRangeAlert(id, locale, what, numDecimals, min, false, max, true, validationContext);
	}
	catch(e)
	{
		reportClientException(e, "checkPositiveDecimalAlert");
	}		
	return 0;	
}

/// Alert user if "what" is not numeric having at most numDecimals after the decimal point, and non-negative (greater than or equal to zero).
// all validation functions MUST return a numeric value for onBlur function chaining
function checkNonNegativeDecimalAlert(id, locale, what, numDecimals, max, validationContext)
{
	try
	{
		var min= "0";
		checkDecimalRangeAlert(id, locale, what, numDecimals, min, true, max, true, validationContext);
	}
	catch(e)
	{
		reportClientException(e, "checkNonNegativeDecimalAlert");
	}		
	return 0;	
}

// date validation:
//	id = id of input element on page
//	locale = current locale
//	what = (translated) label associated with this element
//	dummy = (kluge) ignored parameter used to force locale and what to be generated
// all validation functions MUST return a numeric value for onBlur function chaining
function checkDateAlert(id,locale,what,dummy, validationContext)
{
	try
	{
		var ok = isDate(locale, id);
		if (!ok)
		{
			if (validationContext == 0)
			{
				return 1; // failure without alert
			}
			var parms = new Array(1);
			parms[0]= "'"+removeTrailingChar(what,":")+"'";
			var alertMessage = messages[mJSDate];
			message(alertMessage,parms);
			var elem = getById(id);
			elem.value = elem.defaultValue;
			elem.focus();
		}
	}
	catch(e)
	{
		reportClientException(e, "checkDateAlert");
	}		
	return 0;	
}

// Boolean Validation routines

// is this a valid date
function isDate(locale,id)
{
	var elem = getById(id);
	try
	{
		// a blank is a valid date. use check required to check for blank
		if (elem == null || elem.value == "")
		{
			return true;
		}
		var testDate = ParseDate(elem.value);
		if (testDate.length==0)
		{
			return false;
		}
		elem.value =  testDate;                     
		return true;
	}
	catch(e)
	{
		reportClientException(e, "isDate");
	}
}

function ParseDate(s) 
{	
	// split the input into multiple strings
	// Support for 3 alternate deliminters with the first delimiter as the default.
	var v = ""; // return empty string on error.
	var del = messages[mJSYearDelimiter];
	var a1 = s.split(del); 
	del = messages[mJSMonthDelimiter];
	var a2 = s.split(del);
	del = messages[mJSDayDelimiter];
	var a3=s.split(del);
	
	if ((a1.length!=3)&& (a2.length!=3) && (a3.length!=3)) 
	{ 
	
		return v;
	}	 
	if (a1.length==3) 
	{
		var na=a1;
	}
	if (a2.length==3) 
	{
		var na=a2; 
	}
	if (a3.length==3) 
	{
		var na=a3; 
	}

	if (!isPositiveInteger( na[0]) || !isPositiveInteger(na[1]) || !isPositiveInteger(na[2])) 
	{
		return v; 
	}
	
	var d = 0; // day
	var m = 0; // month
	var y =0; // year
	
	if (messages[mJSDateFormat] == "mdy")
	{ 
		m=na[0];
		d=na[1];
		y=na[2];
	}
	if (messages[mJSDateFormat] == "dmy")
	{
		d=na[0];
		m=na[1];
		y=na[2]; 
	}
	if (messages[mJSDateFormat]  == "ymd") 
	{
		y=na[0];
		m=na[1];
		d=na[2];
	}
	if (messages[mJSDateFormat] == "ydm")
	{
		y=na[0];
		d=na[1];
		m=na[2];
	}
	
	// basic check: digits in range
	if ( y<1000 || y.length>4 || m<1 || m>12||d<1 || d>31)
	{		
		return v;
	}
	
	// extended check: parse as datetime in javascript: this allows us to check for invalid days such as 4/30 or 2/30
	var dateTestString = m+'/'+d+'/'+y;
	var dateTest = new Date(dateTestString);
	if (dateTest.getDate() != d)
	{
		return v;
	}
	
	// The default delimiter is in the year bucket.
	v = na[0]+messages[mJSYearDelimiter]+na[1]+messages[mJSYearDelimiter]+na[2]; 
	return v;
}


// date span start field validation:
//	id = id of input element on page
//	locale = current locale
//	what = (translated) label associated with this element
//	dummy = (kluge) ignored parameter used to force locale and what to be generated
// all validation functions MUST return a numeric value for onBlur function chaining
function checkDateSpanStartAlert(id,locale,what,dummy,validationContext)
{
	try
	{
		// valid dates are good
		if (isDate(locale, id))
		{
			return 0;
		}
		
		// number of week (1..7) is ok
		if (isNumericRange(locale, id, 1, 7))
		{
			return 0;
		}
		
		// access value
		var elem = getById(id);
		if (elem == null || elem.value.length == 0)
		{
			return 0;	// no value is OK (use required value checking to enforce non-empty)
		}
		var curValue = elem.value;
		curValue = curValue.toLowerCase();
		
		// today is ok
		var todayStr = messages[mJSToday];
		if (curValue.length>4 && (curValue.substr(0,5) == todayStr.toLowerCase()))
		{
			if (curValue.length>5)
			{
				// so is today-7 or today+5
				var ext = curValue.substr(5);

				if (! isNaN(ext))
				{
					return 0;
				}
			}
			else
			{
				return 0;
			}
		}
		
		// day of week name is OK, that is, one of: monday, tuesday, ... sunday is ok
		var aDay = messages[mJSMonday];
		if (curValue == aDay.toLowerCase())
		{
			return 0;
		}
		aDay = messages[mJSTuesday];
		if (curValue == aDay.toLowerCase())
		{
			return 0;
		}
		aDay = messages[mJSWednesday];
		if (curValue == aDay.toLowerCase())
		{
			return 0;
		}
		aDay = messages[mJSThursday];
		if (curValue == aDay.toLowerCase())
		{
			return 0;
		}
		aDay = messages[mJSFriday];
		if (curValue == aDay.toLowerCase())
		{
			return 0;
		}
		aDay = messages[mJSSaturday];
		if (curValue == aDay.toLowerCase())
		{
			return 0;
		}
		aDay = messages[mJSSunday];
		if (curValue == aDay.toLowerCase())
		{
			return 0;
		}
		
		// Not one of the good things, then it is bad
		if (validationContext == 0)
		{
			return 1; // failure without alert
		}
		var parms = new Array(1);
		parms[0]= "'"+removeTrailingChar(what,":")+"'";
		var alertMessage = messages[mJSDateSpanStart];
		message(alertMessage, parms);
		if (elem != null)
		{
			elem.value = elem.defaultValue;
			elem.focus();
		}
	}
	catch(e)
	{
		reportClientException(e, "checkDateSpanStartAlert");
	}		
	return 0;	
}


/*************************************************************************/ 
/*Function name :isPositiveInteger(theString) */ 
/*Usage of this function :test for an +ve integer */ 
/*Input parameter required:thedata=string for test whether is +ve integer*/ 
/*Return value :if is +ve integer,return true */ 
/* else return false */ 
/*function require :isDigit */ 
/*************************************************************************/ 
function isPositiveInteger(theString) 
{ 
	var theData = new String(theString) 
	if (!isDigit(theData.charAt(0))) 
	if (!(theData.charAt(0)== '+')) 
	return false 

	for (var i = 1; i < theData.length; i++) 
		if (!isDigit(theData.charAt(i))) 
			return false;
	return true;
}

/**********************************************************************/ 
/*Function name :isDigit(theDigit) */ 
/*Usage of this function :test for an digit */ 
/*Input parameter required:thedata=string for test whether is digit */ 
/*Return value :if is digit,return true */ 
/* else return false */ 
/**********************************************************************/ 
function isDigit(theDigit) 
{ 
	var digitArray = new Array('0','1','2','3','4','5','6','7','8','9'); 

	for (var j = 0; j  < digitArray.length; j++) 
		{
			if (theDigit == digitArray[j]) 
				return true ;
		} 
	return false ;
} 

// check numberic input for math expressions
// arguments: elem -- input element to check
function isMath(elem, decimals)
{
	var result = isFraction(elem, decimals)
	result = isMultiplication(elem, decimals)
	return result;
}

// allow a fration expression in numeric input (a/b).
function isFraction(elem, dec)
{
	var decimals = Number(dec)
	var temp = 0;
	var result = elem.value;
	var testvals = result.split("/");
	if (testvals.length == 2)
	{
	  var testvals2 = testvals[0].split(" ");
	  var plusfact = 0;
	  if (testvals2.length == 2)
	  {
		testvals[0]= testvals2[1];
		plusfact = Number(testvals2[0]);
	  }
	  if (isPositiveInteger(testvals[0]) && isPositiveInteger(testvals[1]))
	  {
		temp = Number(testvals[0]) / Number(testvals[1]);
		temp = roundNumber(temp, decimals);
		result = new String(temp + plusfact) ; 
		elem.value = result;
	  }
	}
	return result;
}

// allow a simple multiplication expression in numeric input (a*b).
function isMultiplication(elem, dec)
{
	var decimals = Number(dec)
	var result = elem.value;
	var testvals = result.split("*");
	if (testvals.length == 2)
	{
		if (isPositiveInteger(testvals[0]) && isPositiveInteger(testvals[1]))
		{
			result = new String(Number(testvals[0]) * Number(testvals[1])) ; 
			elem.value = result;
		}
	}
	return result;
}

// Is the value of the form element "id"  numeric and positive.
// arguments:
//		locale		(currently unused)
//		id			id of element in form (must be input element and this is not checked)
//		min			numeric minimum 
//		minIncluded	bool, where true means inclusive range for min; false exclusive
//		max			numeric maximum
//		maxIncluded bool, where true means inclusive range for max; false exclusive
function isNumericRange(locale, id, min, minIncluded, max, maxIncluded)
{
	try
	{
		var elem = getById(id);
		if (elem != null && elem.value.length > 0)
		{
			// n.0 is not allowed, even though it could be converted.
			if (elem.value.indexOf(".") > -1)
			{
				return false;
			}
			var tempVal = isMath(elem,0);
			var tempNum = new Number(tempVal);
			
			// test for number and integer
			if (isNaN(tempNum) || (!isFinite(tempNum)) || (tempNum != parseInt(tempVal,10)) )
			{
				return false;
			}
			// test that number is within the required range (inclusive or exclusive)
			return numberIsInRange(tempNum, min, minIncluded, max, maxIncluded);
		}
		return true;	// empty values are ok
	}
	catch(e)
	{
		reportClientException(e, "isNumericRange");
	}
}

// internal helper function to test a number within a range
function numberIsInRange(tempNum, min, minIncluded, max, maxIncluded)
{
	// use a range of [min, max]
	if (minIncluded && maxIncluded)
	{
		if ( (tempNum < min) || (tempNum > max))
		{
			return false;
		}
	}
	// use a range of [min, max)
	else if (minIncluded)
	{
		if ( (tempNum < min) || (tempNum >= max))
		{
			return false;
		}
	}
	// use a range of (min, max]
	else if (maxIncluded)
	{
		if ( (tempNum <= min) || (tempNum > max))
		{
			return false;
		}
	}
	// use a range of (min, max)
	else
	{
		if ( (tempNum <= min) || (tempNum >= max))
		{
			return false;
		}
	}
	return true;			
}

function roundNumber(num, dec)
{
  var decfact = Number(Math.pow(10, Number(dec)));
  var val = Number(num);
  val = val * decfact;
  val = Math.round(val);
  return   val / decfact;
  
}
	
// Is the value of the form element "id"  nummeric and within the given range (min/max).
function isDecimalRange(locale, id, min, minIncluded, max, maxIncluded, decimals)
{
	try
	{
		var elem = getById(id);
		if (elem != null && elem.value.length > 0)
		{
			var tempVal = isMath(elem,decimals);
			var tempNum = new Number(tempVal);
			// check that value is numeric
			if (isNaN(tempNum) || (!isFinite(tempNum)) )
			{
				return false;
			}
		
			// check number of decimal places
			var str = elem.value;
			var pos = str.indexOf(".") + 1;
			if (pos >0 && (str.length - pos) > decimals)
			{
				return false;
			}
			
			// test that number is within the required range (inclusive or exclusive)
			return numberIsInRange(tempNum, min, minIncluded, max, maxIncluded);
		}
		return true;	// empty values are ok
	}
	catch(e)
	{
		reportClientException(e, "isDecimalRange");
	}
}

// lookup the element in the current document forms collection.
function getById(id)
{
	try
	{
		return document.getElementById(id);
	}
	catch(e)
	{
		var myForm = GetCBORDForm();
		var elem = myForm.elements[id];
		return elem;
	}
}

// function to format a currency value (for use with computed columns)
// val		-- value to format
// symbol	-- currency symbol to use ('' ok)
// groupSep	-- separator character for each group to left of decimal
// decSep	-- separtor for decimal point
// decPlaces-- number of places to right of decimal point
// groupSize-- array of sizes of each group to left of decimal point, can be null, if defined, 1st element is number of dimensions (technical reasons)
// currencyNegativePattern -- number that indicates which patter to use (we support patterns 0 and 1 only).
function formatCurrency(val, symbol, groupSep, decSep, decPlaces, groupSizes, currencyNegativePattern)
{
	// if our value is not really a number or is infinity, just give up and put out nothing
	// (real error handling should be done elsewhere)
	var numForCheck = new Number(val);
	if (isNaN(numForCheck) || !isFinite(numForCheck))
	{
		return symbol;
	}
	
	var decPart = formatDecimal(val,groupSep,decSep,decPlaces,groupSizes,false);
	var retVal = '';
	// must format negative currencies according to locale's rules: 16 patterns for negative currencies, 5 for positive (we support a subset of the available patterns)
	if (numForCheck < 0)
	{
		if (currencyNegativePattern == 0)
		{
			retVal = '(' + symbol + decPart.substring(1) + ')';
		}
		else
		{
			retVal = symbol + decPart;
		}
	}
	else // this is positive currency pattern 0 (only support this one)
	{
		retVal = symbol + decPart;
	}
	return retVal;
}

// function to format a decimal value (for use with computed columns)
// follows rules outlined in NumberFormatInfo for grouping, etc.
// val		-- value to format
// groupSep	-- separator character for each group to left of decimal
// decSep	-- separtor for decimal point
// decPlaces-- number of places to right of decimal point
// groupSize-- array of sizes of each group to left of decimal point, can be null, if defined, 1st element is number of dimensions (technical reasons)
// removeTrailingZeros -- true/false indication of whether to trim trailing zeros
function formatDecimal(val, groupSep, decSep, decPlaces, groupSizes, removeTrailingZeros)
{
	// if our value is not really a number or is infinity, just give up and put out nothing
	// (real error handling should be done elsewhere)
	var numForCheck = new Number(val);
	if (isNaN(numForCheck) || !isFinite(numForCheck))
	{
		return '';
	}
	
	// create value with proper number of decimal places
	var v = val.toFixed(decPlaces);

	// now fix up decimal point character and groups: format left and right parts separately
	var splitVal = v.split('.');
	var leftOfPoint = splitVal[0];
	var left = '';
	var right = '';
	var pos = leftOfPoint.length-1;
	
	// allow no groups at all (groupsizes is not an array)
	if (typeof(groupSizes)=="object")
	{
		var groupSize = 0;
		var gsIndex = 1;	// first element of array is ignored (so that we always have at least two elements for inline constructor)
		var gsLastIndex = groupSizes.length - 1;
		// handle all the sizes before the last one
		while (gsIndex < gsLastIndex)
		{
			groupSize = groupSizes[gsIndex];
			if (pos >= groupSize)
			{
				left = leftOfPoint.substr((pos-groupSize)+1,groupSize) + left;
				pos = pos - groupSize;
				if (pos >= 0) left = groupSep + left;	
			}
			gsIndex++;
		}
		groupSize = groupSizes[gsIndex];
		// last group size takes all the rest
		while (pos >= groupSize)
		{
			left = leftOfPoint.substr((pos-groupSize)+1,groupSize) + left;
			pos = pos - groupSize;
			if (pos >= 0) left = groupSep + left;	
		}
	}
	
	// add remainder
	if (pos >= 0)
	{
		left = leftOfPoint.substr(0,pos+1) + left;
	}
	
	// remove trailing zeros if requested
	right = splitVal[1];
	// just because it could happen... when there is no decimal point (like "Infinity")
	if (right == null)
	{
		right = '';
	}
	
	if (removeTrailingZeros)
	{
		right = trimTrailingChars(right, '0');
	}
	
	// only use a decimal point if something is to the right
	var result = left;
	if (right.length > 0)
	{
		result = result + decSep + right;
	}
	// fixup nagative number
	if (result.substr(0,2)=="-,")
	{
		result = "-" + result.substr(2); 
	}
	return result;
}

// return the given string will all trailing characters removed
function trimTrailingChars(str, theChar)
{
	var result = str;	// start with the given string as the result
	var len = str.length;
	while (len > 0 && str.charAt(len-1) == theChar)
	{
		len--;
	}
	
	if (len == 0)
	{
		result = '';
	}
	else if (len < str.length)
	{
		result = str.substr(0,len);
	}
	return result;
}

// open help
function openHelp(ignored,helpFileName)
{
	try
	{
		window.open(helpFileName,'_blank','height=768,width=1024,top=50,left=50,status=no,toolbar=false,location=no,menubar=no,scrollbars=yes,resizable=yes');
	}
	catch(e)
	{
		reportClientException(e, "openHelp");
	}
}

// special function to allow only one checkbox for admin unit types in a grid
// DEPENDS on layout of grid: we currently use columns 3, 4, 5, and 6
function setAdjacentCheckBoxesInUnitAdminGrid(checkbox)
{
	// create prefix of grid column id (note: format is 'c' + grid# + '_' + row# + '_' + col#)
	var idParts = checkbox.id.split('_');
	var idPrefix = idParts[0] + '_' + idParts[1] + '_';
	for (var i=3; i<7; i++)
	{
		var curId = idPrefix + i;
		var currentCB = document.getElementById(curId);
		
		if (currentCB.id != checkbox.id)
		{
			currentCB.checked = false;
		}
	}
}

// special function to allow only one checkbox for admin unit types in a form
// DEPENDS on layout of form: we currently use form element numbers 7, 8, 9, and 10
function setAdjacentCheckBoxesInUnitAdminForm(checkbox,first)
{
	// create prefix of editForm id (note: format is 'e' + panelName + element#)
	var idPrefix = checkbox.id.substring(0,checkbox.id.length-1);
	for (var i=first; i<first+4; i++)
	{
		var curId = idPrefix + i;
		var currentCB = document.getElementById(curId);

		if (currentCB.id != checkbox.id)
		{
			currentCB.checked = false;
		}
		else
		{
			currentCB.checked = true;
		}
	}
}

function setHDParent(radiobutton)
{
	 var checkbox = radiobutton.parentElement.previousSibling.firstChild;
	 checkbox.checked = false;
}

function setHDChild(checkbox)
{
	 var radiobutton = checkbox.parentElement.nextSibling.firstChild;
	 radiobutton.checked = false; 
}

// function to compute the value of a summary column in an edit grid
// parameters:
//		colInTable		- some column in the table (sibling of column with given index)
//		columnPrefix	- prefix used by grid to identify columns
//		columnIndex		- index of column in row (used as part of generated column id)
//		nonDisplayedSum	- numeric value of summary columns that are not currently displayed
//		rangeStart		- start index of row being displayed (part of generated column id)
//		rangeEnd		- end index of row being displayed (part of generated column id)
//		currencySymbol	- locale currency symbol for value parsing of displayed stuff
//		decimalGroupChar- locale decimal grouping symbol for value parsing of displayed stuff
//		decimalPointChar- locale decimal point symbol for value parsing of displayed stuff
// NOTE: calls to this function are generated by the Edit grid.
function computeGridSummary(colInTable, columnPrefix, columnIndex, nonDisplayedSum, rangeStart, rangeEnd, currencySymbol, decimalGroupChar, decimalPointChar)
{
	try
	{
		var myForm = GetCBORDForm();	
		var sum = new Number(nonDisplayedSum);			// set up accumulator
		// find our table for access to its rows and cell collections
		// structure is: table row[n] cell table row cell[k] element? (where n is number of displayed rows in the grid and k is the number of displayed columns, the last element is optional)
		// alorithm: find table going up
		var myTable = colInTable.parentNode;
		while (myTable != null && myTable.tagName != 'TABLE')
		{
			myTable = myTable.parentNode;
		}
		
		// bad structure, give up
		if (myTable == null) return;
		
		// column ids are generated using a row numbering of rangeStart-1..rangeEnd-1 (think of "Listing rangeStart to rangeEnd of total, but 0 origin")
		var rowIndexInTable = 0;
		var skipRow = 0;
		var rowID = rangeStart-1;
		while (rowID<rangeEnd)
		{
			skipRow = 0;
			// this must match the GenID method in class ColumnElement plus the row ID generated by ColumnGroup class
			// the columns we are looking for may or may NOT be form elements...computed columns are in named td objects
			var colElemId = columnPrefix + '_' + rowID + '_' + columnIndex;
			
			// if column is a form element, it will be here and we are done
			var colValue = myForm.elements[colElemId];
			
			// not a form element, look for it in the table/row/cell object hierarchy
			if (colValue == null)
			{
				var row = myTable.rows[rowIndexInTable];
				if (row != null)
				{
					// For a Grid, the structure of a row is one cell with a single table in it (and nothing else -- hence the childNodes[0]
					// must also account for grids with a group-by clause (and hence grouping headers)
					var rowCell = row.cells[0];
					if (rowCell == null)
					{
						// alert("bad row");
						return;
					}
					var rowTable = rowCell.childNodes[0];
					// grids with column grouping have extra rows 
					if (rowTable.tagName == 'TABLE') 
					{
						// that table has one row, and then the actual columns
						var innerRow = rowTable.rows[0];
						// this is what we expect
						if (innerRow != null && innerRow.tagName == 'TR')
						{
							var col = innerRow.cells[columnIndex];
							if (col != null)
							{
								colValue = col;
								//alert("found "+colElemId+" by table lookup: "+colValue.innerText);
							}
						}
						//else
						//{
							// alert('inner row is not what we expect!');
						//}
					}
					else
					{
						//alert("skipping a row with index "+rowID+": "+rowTable);
						skipRow = 1;
						rowID--;
					}
				}
				//else
				//{
				//	alert("row is null");
				//}
			}
			
			// still nothing, try getElementById (DOM Level 1 function)
			if (colValue == null && skipRow == 0)
			{
				colValue = document.getElementById(colElemId);
			}
			
			if (colValue != null)
			{
				var innerVal = null;
				if (colValue.tagName == 'TD')
				{
					innerVal = colValue.innerHTML;
				}
				else
				{
					innerVal = colValue.value;
				}
				
				if (innerVal == null)
				{
					// alert("did not find a value for "+colValue.id);
					return;		// bad here, then give up
				}
				
				//tranlate a negative number represented by (NNN) into -NNN, remove currency symbol, grouping char's and convert decimal point
				// NOTE: this routine is called with type specific arguments, so it is OK to duplicate the group and point chars
				var colValAsNumber = convertDisplayToNumeric(innerVal, currencySymbol, decimalGroupChar, decimalPointChar, decimalGroupChar, decimalPointChar);

				if (!isNaN(colValAsNumber))
				{
					sum += colValAsNumber;
				}
				//else
				//{
					// alert("value "+innerVal+" is not a number");
				//}
			}
			//else
			//{
				//if (skipRow == 0)
				//{
					// alert("no value found for "+colElemId);
				//}
			//}
			rowIndexInTable++;
			rowID++
			
		}
		return sum;
	}
	catch(e)
	{
		reportClientException(e, "computeGridSummary");
	}
	return 0;	
}

// convert a displayed numeric value into a true number
// display can be currency, negative currency, and may contain a grouping symbol (like a ',')
//		currencySymbol	- locale currency symbol for value parsing of displayed stuff
//		currencyGroupChar- locale currency grouping symbol for value parsing of displayed stuff
//		currencyPointChar- locale currency point symbol for value parsing of displayed stuff
//		decimalGroupChar- locale decimal grouping symbol for value parsing of displayed stuff
//		decimalPointChar- locale decimal point symbol for value parsing of displayed stuff
function convertDisplayToNumeric(displayVal, currencySymbol, currencyGroupChar, currencyPointChar, decimalGroupChar, decimalPointChar)
{
	// already a number, then done
	if (typeof(displayVal) == "number")
	{
		return displayVal;
	}
		
	// this is one of those other types, it must come first in the conditional test sequence
	if (typeof(displayVal) == "boolean" )
	{
		return displayVal;
	}
	
	// have a string, so try removing currency stuff
	if (typeof(displayVal) == "string")
	{
		var retVal = displayVal
		retVal=retVal.replace(" ","");
		if (retVal == "")
		{
			return "Na"
		}
		// is this a currency value (must have currency symbol)
		if (displayVal.indexOf(currencySymbol) != -1)
		{
			//tranlate a negative number represented by (NNN) into -NNN
			retVal = displayVal.replace(')','');
			retVal = retVal.replace('(','-');
			
			// deal with currency by removing the currency symbol,  may not be in first position
			retVal = retVal.replace(currencySymbol,'');
			
			// remove all grouping characters and translate decimal point if necessary
			var removeGroupRE = eval('/\\'+currencyGroupChar+'/g');
			retVal = retVal.replace(removeGroupRE,'');			
			
			if (currencyPointChar != '.')
			{
				var decimalPointRE = eval('/\\'+currencyPointChar+'/g');
				retVal = retVal.replace(decimalPointRE,'.');
			}
		}
		else // consider it a decimal
		{				
			// remove all grouping characters and translate decimal point if necessary
			var removeGroupRE = eval('/\\'+decimalGroupChar+'/g');
			retVal = retVal.replace(removeGroupRE,'');			
			
			if (decimalPointChar != '.')
			{
				var decimalPointRE = eval('/\\'+decimalPointChar+'/g');
				retVal = retVal.replace(decimalPointRE,'.');
			}
		}
		retVal = new Number(retVal);
		return retVal;
	}
	// TODO: what do to with other types?

	return new Number(displayVal);
}

// simple function to show a video in a new window
function showVideo(name,src)
{
	//var url = "./playvid.asp?speed=300&rec="+ src+"&name="+name;
	
	// temp for demo
	var url = "./catfish.html";
	var popupWin = window.open(url,"popup", "scrollbars=no,width=320,height=283,top = 200, left = 300");
	//popupWin.opener.top.name = "opener";
	popupWin.focus();
}

// simple function to show a picture in a new window
function showPicture(name,src)
{
	var url = src;
	var popupWin = window.open(url,"popup", "scrollbars=no,width=320,height=283,top = 200, left = 300");
	//popupWin.opener.top.name = "opener";
	popupWin.focus();
}

function selectPatronByMRN()
{
	var elem = document.getElementById('mrn');
	if (elem != null)
	{
		if (elem.value == null || elem.value.length == 0)
		{
			alert("You must enter a valid MRN to select.");
		}
		else
		{
			document.getElementById('setSelectedTarget').value = "MRN="+elem.value;
			processAction("select");
		}
	}
}

//================== action processing functions ==============================================================

/*
 * ActionHandler is an object that encapsulates the transaction
 *     request and callback logic for processing 'submit' style actions
 *
 * success( ) provides success case logic
 * failure( ) provides failure case logic
 * call( ) calling this member starts the transaction request.
 */

var ActionHandler = {
	// set the scope so that the yahoo connection utility calls our methods properly; see the yahoo documentation
	scope:null,
	// are we currently processing a call?
	_callInProgress:false,
	// client-supplied "how to deal with this response" function
	_processResponse:null,
	// client-supplied arguements for the _processResponse function.
	_processResponseArguements:{},
	// client-supplied response type for the _processResponse function.
	_responseType:null,
	// wait message to use at start
	_waitMsgStart:"Sending request, please wait...",		// TODO: get from resources
	// wait message to use at while processing response
	_waitMsgMid:"Processing response...",		// TODO: get from resources

	// function called if server response was successful
	success:function(o)
	{
		try 
		{
			this._beginSubmit(this._waitMsgMid);
			if(o.responseText != undefined)
			{
				var response = o.responseText;
				//  check the error status of the returned response before evaluating the script
				var xmlDoc =  Sarissa.getDomDocument();
				xmlDoc = (new DOMParser()).parseFromString(response, "text/xml");
				xmlDoc.setProperty("SelectionNamespaces", "xmlns:xsl='http://www.w3.org/1999/XSL/Transform'");
				xmlDoc.setProperty("SelectionLanguage", "XPath");
			
				var errorNode = xmlDoc.selectSingleNode("/Response/Status/Error");
				if (errorNode == null)
				{
					throw "Missing status information in the response from the server";
				}
				
				//If there has been an error redirect to error page
				if (errorNode.firstChild.data != "NONE")
				{
					var javascriptNode = xmlDoc.selectSingleNode("/Response/Status/ErrorScript");
					var script = "";
					
					if (javascriptNode != null)
					{
						script = javascriptNode.firstChild.data;
						if (script == null)
						{
							script = "";
						}
					}
					
					if (script.length > 0)
					{
						eval(script);
					}
				}
				else
				{
					responseNode = xmlDoc.selectSingleNode("/Response/Data");
					switch (this._responseType)
					{
						case "xml":
							//Pass the XML payload of the response to the handler function.
							this._processResponse(responseNode, this._processResponseArguements);
							break;
						case "text":
							//Pass the text payload of the response to the handler function.
							this._processResponse(responseNode.firstChild.data, this._processResponseArguements);
							break;
						default :
							throw "Unknown responseType '"+this._responseType+"' in ActionHandler success call";
					}
				}
			}
		}
		catch (e)
		{
			// we can't call reportClientException(e, "success handler") in a callback
			alert("ActionHandler success callback generated an exception: "+e.name+": "+e.description+contactCBORDMsg);
		}
		finally
		{
			// must be done last....otherwise exit page will cause re-entrent call which is bad, very bad
			this._endSubmit();
		}
	},

	// function called if server response fails
	failure:function(o)
	{
		var result = "A "+o.statusText+" error has occured with the error code: "+o.tId+" "+o.status;
		alert(result+contactCBORDMsg);
		// must be done last....otherwise exit page will cause re-entrent call which is bad, very bad
		this._endSubmit();
		// try to recover by refreshing the page (DOES NOT WORK)
		// window.location = window.location;
	},

	/*
	* Builds the parameters to pass in the request
	* toAppendData: Associative array of the data to be included
	*/
	_buildRequestParms:function(toAppendData)
	{
		var postData = "";
		for (var obj in toAppendData)
		{
			var parmName = obj;
			var parmValue = toAppendData[obj];
						
			if (parmName == null || parmName.length == 0)
			{
				throw "Empty parm passed to ActionHandler request";
			}
			postData = postData + "&" + parmName + "=" + parmValue;
		}		
		return postData;			
	},

	// function to call a server method and process the response.
	call:function(action, idValuePairs, responseHandler, responseHandlerArgs, responseType)
	{
		//set the scope of this to our instance.
		this.scope = this;
		// save client-supplied response handling info
		this._processResponse = responseHandler;
		this._processResponseArguements = responseHandlerArgs;
		this._responseType = responseType;
		var postData = "action="+action+"&responseType="+responseType+this._buildRequestParms(idValuePairs);
		// this needs to be the last thing you do before you call the async request.
		this._beginSubmit(this._waitMsgStart);
		YAHOO.util.Connect.asyncRequest("POST", "Ajax.aspx", this, postData);
	},

	// call to change the "in progress indicator"
	_beginSubmit:function(waitMsg)
	{
		window.document.body.style.cursor="wait";
		window.status=waitMsg;
		var waitDiv = document.getElementById('pleaseWait');
		if (waitDiv != null)
		{
			waitDiv.style.visibility = 'visible';
			var waitDivText = document.getElementById('waitText');
			if (waitDivText != null)
			{
				waitDivText.innerHTML = waitMsg;
			}
		}
		this._callInProgress = true;
	},

	// call to hide the "in progress indicator"
	_endSubmit:function()
	{
		window.status="Done.";
		window.document.body.style.cursor="default";
		var waitDiv = document.getElementById('pleaseWait');
		if (waitDiv != null)
		{
			waitDiv.style.visibility = 'hidden';
		}
		this._callInProgress = false;
	},

	isCallInProgress:function()
	{
		return this._callInProgress;
	}

};

function dumpMessageToScreen(message)
{
	targetElement = document.getElementById("contents");
	// can we create the element without this span? can this be done by "reflection"?
	var child = document.createElement("<p>");
	child.innerHTML = message;
	//alert(child.innerHTML);
	targetElement.insertBefore(child,null);
}

// ============== End of ActionHandler Functions =================================================================

// ====================== BEGIN UI Control Event Functions =============================
function MakeEventHandler()
{
	var handler = {};	
	// client-supplied "how to deal with this response" function
	handler._processResponse = null;

	// function called if server response was successful
	handler.success =function(o)
	{
		try 
		{
			if(o.responseText != undefined)
			{
				var response = o.responseText;
				//  check the error status of the returned response before evaluating the script
				var xmlDoc =  Sarissa.getDomDocument();
				xmlDoc = (new DOMParser()).parseFromString(response, "text/xml");
				xmlDoc.setProperty("SelectionNamespaces", "xmlns:xsl='http://www.w3.org/1999/XSL/Transform'");
				xmlDoc.setProperty("SelectionLanguage", "XPath");
			
				var errorNode = xmlDoc.selectSingleNode("/Response/Status/Error");
				if (errorNode == null)
				{
					throw "Missing status information in the response from the server";
				}
				
				//If there has been an error redirect to error page
				if (errorNode.firstChild.data != "NONE")
				{
					var javascriptNode = xmlDoc.selectSingleNode("/Response/Status/ErrorScript");
					var script = "";
					
					if (javascriptNode != null)
					{
						script = javascriptNode.firstChild.data;
						if (script == null)
						{
							script = "";
						}
					}
					
					if (script.length > 0)
					{
						eval(script);
					}
				}
				else
				{
					responseNode = xmlDoc.selectSingleNode("/Response/Data");
					//Pass the XML payload of the response to the handler function.
					this._processResponse(responseNode);
				}
			}
		}
		catch (e)
		{
			// we can't call reportClientException(e, "success handler") in a callback
			alert("EventHandler success function generated an exception: "+e+contactCBORDMsg);
		}
	};

	// function called if server response fails
	handler.failure = function(o)
	{
		var result = o.tId + " " + o.status + " " + o.statusText;
		alert("EventHandler transaction failed.  The error is: " + result+contactCBORDMsg);
	};

	/*
	* Builds the parameters to pass in the request
	* toAppendData: Associative array of the data to be included
	*/
	handler._buildRequestParms = function(toAppendData)
	{
		var postData = "";
		for (var obj in toAppendData)
		{
			var parmName = obj;
			var parmValue = toAppendData[obj];
						
			if (parmName == null || parmName.length == 0)
			{
				throw "Empty parm passed to EventHandler request";
			}
			postData = postData + "&" + parmName + "=" + parmValue;
		}		
		return postData;			
	};

	// function to call a server method and process the response.
	handler.call =function(action, idValuePairs, responseHandler)
	{
		// set the scope so that the yahoo connection utility calls our methods properly; see the yahoo documentation
		this.scope = this;
		// save client-supplied response handling info
		this._processResponse = responseHandler;
		var postData = "action="+action+"&responseType=xml"+this._buildRequestParms(idValuePairs);
		// this needs to be the last thing you do before you call the async request.
		YAHOO.util.Connect.asyncRequest("POST", "Ajax.aspx", this, postData);
	};

	return handler;
}

function clearElement(elementId)
{
	var element = document.getElementById(elementId);
	if (element != null)
	{
		var parentElement = element.parentElement;
		if (parentElement != null)
		{
			// this is the prefered behavior
			parentElement.removeChild(element);
		}
		else
		{
			// this is to evoke equivalent behavior in older browsers
			element.innerHTML = "";
		}
	}
}

// ====================== END UI Control Event Functions =============================


// ====================== BEGIN Floating Panel functionality =============================

function closePanel(panelName)
{
	try
	{
		var panelNameArray = panelName.split(",");

		for (var i=0; i<panelNameArray.length; i++)
		{
			var panelDiv = document.getElementById(panelNameArray[i]);
			if (panelDiv == null) 
			{
				alert("Cannot find "+panelNameArray[i]+contactCBORDMsg);
			}
			else
			{
				panelDiv.style.visibility = "hidden";
			}
		}
		
		var eventHandler = MakeEventHandler();		
		var idValuePairs = new Object();
		idValuePairs["panelName"] = panelName;
		// call the server to handle the event
		eventHandler.call("ClosePanel", idValuePairs, controlEventNullResponse);
	}
	catch (e)
	{
		alert("Something is wrong...refresh page now confirm box");
	}
}

function openPanel(panelName)
{
	try
	{
		var panelNameArray = panelName.split(",");

		for (var i=0; i<panelNameArray.length; i++)
		{
			var panelDiv = document.getElementById(panelNameArray[i]);
			if (panelDiv == null) 
			{
				alert("Cannot find "+panelNameArray[i]+contactCBORDMsg);
			}
			else
			{
				panelDiv.style.visibility = "visible";
			}
		}
		
		var eventHandler = MakeEventHandler();
				
		var idValuePairs = new Object();
		idValuePairs["panelName"] = panelName;
		// call the server to handle the event
		eventHandler.call("OpenPanel", idValuePairs, controlEventNullResponse);
	}
	catch (e)
	{
		alert("Something is wrong...refresh page now confirm box");
	}
}

function swapPanel(panelToOpenId, panelToCloseId, newPanelHeight)
{
	var panelToOpen = document.getElementById(panelToOpenId);
	var panelToClose = document.getElementById(panelToCloseId);
	panelToClose.style.visiblity="hidden";
	panelToClose.style.height=0;
	panelToOpen.style.visibility="visible";
	panelToOpen.style.height=newPanelHeight;
}

// server callable function to set the scroll position of a newly rendered div
function setScrollPosition(divId, scrollTop, scrollLeft)
{
	var divElem = document.getElementById(divId);
	if (divElem != null)
	{
		divElem.scrollTop = scrollTop;
		divElem.scrollLeft = scrollLeft;
	}
}

// handle div scroll events, by remembering which divs have scrolled
var divsThatScrolled = null;

// on Scroll Event
function divOnScroll(divElem)
{
	if (divsThatScrolled == null)
	{
		divsThatScrolled = new Object();
	}
	divsThatScrolled[divElem.id] = divElem.scrollTop+","+divElem.scrollLeft;
}

// function called before a submit that remembers all the div positions
function sendDivsThatScrolled()
{
	if (divsThatScrolled != null)
	{
		for (var divId in divsThatScrolled)
		{
			updateDivScrollPos(divId, divsThatScrolled[divId]);
		}
		divsThatScrolled = null;
	}
}

// send the new posistion to the server
function updateDivScrollPos(divId, scrollPositions)
{
	var scrollPos = scrollPositions.split(",");
	if (scrollPos.length == 2)
	{
		var eventHandler = MakeEventHandler();
		var args = new Object();
		args["divId"] = divId;
		args["scrollTop"] = scrollPos[0];
		args["scrollLeft"] = scrollPos[1];
		eventHandler.call("SetDivScrollPosition", args, controlEventNullResponse);
	}
}

// we do not care what the response is in this case
function controlEventNullResponse(responseNode)
{
}

function setDivStyleClass(divID, styleClassName)
{
	var div = document.getElementById(divID);
	div.className=styleClassName;
}
// ====================== END Floating Panel functionality =============================
