/*============================================================================
OCCA glue.js
Author: Paul Fritze
Modified: Richard Rothwell, 
23/4/2000: getState() and setState() cleaned up.
============================================================================*/	

/*============================================================================
The state description protocol -- SDP.
The state description protocol is used to save the state of a web page
to a database and to restore the state of the web page from the database.  
More precisely the state of the web page is restricted to those input elements
and OCCA components contained within the OCCA form (form[0]). 
The SDP uses a string that represents a Javascript object in its literal form.

An important limitation of the SDP is that all input elements in the OCCA form
must have a unique name unless they are to be treated as an input element array.
In addation each web page must have a unique name so that the page state
can be restored from the corresponding element in the database.
============================================================================*/	

//HTML <A NAME="present"></A>
/*
PUBLIC VARIABLES
Other Javascript modules can test these variables to see if
the glue code has been loaded correctly
*/
var gl_Present = true;
var gl_Version = "2.4";

//HTML <A NAME="strings"></A>
/*============================================================================
Time manipulation
============================================================================*/	

var gl_startTime = new Date();

/*
PRIVATE METHOD
Return a duration since this page was loaded
or since last call to gl_getDuration.
*/
function gl_getDuration()
	{
	var nowTime = new Date();
	var duration = (nowTime.getTime() - gl_startTime.getTime())/1000.0;
	gl_startTime = nowTime;
	}
//HTML <A NAME="strings"></A>
/*============================================================================
String manipulation
============================================================================*/	

/*
PUBLIC METHOD
Return a string with leading spaces removed
*/
function gl_ltrim()
	{
	if(this == '') 
		{
		return this;
		}
	var spaceCount = 0;
	for(var charIndex = 0; charIndex < this.length; charIndex++ )
		{
		if(this.charAt(charIndex) != " ")
			{
			break;
			}
		spaceCount++;
		}
	return this.substring(spaceCount);
	}
String.prototype.ltrim = gl_ltrim;

/*
PUBLIC METHOD
Return a string with trailing spaces removed
*/
function gl_rtrim()
	{
	if(this == '') 
		{
		return this;
		}
	var spaceCount = this.length;
	for(var charIndex = this.length - 1; charIndex >= 0 ; charIndex-- )
		{
		if(this.charAt(charIndex) != " ")
			{
			break;
			}
		spaceCount--;
		}
	return this.substring(0, spaceCount);
	}
String.prototype.rtrim = gl_rtrim;
	
/*
PUBLIC METHOD
Return a string with leading and trailing spaces removed
*/
function gl_trim()
	{
	// Look for a string with leading and trailing spaces -- 0 or more	front and rear,
	// and isolate the central portion consisting of a group of non-spaces
	// preceded by a 0 or more groups of a non-space group followed by a space group.
	// The implementation below, based on a regular expression, was found to crash the Mac when a 
	// different server was used, even though it was previously heavily tested.
	// return this .replace(/\s*(([^ ]+\s+)*[^ ]+)\s*/, "$1");
	return this.ltrim().rtrim();
	}
String.prototype.trim = gl_trim;

/*
PRIVATE METHOD
Return the substring before a certain character
*/
function gl_GetBefore(separatorChar)
	{
	var subString ;
	var separatorLoc = this.indexOf(separatorChar);
	if(separatorLoc != -1)
		{
		subString = this.substring(0, separatorLoc);
		}
	else
		{
		subString = this;
		}
	return subString;
	}
/*
PUBLIC METHOD
Attach to normal string methods
*/
String.prototype.getBefore = gl_GetBefore;

/*
PRIVATE METHOD
Return the substring after a certain character
*/
function gl_GetAfter(separatorChar)
	{
	var subString ;
	var separatorLoc = this.lastIndexOf(separatorChar);
	if(separatorLoc != -1)
		{
		subString = this.substring(separatorLoc + 1);
		}
	else
		{
		subString = this;
		}
	return subString;
	}
/*
PUBLIC METHOD
Attach to normal string methods
*/
String.prototype.getAfter = gl_GetAfter;

/*
PRIVATE METHOD
Return the substring after removing leading and trailing braces.
*/
function gl_deBrace()
	{
	// Get rid of enclosing braces.
	var leftBraceLoc = this.indexOf("{");
	var rightBraceLoc = this.lastIndexOf("}");
	return this.substring(leftBraceLoc + 1, rightBraceLoc);
	}

/*
PUBLIC METHOD
Attach to normal string methods
*/
String.prototype.deBrace = gl_deBrace;


/*
PUBLIC METHOD
Make sure that a name is a valid c/Javascript name
*/
function gl_validateName(elementName)
	{
	// Make sure there is a valid javascript name since it will have to be evaluated as later.
	if(elementName.match(/[a-zA-Z_][a-zA-Z0-9_]*/)) 
		{
		// the name is OK
		}
	else
		{
		var sWhere = "Error: in gl_validateName()" + "\n";
		var sWhat = " This name is invalid: " + elementName + "\n";
		sWhat += " Javascript names must contain only a-z, A-Z, 0-9 and _ and must not start with a digit. ";
		alert(sWhere +  sWhat);
		elementName = "bad_name";
		// break
		}
	return elementName;
	}

//HTML <A NAME="file_paths"></A>
/*============================================================================
File path manipulation
============================================================================*/	

/*
PUBLIC METHOD
Return the base path (current doc by default).
In other words the file path without the file name.
*/
function gl_pathBase(thePath) 
	{ 
	thePath = thePath?thePath:window.location.href;
	return thePath.getAfter("/");
	}

/*
PUBLIC METHOD
Get the name of the file that is included as part of a query URL.
E.g /xxx/yyy/zzz/loader.htm?/ggg/aaa.htm
--> /ggg/aaa.htm
--> aaa
*/
function gl_indexName(thePath) 
	{ 
	// The path of the window document is set by calling a script or this page itself
	thePath = thePath?thePath:window.location.href	;
	// Handle URLs of form ..../xxx/yyyy/zzz.htm?aaaa/bbbb.htm
	// and ..../xxx/yyyy/zzz.htm?url=aaaa/bbbb.htm
	// Get part of URL after last "?"
	// e.g. ../xxx/yyyy/zzz.htm?aaaa/bbbb.htm --> aaaa/bbbb.htm
	thePath = thePath.getAfter("?");
	// Get part of URL after last "=".
	// e.g. .url=aaaa/bbbb.htm --> aaaa/bbbb.htm
	thePath = thePath.getAfter("=");
	// Get part of URL after last "/".
	// aaaa/bbbb.htm --> bbbb.htm
	thePath = thePath.getAfter("/");
	// Get part of URL before last ".".
	// bbbb.htm --> bbbb
	thePath = thePath.getBefore(".");
	return thePath;
	}

//HTML <A NAME="encoding"></A>
/*============================================================================
Encoding and decoding.
============================================================================*/	

/*
PRIVATE METHOD
Decode textarea string -- returns, single and double quotes
Used when setting the state of a text area.
*/
function gl_barDecode(text) 
	{ 
	if (typeof text == "string") 
		{
		text=text.replace(/\|N\|/g, "\n");
		text=text.replace(/\|R\|/g, "\r");	// Obsolete??
		text=text.replace(/\|Q\|/g, "\"");
		text=text.replace(/\|T\|/g, "\'");
		text=text.replace(/\|B\|/g, "\\");
		}
	else
		{
		text = "";
		}
	return text;
	}

/*
PUBLIC METHOD
Encode textarea string -- returns, single and double quotes
Used when getting the state of a text area.
*/
function gl_barEncode(text)
	{	
	if (typeof text == "string") 
		{
		// Bar encode SDP text Strings
		// End-of line characters
		// -- Wintel (This combination can even occur in text areas on a Mac.)
		text=text.replace(/\x0D\x0A/g, "|N|");	
		// -- Any 
		text=text.replace(/\n/g, "|N|");
		// -- Mac, maybe! 
		text=text.replace(/\r/g, "|N|");
		// -- Unix, maybe! 
		text=text.replace(/\x0A/g, "|N|");
		text=text.replace(/\"/g, "|Q|");
		text=text.replace(/\'/g, "|T|");
		text=text.replace(/\\/g, "|B|");
		}
	else
		{
		text = "";
		}
	return text;
	}
	
//HTML <A NAME="cookies"></A>
/*
============================================================================
Cookie utilities
============================================================================
*/

/*
PUBLIC METHOD
Domain and path for cookie visibility
The redirect CGI and the web pages should be in the 
same directory tree and the same domain.
*/
function gl_cookieContext()
	{
	// Be warned! Nestscape considers 128.250.125.155 and www5.meu.unimelb.edu.au
	// to be different domains even though they are the same web site.
	// Better to just rely on default behaviour here and not specify domain.
	//var context = "; domain=128.250.125.155"
	var context = "; domain=" + co_configuration.getHostName()
	//var context = "; domain="
	// Use this path if the redirect CGI is within the ResearchingHistory directory.
	//context += "; path=/ResearchingHistory"
	// Use this path if the redirect CGI is within the web root 
	// but outside the the ResearchingHistory directory.
	context += "; path=/"
	return context
	}

/*
PUBLIC METHOD
Set the value of a named cookie.
*/
function gl_setCookie(cookieName, cookieValue) 
	{
	// Set expiry date so cookie is transient  
	// and lasts only for this browser session.
	//var today = new Date();
	//var expiry = new Date(today.getTime() + 10 * 60 * 1000); // plus 28 days
	// alert("Name: " + cookieName + "\n" + "Value: " + cookieValue)
	if (cookieValue != null && cookieValue != "")
		{
		// Build cookie value
		var cookie = cookieName + "=" + escape(cookieValue) 
		cookie += gl_cookieContext()
		// Set cookie value
		document.cookie = cookie
		}	
	}
	
/*
PUBLIC METHOD
Get the value of a named cookie.
*/
function gl_getCookie(cookieName) 
	{
	// Get cookies
	var cookies=document.cookie; 
	// If no cookies return blank string
	if(cookies == null || cookies == '')
		{
		return ''
		}
	var equalLoc = cookies.indexOf(cookieName + "=");
	// If particular cookie is not found return blank string
	if (equalLoc == -1)
		{
		return '';
		}
	// Return unescaped cookie value
	equalLoc = cookies.indexOf("=", equalLoc) + 1;
	var endstr = cookies.indexOf(";", equalLoc);
	if (endstr == -1)
		{
		endstr = cookies.length;
		}
	return unescape(cookies.substring(equalLoc, endstr));
	}
	
/*
PUBLIC METHOD
Delete a named cookie.
*/
function gl_deleteCookie (cookieName) 
	{
	var cookieValue = gl_getCookie(cookieName)
	if (cookieValue != '') 
		{
		// To delete a particular cookie 
		// set value of cookie to blank string
		// Build cookie value
		// alert("Delete cookieValue: " + cookieValue)
		var cookie = cookieName + "="
		cookie += gl_cookieContext()
		// and set expiry date to date already past.
		cookie += "; expires=Thu, 01-Jan-70 00:00:01 GMT"
		// Set cookie value
		document.cookie = cookie
		}
	}
//HTML <A NAME="descriptors"></A>
/*
============================================================================
State descriptor manipulation.
============================================================================
*/	

/*
PUBLIC METHOD
Get the page state and then extract the value of a state field.
*/
function gl_getStateField(fieldReference)
	{
	var result = ''
	var SDPstate = new Object();
	eval("SDPstate = " + getState());	
	var value = "SDPstate";
	if (typeof fieldReference != "undefined") 
		{
		// SDPstate.fieldReference0.fieldReference1.fieldReference2....
		value += "." + fieldReference;
		}
	// SDPstate, fieldReference0, fieldReference1, fieldReference2, ...
	var referenceComponents = value.split('.');
	// Evaluate object with more and more reference components.
	var componentCount = referenceComponents.length
	var componentIndex = 0
	var partialReference = '';
	var dot = ""
	for (componentIndex = 0; componentIndex < componentCount; componentIndex++) 
		{
		partialReference += dot + referenceComponents[componentIndex];
		dot = "." 
		var result = eval(partialReference);
		if (typeof result == 'undefined') 
			{
			break
			}
		}	
    return result;
	}

/*
PRIVATE METHOD
Convert an object to its string literal equivalent.
*/
function gl_objectToString(descriptorObject) 
	{
	var resultString = "";
	if (typeof descriptorObject == "String")
		{
		resultString = descriptorObject // PAF 29.6
		}
	else
		{
		var fieldName
		for (fieldName in descriptorObject) 
			{
			if (typeof(descriptorObject[fieldName]) == "object") 
				{
                                if (descriptorObject[fieldName] != null)
                                  {
				  var subObject = descriptorObject[fieldName];
				  resultString += fieldName + ":" + gl_objectToString(subObject) + ",";
                                  }
				}
			else 
				{
				if (typeof(descriptorObject[fieldName]) == "number") 
					{
					resultString += fieldName + ":" + descriptorObject[fieldName] + ",";
					}
				else 
					{
					resultString += fieldName + ":\"" + descriptorObject[fieldName] + "\",";
					}
				}
			}
		// Remove trailing comma and embrace 
		resultString = resultString.substring(0, resultString.length - 1);
		resultString = "{" + resultString + "}";
		}
	return resultString;
	}
	
//HTML <A NAME="component_API"></A>
/*============================================================================
OCCA component API
============================================================================*/	
/*
List of OCCA component registered in the web page
to hand their own getState/setState
*/
var gl_occaComponents = new Array()

	
function gl_initComponents()
	{
	var componentIndex = 0
	var componentCount = gl_occaComponents.length
	for(componentIndex = 0; componentIndex < componentCount; componentIndex++)
		{
		// alert("Init component")
		gl_occaComponents[componentIndex].init()
		}
	}
	
function gl_getComponentStates()
	{
	var descriptor = ""
	var comma = ""
	var componentIndex = 0
	var componentCount = gl_occaComponents.length
	for(componentIndex = 0; componentIndex < componentCount; componentIndex++)
		{
		// alert("Get component state")
		descriptor += comma + gl_occaComponents[componentIndex].getState()
		comma = ","
		}
	return descriptor
	}

function gl_setComponentStates(descriptor)
	{
	var componentIndex = 0
	var componentCount = gl_occaComponents.length
	for(componentIndex = 0; componentIndex < componentCount; componentIndex++)
		{
		// alert("Set component state")
		gl_occaComponents[componentIndex].setState(descriptor)
		}
	}
	
//HTML <A NAME="registerComponent"></A>
function gl_registerComponent(componentObject)
	{
	// append function to array of functions
	gl_occaComponents[gl_occaComponents.length] = componentObject
	}


/*
Return a reference to the current form element.
The index is saved during page loading rather than the reference itself,
since the references appear to be altered by the browser 
after page loading is completed.
*/	
function gl_getFormElement()
	{
	var form = document.forms[this.formIndex];
	var element = form.elements[this.elementIndex];
	return element;
	}
	
function gl_setCurrentFormElement()
	{
	this.formIndex = document.forms.length - 1;
	this.elementIndex = document.forms[this.formIndex].elements.length - 1;
	}
	
//HTML <A NAME="OccaFormComponentClass"></A>
/*
Base class for an OCCA self-registering component based on a form element.
This is used by specific OCCA form component classes via their prototype property.
These classes create a component object just after the form element
is interpreted.
*/	
function OccaFormComponent()
	{
	this.getFormElement = gl_getFormElement;
	this.setCurrentFormElement = gl_setCurrentFormElement;
	}

//HTML <A NAME="form"></A>
/*
============================================================================
*/

/*
PRIVATE METHOD
OCCA form for automatic components
*/
var gl_occaFormName = "OCCA_form"

function gl_getOCCAform()
	{
	if(typeof document.forms[gl_occaFormName] == "undefined")
		{
		OCCAform = document.forms[0]
		}
	else
		{
		OCCAform = document.forms[gl_occaFormName]
		}
	return  OCCAform
	}

//HTML <A NAME="getStateHandlers"></A>
/*============================================================================
GetState(): scan a web page and extract the contents of the form input elements
amd encode the data as a state descriptor (object literal as a String)
============================================================================*/	

//HTML <A NAME="getTextState"></A>
/*
PRIVATE METHOD
Convert the contents of a text input element to a descriptor component.
*/
function gl_getTextState(element)
	{
	var nameValuePair = ""
	var elementName = gl_validateName(element.name);
	var elementValue = element.value
	// If the field contents are "embraced" it is likely that the contents have been 
	// supplied by another more complex object.
	if ((elementValue.indexOf("{")==0) && (elementValue.lastIndexOf("}")==elementValue.length-1)) 
		{
 		// So just use the contents directly.
 		nameValuePair = elementName + ":" + elementValue;
		}
	else 
		{
		// Otherwise the contents are just a simple String so they must be encoded
		// to remove returns, single and double quotes etc.
		nameValuePair = elementName + ":\"" + gl_barEncode(elementValue) + "\"";
		}
	return 	nameValuePair 
	}
	
//HTML <A NAME="getMenuState"></A>
/*
PRIVATE METHOD
Convert the contents of a menu input element to a descriptor component.
*/
function gl_getMenuState(element)
	{
	var nameValuePair = ""
	var elementName = gl_validateName(element.name);
	var elementValue = "";
	var optionIndex = element.selectedIndex;
	if(optionIndex > -1)
		{
		elementValue = element.options[optionIndex].value;
		}
	nameValuePair = elementName + ":\"" + elementValue + "\"";
	return 	nameValuePair 
	}	
	
//HTML <A NAME="getListState"></A>
/*
PRIVATE METHOD
Convert the contents of a listbox input element to a descriptor component.
*/
function gl_getListState(element)
	{
	var elementName = gl_validateName(element.name);
	var nameValuePair = ""
	var elementArray = ""
	var comma = ""
	var itemLabelPrefix = "option";
	var itemLabel = "";
	var elementValue = "";
	var optionIndex = 0
	var optionCount = element.options.length
	for (optionIndex = 0; optionIndex < optionCount; optionIndex++)
		{
		if(element.options[optionIndex].selected)
			{
			// Set to value of selected option.
			elementValue = element.options[optionIndex].value;
			}
		else
			{
			// Set to blank string.
			// Maybe this should be null or even true/false
			elementValue = "";	
			}
		itemLabel = itemLabelPrefix + optionIndex + ":";
		elementArray += comma + itemLabel + "\"" + elementValue + "\""
		comma = ","
		}
	nameValuePair = elementName + ":{" + elementArray + "}";
	return nameValuePair
	}		

//HTML <A NAME="getCheckboxState"></A>
/*
PRIVATE METHOD
Convert the contents of a checkbox input element to a descriptor component.
Obsolete function
*/
function gl_getCheckboxState(element)
	{
	var nameValuePair = ""
	var elementName = gl_validateName(element.name)
	var elementValue = element.checked ? element.value : ""
	nameValuePair = elementName + ":\"" + elementValue + "\"";
	return 	nameValuePair 
	}

//HTML <A NAME="getCheckboxGroupState"></A>
/*
PRIVATE METHOD
Convert the contents of a checkbox group input element to a descriptor component.
*/
function gl_getCheckboxGroupState(element)
	{
	var nameValuePair = ""
	var elementName = gl_validateName(element.name)
	var elementValue = ""
	var itemLabelPrefix = "check";
	var itemLabel = "";
	// Get the number of items in the checkbox group.
	var parentForm = element.form
	var checkCount = 1
	if(typeof parentForm[elementName][0] != "undefined")
		{
		checkCount = parentForm[elementName].length
		}
	if(checkCount > 1)
		{
		// And scan the group looking for the checked item 
		var elementArray = ""
		var comma = ""
		for (var checkIndex = 0; checkIndex < checkCount ; checkIndex++)	
			{
			elementValue = ""
			if (parentForm[elementName][checkIndex].checked) 
				{
				// And hence the value of the group
				elementValue = parentForm[elementName][checkIndex].value
				}			
			itemLabel = itemLabelPrefix + checkIndex + ":";
			elementArray += comma + itemLabel + "\"" + elementValue + "\""
			comma = ","
			}
		nameValuePair = elementName + ":{" + elementArray + "}";
		}
	else
		{
		elementValue = element.checked ? element.value : ""
		nameValuePair = elementName + ":\"" + elementValue + "\"";
		}
	return 	nameValuePair 
	}

//HTML <A NAME="getRadioState"></A>
/*
PRIVATE METHOD
Convert the contents of a radio group input element to a descriptor component.
*/
function gl_getRadioState(element)
	{
	var nameValuePair = ""
	var elementName = gl_validateName(element.name)
	var elementValue = ""
	// Get the number of items in the radio group.
	var parentForm = element.form
	var radioCount = parentForm[elementName].length
	// And scan the group looking for the checked item 
	for (var radioIndex = 0; radioIndex < radioCount ; radioIndex++)	
		{
		if (parentForm[elementName][radioIndex].checked) 
			{
			// And hence the value of the group
			elementValue = parentForm[elementName][radioIndex].value
			break
			}
		}
	nameValuePair = elementName + ":\"" + elementValue + "\"";
	return 	nameValuePair 
	}
	
//HTML <A NAME="getOCCAFormStates"></A>
/*
PUBLIC METHOD
Convert the contents of the OCCA form to a descriptor.
*/
function gl_getOCCAFormStates(formObject)
	{
	var descriptor="";
	var comma = ""
	if (typeof formObject != "undefined") 
		{
		// Hashed list of element names that have been found.
		// Some types of elements treat duplicate names as members of an input element array
		var namesFound = new Array()
		var elementIndex = 0
		var elementCount = formObject.elements.length
		for (elementIndex=0; elementIndex < elementCount; elementIndex++)
			{
			var element = formObject.elements[elementIndex]
			var elementName = element.name
			// Process the element only if its name has not been seen before.
			if(typeof namesFound[elementName] == "undefined")
				{
				// Mark the element name as used so duplicate names are not reprocessed
				namesFound[elementName] = true
				var elementType=element.type.toString().toLowerCase();
				var elementDescriptor = ""
				if (elementType == "hidden") 
					{
					elementDescriptor = gl_getTextState(element)
					}
				else if (elementType == "text") 
					{
					elementDescriptor = gl_getTextState(element)
					}
				else if (elementType == "textarea") 
					{
					elementDescriptor = gl_getTextState(element)
					}
				else if (elementType == "select-one")
					{
					// No distinction is made here between the select-one and select-multiple types
					// since Nestscape does not properly identify the select-multiple type.
					//elementDescriptor = gl_getMenuState(element)
					elementDescriptor = gl_getListState(element)
					}
				else if (elementType == "select-multiple")
					{
					elementDescriptor = gl_getListState(element)
					}
				else if (elementType == "checkbox")
					{
					elementDescriptor = gl_getCheckboxGroupState(element)
					}
				else if (elementType == "radio")
					{
					elementDescriptor = gl_getRadioState(element)
					} 
				// Append the item to the descriptor only if it is non-blank
				if(elementDescriptor != "")
					{
					descriptor += comma + elementDescriptor     
					comma = ","
					}
				}
			}
		}
	return descriptor;
	}
	
//HTML <A NAME="getState"></A>
/*
PUBLIC METHOD
Convert the contents of web page to a descriptor.
*/
function gl_getState()
	{
	var comma = "";
	var descriptor0 = "";
	// Process all forms called "occa_form" -- there can be more than one.
	if(typeof document.forms != "undefined")
		{
		var formObject = document.forms;
		var formCount = 0;
		if(typeof formObject.length != "undefined")
			{
			formCount = formObject.length;
			}
		for(var formIndex = 0; formIndex < formCount; formIndex++)
			{
			if(formObject[formIndex].name == gl_occaFormName)
				{
				descriptor0 += comma + gl_getOCCAFormStates(formObject[formIndex]);
				comma = ",";
				}
			}
		}
	var descriptor1 = gl_getComponentStates()
	comma = (descriptor0 == "" || descriptor1 == "") ? "" : ","
	descriptor = descriptor0 + comma + descriptor1
	descriptor = "{" + descriptor + "}"
	//alert("descriptor: " + descriptor)
	return descriptor
	}

//HTML <A NAME="setStateHandlers"></A>
/*============================================================================
setState(): scan a web page and set the contents of the form input elements
using a state descriptor (object literal as a String) as the data source.
============================================================================*/	

//HTML <A NAME="setTextState"></A>
/*
PRIVATE METHOD
Set the value a text input element from a state descriptor
*/
function gl_setTextState(element, descriptorObject)
	{
	var elementName = element.name
	var textObject = descriptorObject[elementName];
	// Check the kind of the sub-object
	if (typeof textObject == "object") 
		{ 
		// If it is an object convert it to a String and place in the text field
		var subDescriptor = gl_objectToString(textObject)
		element.value = subDescriptor 
		// Check if the field element name corresponds to the name of a plug-in or embedded object
		var pluginObject = document.embeds[elementName]
		if (typeof pluginObject == "undefined") 
			{
			// If not, assume that there is a Javascript component with a set state function
			// whose name is derived from the name of the associated text field.
			// Execute that function.			
			eval(elementName + "_setState('" + subDescriptor +"')"); 
			}
		// If there is a plug-in check that it is a Shockwave plug-in.
		else if (pluginObject.toString().indexOf("Shockwave") != -1)
			{ 
			// If so execute the function that sets the state of the shockwave plug-in.
			eval("sw_setState('" + elementName + "','" + subDescriptor +"')");			
			}
		else 
			{
			// If not execute the function that sets the state of the plug-in.
			// Note: this function may have to be defined by the component builder.
			eval(elementName + "_setState('" + subDescriptor +"')"); 
			}
		}
	// Check that the object is a string or number or boolean (i.e. not an object) 
	// and if so place it directly in the field.
	else if (typeof textObject != "undefined") 
		{ 
		// Since the text may contain 'return's, 'quote's etc. decode it 
		// then place the text into the text field.
		element.value = gl_barDecode(textObject);
		}
	}	
	
//HTML <A NAME="setMenuState"></A>
/*
PRIVATE METHOD
Set the value a menu input element from a state descriptor
*/
function gl_setMenuState(element, descriptorObject)
	{
	// Get value of menu option from descriptor
	// and scan through the named menu to find the matching value.
	// When found set the menu's value via the selectedIndex property and then exit.
	var elementName = element.name
	var newValue = descriptorObject[elementName]
	var optionIndex = 0
	var optionCount = element.options.length
	for (optionIndex = 0; optionIndex < optionCount; optionIndex++)
		{
		if(element.options[optionIndex].value == newValue) 
			{
			element.selectedIndex=optionIndex;
			break
			}
		}
	}		
	
//HTML <A NAME="setListState"></A>
/*
PRIVATE METHOD
Set the value a menu input element from a state descriptor
*/
function gl_setListState(element, descriptorObject)
	{
	// Get value of menu option from descriptor
	// and scan through the named menu to find the matching value.
	// When found set the menu's value via the selectedIndex property and then exit.
	var elementName = element.name
	var descriptorArray = descriptorObject[elementName]
	if(typeof descriptorArray != "undefined" && descriptorArray != null)
		{
		var itemLabelPrefix = "option";
		var itemLabel = "";
		var optionIndex = 0;
		var optionCount = element.options.length;
		for (optionIndex = 0; optionIndex < optionCount; optionIndex++)
			{
			itemLabel = itemLabelPrefix + optionIndex;
			if (typeof descriptorArray =="object")
				var newValue = descriptorArray[itemLabel];
			else 
				var newValue = descriptorArray;
				
			if(newValue == element.options[optionIndex].value)
				{
				element.options[optionIndex].selected = true;
				}
			else
				{
				element.options[optionIndex].selected = false;
				}
			}
		}
	}		
	
//HTML <A NAME="setRadioState"></A>
/*
PRIVATE METHOD
Set the value a radio button group input element from a state descriptor
*/
function gl_setRadioState(element, descriptorObject)
	{
	// Get value of radio group option from the descriptor
	// and scan through the named radio group to find the matching value.
	// When found check the radio button and then exit.
	var elementName = element.name
	var radioValue = descriptorObject[elementName]
	var parentForm = element.form
	var radioIndex = 0
	var radioCount = parentForm[elementName].length
	for (radioIndex = 0; radioIndex < radioCount ; radioIndex++)	
		{
		if (parentForm[elementName][radioIndex].value == radioValue)
			{	
			parentForm[elementName][radioIndex].checked=true
			break
			}
		}
	}		

//HTML <A NAME="setCheckboxState"></A>
/*
PRIVATE METHOD
Set the value a checkbox button group input element from a state descriptor
*/
function gl_setCheckboxState(element, descriptorObject)
	{
	var elementName = element.name
	var checkValue = descriptorObject[elementName]
	var itemLabelPrefix = "check";
	var itemLabel = "";
	// Get the number of items in the checkbox group.
	var parentForm = element.form
	var checkCount = 1
	if(typeof parentForm[elementName][0] != "undefined")
		{
		checkCount = parentForm[elementName].length
		}
	if(checkCount > 1)
		{
		//checkCount = checkValue.length
		var checkedState = false
		var checkIndex = 0
		for(checkIndex = 0; checkIndex < checkCount; checkIndex++)
			{
			itemLabel = itemLabelPrefix + checkIndex;
			if(typeof checkValue[itemLabel] != "undefined")
				{
				checkedState = parentForm[elementName][checkIndex].value == checkValue[itemLabel]
				parentForm[elementName][checkIndex].checked = checkedState;
				}
			}
		}
	else
		{
		element.checked = element.value == checkValue;
		}
	}

//HTML <A NAME="setOCCAFormStates"></A>
/*
PUBLIC METHOD
setState() scans the web page sets the state of each input element it finds.
It can handle input element arrays.
*/
function gl_setOCCAFormStates(formObject, descriptorObject)
	{	
	if (typeof formObject != "undefined") 
		{
		// Hashed list of element names that have been found.
		// Some types of elements treat duplicate names as members of an input element array
		var namesFound = new Array()
		var elementIndex = 0
		var elementCount = formObject.elements.length
		for (elementIndex = 0; elementIndex < elementCount ; elementIndex++)
			{
			var element = formObject.elements[elementIndex]
			var elementName = element.name
			// Process the element only if its name has not been seen before.
			if(typeof namesFound[elementName] == "undefined")
				{
				// Mark the element name as used so duplicate names are not reprocessed
				namesFound[elementName] = true
				var elementType=element.type.toString().toLowerCase();
				if (elementType == "hidden") 
					{
					gl_setTextState(element, descriptorObject)
					}
				else if (elementType == "text") 
					{
					gl_setTextState(element, descriptorObject)
					}
				else if (elementType == "textarea") 
					{
					gl_setTextState(element, descriptorObject)
					}
				else if(elementType == "select-one")
					{
					// No distinction is made here between the select-one and select-multiple types
					// since Nestscape does not properly identify the select-multiple type.
					//gl_setMenuState(element, descriptorObject)
					gl_setListState(element, descriptorObject)
					}
				else if (elementType == "select-multiple")
					{
					gl_setListState(element, descriptorObject)
					}
				else if (elementType == "radio")
					{
					gl_setRadioState(element, descriptorObject)
					}       
				else if (elementType =="checkbox")
					{
					gl_setCheckboxState(element, descriptorObject)
					}       
				}
			}
		}
	}
	
//HTML <A NAME="setState"></A>
/*
PUBLIC METHOD
setState() scans the web page, sets the state of each input element it finds.
It can handle input element arrays.
*/
function gl_setState(descriptor)
	{
	// Force descriptor into string object form
	// so that length property works properly.
	descriptor = new String(descriptor)
	// Remove leading and trailing spaces 
	// then do nothing if the descriptor is empty.
	descriptor = descriptor.trim()
	//alert("descriptor: " + descriptor)
	if(descriptor != "{}") 
		{
		// Make sure the descriptor is properly enclosed by braces. 
		var openBraceLoc = descriptor.indexOf("{")
		var closeBraceLoc = descriptor.lastIndexOf("}")
		if(openBraceLoc == 0 && closeBraceLoc == descriptor.length - 1)
			{
			var descriptorObject = new Object()
			eval("descriptorObject = " + descriptor);
			// Process all forms called "occa_form" -- there can be more than one.
			if(typeof document.forms != "undefined")
				{
				var formObject = document.forms;
				var formCount = 0;
				if(typeof formObject.length != "undefined")
					{
					formCount = formObject.length;
					}
				for(var formIndex = 0; formIndex < formCount; formIndex++)
					{
					if(formObject[formIndex].name == gl_occaFormName)
						{
						gl_setOCCAFormStates(formObject[formIndex], descriptorObject);
						}
					}
				}
			gl_setComponentStates(descriptorObject)
			}
		else
			{
			var sErrorWhere = "Error: in gl_setState(). \n"
			var sErrorWhat = " The incoming descriptor is not enclosed by braces correctly. "
			alert(sErrorWhere + sErrorWhat)
			}
		}
	}

//HTML <A NAME="shockwave"></A>
/*============================================================================
Shockwave movie handlers.
============================================================================*/	

/*
PRIVATE METHOD
Set the value a shockwave movie from a state descriptor after a time delay.
The shadow text field will also be updated at the same time. 
*/
function sw_setState(movieName, descriptor)
	{
	setTimeout ("sw_deferredSetState ('" + movieName +  "','" + descriptor + "')", 10);
	}
	
/*
PRIVATE METHOD
Set the value a shockwave movie from a state descriptor.
*/
function sw_deferredSetState(movieName, descriptor)
	{
	descriptor = escape(descriptor);
	var movieObject = document.embeds[movieName];
	movieObject.EvalScript(descriptor);
	}
	
/*
PRIVATE METHOD
The Shockwave movie Lingo script calls this function to update the contents
of the movie's shadow field.
This may need to be a deferred call -- investigate.
*/
function sw_setShadowField(fieldName, descriptor)
	{
	//alert("Field: " + fieldName + ", Descriptor: " + descriptor)
	var form = gl_getOCCAform();
	form[fieldName].value = descriptor;
	}

/*============================================================================
Initialisation to be performed in the onLoad handler of the web page.
A reference to the onload function below should be inserted into the web page body tag.
============================================================================*/	

/*
Register module initialisation.
The functions stored in this array will be executed by the web page onLoad handler
before component instances are initialised. 
*/
var gl_initTasks = new Array(); 

/*
Register module onload tasks
The functions stored in this array will be executed by the web page onLoad handler
after component instances are initialised
*/
var gl_onLoadTasks = new Array(); 

//HTML <A NAME="registerInit"></A>
/*
PUBLIC METHOD
A module will use this function to add its own initialisation code to 
the OCCA gl_onInitTasks list so that OCCA can orchestrate the onload intialisation.
For example DBconnect uses this function to register its own onload intialisation code.
*/
function gl_RegisterInit(functionReference)
	{
	// append function to array of functions
	gl_initTasks[gl_initTasks.length] = functionReference
	}

//HTML <A NAME="registerOnload"></A>
/*
PUBLIC METHOD
A component will use this function to add its own task code to 
the OCCA gl_onLoadTasks list so that OCCA can orchestrate the onload tasks.
For example DBconnect uses this function to register its own onload task code.
*/
function gl_RegisterOnLoad(functionReference)
	{
	// append function to array of functions
	gl_onLoadTasks[gl_onLoadTasks.length] = functionReference
	}

//HTML <A NAME="initialisation"></A>
/*
PUBLIC METHOD
This is the onLoad handler that should be appended to the body tag of all OCCA pages.
*/
function gl_OnLoad() 
	{
	// Force reload on first visit of OCCA site after authorisation has completed, 
	// to fix WinNT Netscape button freezing problem.
	var userID = gl_getCookie('userID');
	if(userID != "")
		{
		if(gl_getCookie('first_visit') == '')
			{
			// alert("reloading")
			gl_setCookie('first_visit', 'true');
			location.reload(true);
			}
		}
	// Perform module initialisation
	var taskCount = gl_initTasks.length
	for (var taskIndex = 0; taskIndex < taskCount; taskIndex++) 
		{
		gl_initTasks[taskIndex](); 
		}
	// Initialise component instances
	gl_initComponents()	
	// Perform module tasks
	var taskCount = gl_onLoadTasks.length
	for (var taskIndex = 0; taskIndex < taskCount; taskIndex++) 
		{
		gl_onLoadTasks[taskIndex](); 
		}
	}
/*
============================================================================
The occa object.
An object used as a container for publishing public scripting  methods.
*/	
var occa = new Object()
occa.load = gl_OnLoad
occa.setState = gl_setState
occa.getState = gl_getState
/*
============================================================================
*/	

	
function setState(descriptor)
	{
	gl_setState(descriptor)
	}

function getState()
	{
	return gl_getState()
	}
	
