// NetPoint 2.70.00
// Java Script Version 2.70.02
// Copyright © 2007 Respond Group Limited.
// Respond ® is a registered trademark of Respond Group Limited.

//############################################## NOTE ##########################################################################
// cpdisplayname : All HTML controls to be validated must contain the attribute cpdisplayname.  This value is used 
// when displaying error messages to the user.
// cpfulldate : All HTML controls that represent date fields must contain the attribute cpfulldate.  This value is
// used to validate date fields.  The value must be of the format mm/dd/yyyy and if a default value is used 
// for a date field, cpfulldate should also contain the value, but of the format mm/dd/yyyy.  If a date is split
// over three controls, each control must contain the attribute an be of the format mm/dd/yyyy.

//######################### Global variables specifying the browser being used #################################################
var objNS4 = (document.layers)?1:0;
var objNS6 = (!document.layers) && (navigator.userAgent.indexOf('Netscape')!=-1)?1:0;
var objIE4 = ((document.all)&&(navigator.appVersion.indexOf("MSIE 4.")!=-1))?1:0;
var objIE5 = ((document.all)&&(navigator.appVersion.indexOf("MSIE 5.")!=-1))?1:0;

//###################### Error Messages displayed to the user. [] specifies a value to be replaced. ##########################
var MSG_MAX_NUMBER_EXCEEDED = "The number entered into '[FIELDNAME]' has exceeded the maximum number '[MAXNUMBER]'.\nPlease enter a number less than or equal to '[MAXNUMBER]'.";
var MSG_MIN_NUMBER_NOT_EXCEEDED = "The number entered into '[FIELDNAME]' is less than the minimum number '[MINNUMBER]'.\nPlease enter a a number greater than or equal to '[MINNUMBER]'.";
var MSG_INVALID_NUMBER = "The value entered into '[FIELDNAME]' is not valid.\nPlease enter a valid number.";
var MSG_MAX_LENGTH_EXCEEDED = "The text entered into '[FIELDNAME]' has exceeded the maximum length '[MAXLENGTH]'.\nPlease ensure the text length is less than or equal to '[MAXLENGTH]'.";
var MSG_MIN_LENGTH_NOT_EXCEEDED = "The text entered into '[FIELDNAME]' is less than the minimum length '[MINLENGTH]'.\nPlease ensure the text length is greater than or equal to '[MINLENGTH]'.";
var MSG_MANDATORY_FIELD_NOT_POPULATED = "The mandatory field '[FIELDNAME]' cannot be blank.\nPlease enter a value.";
var MSG_FIELD_FAILED_PATTERN_VALIDATION = "The field '[FIELDNAME]' has failed the pattern validation.";
var MSG_INVALID_DATE = "The date '[DATE]' is not valid.\nPlease enter a valid date.";
var MSG_INVALID_MONTH = "The month '[MONTH]' is not valid.\nPlease enter a month between 1 and 12.";
var MSG_INVALID_DAY = "The day '[DAY]' is not valid.\nPlease enter a day between 1 and 31.";
var MSG_INVALID_DAYS_IN_MONTH = "[MONTH] does not have 31 days.\nPlease enter a valid day.";
var MSG_INVALID_LEAP_YEAR = "February '[YEAR]' does not have '[DAY]' days.\nPlease enter a valid date.";
var MSG_INVALID_YEAR = "Please enter a 4 digit year greater than or equal to 1900";
var MSG_FORCE_FUTURE_DATE = "The date '[DATE]' is not valid.\nPlease enter a date greater than or equal to today's date.";
var MSG_FORCE_PAST_DATE = "The date '[DATE]' is invalid.\nPlease enter a date less than or equal to today's date.";
var MSG_UNKOWN_MANDATORY_FIELD_NOT_POPULATED = "One or more mandatory fields have not been populated.\nPlease ensure all mandatory fields are populated.";
var MSG_MANDATORY_RADIO_OPTION_FIELD_NOT_POPULATED = "The mandatory field '[FIELDNAME]' has not been populated or an option has been selected.";
var MSG_NAMED_MANDATORY_FIELDS_NOT_POPULATED = "The following mandatory field(s) have not been populated:\n\n[FIELDNAMES]\nPlease ensure all mandatory fields are populated.";
var MSG_NAMED_MANDATORY_FIELD_NOT_POPULATED = "The following mandatory field has not been populated:\n\n[FIELDNAMES]\nPlease ensure all mandatory fields are populated.";
var MSG_DRILL_DOWN_COMBO_LEAF_OPTION_NOT_SELECTED = "The field '[FIELDNAME]' cannot have a non-leaf option selected."

//############################################ Month names #######################################################################
var objMonthNames = new Array("January","February","March","April","May","June","July","August","September","October","November","December");
var MONTH_NAMES=new Array('January','February','March','April','May','June','July','August','September','October','November','December','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');

//############################################ Field data type constants ##########################################################
var DATA_TYPE_NUMBER = "1";
var DATA_TYPE_SHORT_STRING = "3";
var DATA_TYPE_LONG_STRING = "4";
var DATA_TYPE_CURRENCY = "5";
var DATA_TYPE_DATE = "6";
var DATA_TYPE_DATE_TIME = "8";

//############################################ Drill Down Combo Box constants ####################################################
var DRILL_DOWN_COMBO_DESELECTION_ITEM = 1;
var DRILL_DOWN_COMBO_ADD_BUTTON = "Add"
var DRILL_DOWN_COMBO_REMOVE_BUTTON = "Remove"
var DRILL_DOWN_COMBO_NEXT_IDENTIFIER = "n";

//############################################ Category option constants ####################################################
var DISPLAY_TYPE_USE_FULL_HIERARCHY = "300"
var DISPLAY_TYPE_HIERARCHY_IN_BRACKETS = "301"
var DISPLAY_TYPE_LEAF_ONLY = "302"

//############################################ MISC ################################################################################
var HTML_NAME_WARNING_MESSAGE_LABEL = "NetPointWarning"
var objUseErrorLabel = 1;
var CLASS_ATTRIBUTE_NAME = "class";

//########################### Functions to interogate the form and controls #################################################################################

// This function will handle the user changing the selected category

function HandleCategorySelectionForCheckbox(PCheckboxControl)
{
	if (!PCheckboxControl.checked) return;

	// Get the field ids to update
	var LFieldValues = GetAttributetValue("cplinkedfieldvalues", PCheckboxControl, PCheckboxControl.cplinkedfieldvalues);
	UpdateFormControlsForFieldValues(PCheckboxControl.form, LFieldValues);
}

function HandleCategorySelectionForRadio(PRadioControl)
{
	// Get the field ids to update
	var LFieldValues = GetAttributetValue("cplinkedfieldvalues", PRadioControl, PRadioControl.cplinkedfieldvalues);
	UpdateFormControlsForFieldValues(PRadioControl.form, LFieldValues);
}

function HandleCategorySelectionForSelect(PCategoryControl)
{	
  // Check that selected index is valid
  if (PCategoryControl.selectedIndex == -1)
    return;

	// Get the selected option
	var LOption = PCategoryControl.options[PCategoryControl.selectedIndex];
	
	// Get the field ids to update
	var LFieldValues = GetAttributetValue("cplinkedfieldvalues", LOption, LOption.cplinkedfieldvalues);

	UpdateFormControlsForFieldValues(PCategoryControl.form, LFieldValues);
}

// This function will update the forms controls from the field value
// pairs that are passed in. i.e. "FieldId:=fld00024 Value:=B FieldId:=fld00325 Value:=B"
function UpdateFormControlsForFieldValues(PForm, PFieldValues)
{
	var FIELD_ID_TAG = "FieldId";
	var VALUE_TAG = "Value";
	var LParsingFieldValue, LParsingFieldId;
	var LIds = new Array;
	var LValues = new Array;
	var LFieldIndex = -1;

	if (PFieldValues == null) return;

	var LFieldValuePairs = PFieldValues.split(/\|\||:=/);

	for (var i = 0; i < LFieldValuePairs.length; i++) {
		if (LFieldValuePairs[i] == FIELD_ID_TAG) {
			LParsingFieldValue = false;
			LFieldIndex ++;
			LIds[LFieldIndex] = ""
			LValues[LFieldIndex] = ""
		}

		if (LFieldValuePairs[i] == VALUE_TAG) LParsingFieldId = false;

		if (LParsingFieldId) LIds[LFieldIndex] += LFieldValuePairs[i];
		if (LParsingFieldValue) LValues[LFieldIndex] += LFieldValuePairs[i];

		if (LFieldValuePairs[i] == FIELD_ID_TAG) LParsingFieldId = true;
		if (LFieldValuePairs[i] == VALUE_TAG) LParsingFieldValue = true;
	}

	// Update the field controls on the current from
	for(var LCount = 0; LCount < PForm.elements.length; LCount++) {
		var LElement = PForm.elements[LCount];
		var LFieldId = GetAttributetValue("cpfieldid", LElement,LElement.cpfieldid);

		for (var i = 0; i <= LFieldIndex; i++) {
			if (LFieldId == LIds[i]) 
				LElement.value = LValues[i];
		}		
	}

}

// Function to return the element determined by the Id passed in.
function GetElementById(PID)
{
	if(objNS4) LElement = null;
	else if(objIE4) var LElement = document.all[PID];
	else if(objIE5) var LElement = document.getElementById(PID);
	else var LElement = document.getElementById(PID);
	return LElement;
}

// This routine extracts the attribute specified by the calling routine depending
// on the browser version.  If the attribute does not exist null is returned to the caller.
function GetAttributetValue(PAttribute, PControl, PValue)
{	
	if (PControl == null || PAttribute == null) return null;
	
	if (objNS4) return null;
	else if (objIE4 || objIE5) 
	{
		if (PValue == null) return null;
		else return PValue;
	}
	else 
	{
		var LAttribute = PControl.attributes.getNamedItem(PAttribute);
		if (LAttribute == null) return null;
		else return LAttribute.value;
	}
}

//##################### Functions for text replacement, pattern matching and their associated functions ####################################################

function AutoReplaceText(PControl)
{
	var LCPReplace = GetAttributetValue("cpreplace",PControl, PControl.cpreplace);
	var LCPReplaceWith = GetAttributetValue("cpreplacewith",PControl, PControl.cpreplacewith);
	
	if (LCPReplace == null || LCPReplaceWith == null) return false;
	
	var LDisplayText = PControl.value;
	var LReplace = GetRegularExpression(LCPReplace);
	PControl.value = LDisplayText.replace(LReplace,LCPReplaceWith);	
}

//###################################### HTML control manipulation functions ########################################

// Function to restrict the characters the user is able to enter:"0-9","-" , "."
function FilterNumberField(PEvent, PControl)
{
	if (PEvent.which) var LKeyCode = PEvent.which;
	else var LKeyCode = PEvent.keyCode;

	var LValue = PControl.value;

	if (LKeyCode == 45) { // allow a "-" at the start
		if (LValue.length > 0) return false;
		else return true;}

	else if (LKeyCode == 46) { // only allow one "."
		var LRegExp = /\./gi;
		var LFound = !(LRegExp.test(LValue));
		return LFound;}

	else if (LKeyCode >= 48 && LKeyCode <=57){
		return true;}

	else if ((LKeyCode == 8 || LKeyCode == 9 || LKeyCode == 39 ||  LKeyCode == 37 ||  LKeyCode == 38 ||  LKeyCode == 40) && (objNS6) && (PEvent.keyCode)){
		return true;}

	// Allow tab and enter buttons.
	else if (LKeyCode == 9 || LKeyCode == 13)
	    {return true;}		

	else {return false;}
}

// Function to restrict the characters the user is able to enter:"0-9", ".", "-"
function FilterCurrencyField(PEvent, PControl)
{    
	if (PEvent.which) var LKeyCode = PEvent.which;
	else var LKeyCode = PEvent.keyCode;
    
	var LValue = PControl.value;

	if (LKeyCode == 45) { // allow a "-" at the start
		if (LValue.length > 0) return false;
		else return true;}

	else if (LKeyCode == 46) { // only allow one "."
		var LRegExp = /\./gi;
		var LFound = !(LRegExp.test(LValue));
		return LFound;}

	else if (LKeyCode >= 48 && LKeyCode <=57) 
		{return true;}

	else if ((LKeyCode == 8 || LKeyCode == 9 || LKeyCode == 39 ||  LKeyCode == 37 ||  LKeyCode == 38 ||  LKeyCode == 40) && (objNS6) && (PEvent.keyCode))
		{return true;}
	
	// Allow tab and enter buttons.
	else if (LKeyCode == 9 || LKeyCode == 13)
	    {return true;}

	else {return false};
}
	
// Function to restrict the characters the user is able to enter: "0-9"
function FilterDateField(PEvent)
{
	if (PEvent.which) var LKeyCode = PEvent.which;
	else var LKeyCode = PEvent.keyCode;
			
	if ((LKeyCode >= 48 && LKeyCode <=57) || LKeyCode == 8 || LKeyCode == 9 || LKeyCode == 39 ||  LKeyCode == 37 ||  LKeyCode == 38 ||  LKeyCode == 40) return true;
	else return false;
}

// Function to select the text for a given control.
function SelectText(PCurrentControl) 
{
	PCurrentControl.select();
}
		
// Function to display the tooltip for the specified HTML control.
function ShowTooltip(PControl,PEvent,PShowTooltip) 
{ 	
	var LTooltip = GetAttributetValue("cptooltip", PControl, PControl.cptooltip);
	if (LTooltip != null && !IsBlank(LTooltip))
	{
		if (document.layers) 
		{
	   		if (PShowTooltip) 
				{
	      			document.layers["tooltip"].document.write('<tableborder=1><tr><td>'+LTooltip+'</td></tr></table>'); 
	      			document.layers["tooltip"].document.close(); 
	      			document.layers["tooltip"].top = PEvent.pageY+15;
	      			document.layers["tooltip"].left= PEvent.pageX; 
	      			document.layers["tooltip"].visibility="show"; 
	   			}
			else document.layers["tooltip"].visibility="hide"; 
		}
		else if (window.event.srcElement.title == '') window.event.srcElement.title = LTooltip;
   }
   return true;
}

//Function to enable or disable the related field depending on the search operator specifed by the user.
function SetFieldEnableAttribute(PForm, POperatorControl, PFieldControlId, PFieldId) {
	
  var LComboId;
  var LCombo;
  var LSelectedListId;
  var LSelectedList;
  var LEnableNextCombo;
  var LMultiSelect;
  var LEnableAddbutton;
  var LEnableRemovebutton;
  var LPopUpId;
  var LPopUpImage;
	
	var LOperator = POperatorControl.options[POperatorControl.selectedIndex].value
	if ((LOperator == "20") || (LOperator == "13")) var LDisableControl = true;
	else var LDisableControl = false;
	
	for(var LCount = 0; LCount < PForm.elements.length; LCount++) {
		var LElement = PForm.elements[LCount];
		
		if (PFieldId) {	
			var LFieldId = GetAttributetValue("cpfieldid", LElement,LElement.cpfieldid);
			if (LFieldId == PFieldId) LElement.disabled = (LDisableControl);
			
			// 20008471 +++ Sims 02/05/2006: If this is a popup calendar control, 
			// also need to disable the image.
			if(LElement.id)	{
				// Try to get the image button by adding _POPUP to the id.
				LPopUpId = LElement.id + '_POPUP'
				LPopUpImage = GetElementById(LPopUpId);
				
				// Check for nothing
				if(LPopUpImage!=null){
					//Found calendar control, so set it's disabled attribute as required.
					LPopUpImage.disabled = (LDisableControl);
				}
			}
			// 20008471 +++ Sims 02/05/2006: <END>
		}
		else {
			var LElementId = GetAttributetValue("id", LElement, LElement.id);
			if (LElementId == PFieldControlId) 
			{
	        LElement.disabled = (LDisableControl);
	        
	        // If this is a drill down field then make sure that all combo boxes are deselected and disabled
	        var LDrillDown = (GetAttributetValue("cpfieldid", LElement,LElement.cpfieldid) != null);
	        if (LDrillDown)
	        {
	          LElement.disabled = LDisableControl;
	          LEnableNextCombo = (LElement.selectedIndex > 0);
		  
	          LMultiSelect = (GetAttributetValue("cpmultiselect", LElement,LElement.cpmultiselect) != null);
	          
	          if (LMultiSelect)
	          {
	            LSelectedListId = GetAttributetValue("cpdrilldownselectedlistid", LElement,LElement.cpdrilldownselectedlistid);
	            LSelectedList = GetElementById(LSelectedListId);
	            
	            if (LSelectedList != null)
	              LSelectedList.disabled = LDisableControl;
	
	            LEnableAddbutton = !LDisableControl && (LElement.selectedIndex > 0);
	            LEnableRemovebutton = !LDisableControl && (LSelectedList.selectedIndex >= 0)
	            
	            SetAddRemoveButtonAccess(LElementId, LEnableAddbutton, LEnableRemovebutton);
	          }          
	          
	          LComboId = LElementId + DRILL_DOWN_COMBO_NEXT_IDENTIFIER;
	          LCombo = GetElementById(LComboId);
	          
	          while (LCombo != null)
	          {
	            LCombo.disabled = (LDisableControl || (LCombo.selectedIndex <= 0));
	            if (!LDisableControl && LEnableNextCombo)
	            {
	              LCombo.disabled = false;
	              LEnableNextCombo = (LCombo.selectedIndex > 0);
	            }
	            
	            LComboId = LComboId + DRILL_DOWN_COMBO_NEXT_IDENTIFIER;
	            LCombo = GetElementById(LComboId);
	          }
	        }        
	      }	
		}
	}	
} 

// This function will set the access to all the controls on the form.
// This is needed for the search controls where depending on the operator selected
// only certain options can be chosen.
function SetFormAccess(PForm) {

    for(var LCount = 0; LCount < PForm.elements.length; LCount++) 
    {
        var LOperatorControl = PForm.elements[LCount];
	
	// to do: check to see whether this control 

	var LFieldId = 1;
	var LFieldControlId = 1;
	SetFieldEnableAttribute(PForm,LOperatorControl,LFieldControlId,LFieldId);
    }

}

// This function will set access to the contact selection controls when a template
// is selected, in the template selection combo.
function HandleTemplateSelectionChanged( PTemplateControl, PControl1Id, PControl2Id )
{
	var LIndex = PTemplateControl.selectedIndex;
	var LSelectedOption = PTemplateControl.options[LIndex];
	var LEnableCombo;
	var LCombo;
	
	LEnableCombo = GetAttributetValue( "cpcontact1", LSelectedOption, LSelectedOption.cpcontact1 );
	LCombo = GetElementById( PControl1Id );
	if ( LCombo != null )
	{
		if ( LEnableCombo == 1 )
			LCombo.disabled = false;
		else
			LCombo.disabled = true;
	}
	
	LEnableCombo = GetAttributetValue( "cpcontact2", LSelectedOption, LSelectedOption.cpcontact2 );
	LCombo = GetElementById( PControl2Id );
	if ( LCombo != null )
	{
		if ( LEnableCombo == 1 )
			LCombo.disabled = false;
		else
			LCombo.disabled = true;
	}
	
	return;
}

//######################################## Miscellaneous Functions ######################################################

// Function to determine whether the value passed in is blank.  All tabs,carriage returns and spaces are ignored.
function IsBlank(PValue)
{
    for(var LCount = 0; LCount < PValue.length; LCount++) 
    {
        var LChar = PValue.charAt(LCount);
        if ((LChar != ' ') && (LChar != '\n') && (LChar != '\t')) return false;
    }
    return true;
}


// [REQ00451] +++ RP 06/01/2006

// This function converts a multi select label into a multi-line textbox
function CopyLabelToTextbox(PLabel, PTextbox)
{
    var LLabelText = PLabel.innerHTML;
    var LArrElements;

    // Hide the current label
    PLabel.style.display ='none';    
 
    // Split each of the category options into an array
    LArrElements = LLabelText.split('), ');
	
    // Initialise list with a blank value
    PTextbox.value = '';

    // Loop through each of the options
    for (var i=0; i < LArrElements.length; i++)
    {
        // See if we are at the last option
	if(i == LArrElements.length - 1)
	{
	    // Add the option to the list
	    PTextbox.value = PTextbox.value + LArrElements[i] + '\n';

	    // Make sure any ampersands are unescaped
            PTextbox.value = PTextbox.value.replace(/&amp;/,'&');			
	}
	else
	{
	    // See if there are some options to deal with
	    if (LArrElements[i].length>0)
	    {
	        // Add the value to the list, and include a closing bracket
		PTextbox.value = PTextbox.value + LArrElements[i]+')\n';

		// Make sure any ampersands are unescaped
		PTextbox.value = PTextbox.value.replace(/&amp;/,'&'); 
	    }
	}		
    }
}

// This function sets the case of the text to sentence case
function SentenceCaseText(PControl)
{	
  	var returnString = PControl.value; 
	// 20008000 SS *** 06/02/06: Search for a . then any number of spaces.
  	// var pattern = /(\.\s[a-z])/;
	var pattern = /(\.\s*[a-z])/;
  	var result;
  	
	// 20008000 SS +++ 06/02/06: Convert the whole string to lower case first, then only uppercase the first letter of each sentance.
	returnString = returnString.toLowerCase();
  	// replace the first character
  	returnString = ConvertToUpper(returnString, 0);
  	
  	// replace any other first characters
  	while ((result = pattern.exec(returnString)) != null)
  	{
	  	returnString = ConvertToUpper(returnString, result.index + 1);
  	}
  	
    PControl.value =  returnString;
}

// 20007970 SS *** 06/02/06:  Add option for title case formatting to netpoint short text fields.
// This function sets the case of the text to Title case
function TitleCaseText(PControl)
{	
  	var returnString = PControl.value; 
	var pattern = /(\s[a-z])/;
  	var result;
  
	// Make whole string lower case to begin with.
	returnString = returnString.toLowerCase();
  	// replace the first character
  	returnString = ConvertToUpper(returnString, 0);
  	
  	// replace any other first characters
  	while ((result = pattern.exec(returnString)) != null)
  	{
		returnString = ConvertToUpper(returnString, result.index + 1);
  	}
  	
    PControl.value =  returnString;
}
// 20007970 SS *** 06/02/06: <END>

function ConvertToUpper(sourceString, index)
{
	var firstChars = sourceString.slice(0, index);
	var lastChars = sourceString.slice(index+1);
	var indexOfFirstChar = index;
	var stringToReturn;
	var numberOfSpaces = 0;


	// 20008000 SS *** 06/02/06: count the number of spaces, and adjust firstChars and lastChars accordingly.
	while (sourceString.charAt(indexOfFirstChar) == " ")
	{
		numberOfSpaces += 1;
		indexOfFirstChar += 1;
		firstChars += " "
		lastChars = lastChars.substring(1);
	}
	// 20008000 SS *** 06/02/06: <END>
	
	stringToReturn = firstChars + sourceString.charAt(indexOfFirstChar).toUpperCase() + lastChars;
	
	return stringToReturn;
}

// This function converts all of the text in the control to upper case
function UpperCaseText(PControl)
{
    PControl.value = PControl.value.toUpperCase();
}

// This function converts all of the text in the control to lower case
function LowerCaseText(PControl)
{
    PControl.value = PControl.value.toLowerCase();
}

// This routines formats the string to be in currency format
function CurrencyString(PValue)
{
	// Get the string value
	var fValue = parseFloat(PValue);
	var szValue = PValue.toString();
	var iPoint;
	var iDigits;

	// Round the value to 2 decmial places.
	fValue = Math.round(100*fValue)/100;

	// Convert the value back to a string.
	szValue = fValue.toString();

	// Values that are not numbers are displayed as NaN, so if found, then set to 0.
	if(szValue=="NaN")
	{
		szValue = "0";
	}

	// Get the decimal point position.
	iPoint = szValue.indexOf(".");

	// Add zeros according to the position of the decimal point.
	if(iPoint == -1)
	{
		// No decimal point, just add the point, and two zeros
		szValue += ".00";

		// Get the decimal point position again.  (Because it's changed due to adding a point)
		iPoint = szValue.indexOf(".");
	}
	else
	{
		if(iPoint == (szValue.length - 2))
		{
			// If there is one number after the decimal point, add one zero
			szValue += "0";
		}
		else
		{
			if(iPoint == (szValue.length - 1))
			{
				// If there is no numbers after the decimal point, add two zero
				szValue += "00";
			}
			else
			{
				// See if the value should be truncated to two decimal places. Shouldn't be because the round function should have taken care of that.
				if(iPoint < (szValue.length - 2))
				{
					szValue = szValue.substr(0, iPoint + 3);
				}
			}
		}
	}
	
	return szValue;
}

// This function calculates the total cost when unit cost, quantity and total cost text boxes are included in a page
function CalculateTotalCost(PUnitCostControlId, PQuantityControlId, PTotalCostControlId)
{
	var LQuantity;
	var LUnitCost;
	var LTotal;
	var LUnitCostControl;
	var LQuantityControl;	
	var LTotalCostControl;

	if (PUnitCostControlId) 
	{
	    LUnitCostControl = GetElementById(PUnitCostControlId);
	    if (LUnitCostControl == null)
	    {
	        return;
	    }
	}
	
	if (PQuantityControlId != null) 
	{
	    LQuantityControl = GetElementById(PQuantityControlId);
	    if (LQuantityControl == null)
	    {
	        return;
	    }
	}
	
	if (PTotalCostControlId != null) 
	{
	    LTotalCostControl = GetElementById(PTotalCostControlId);
	    if (LTotalCostControl == null)
	    {		
	        return;
	    }
	}	

	// Get the quantity value
	LQuantity = parseFloat(LQuantityControl.value);
	if(isNaN(LQuantity))
	{
		// Not got a value, set the fields to blank
		LQuantityControl.value = "";
	}	

	// Get the unit cost value
	LUnitCost = parseFloat(LUnitCostControl.value);
	
	// See if the unit cost has a value
	if (isNaN(LUnitCost))
	{
		// Not got a value, set the fields to blank
		LUnitCostControl.value = "";
	}
	else
	// Unit cost has a value
	{
		// Round it to two decimal places
		LUnitCost = Math.round(LUnitCost * 100)/100;

		// Apply the currency rule
		LUnitCostControl.value = CurrencyString(LUnitCost);

		// Set the total cost
		LTotal = LQuantity * LUnitCost;
	}

	// Check the total has a number
	if(!isNaN(LTotal))
	{
		// Make sure the number is rounded and set properly
		LTotal = Math.round(LTotal * 100)/100;
		
		// 20010569 *** SR 13/08/2007: If the total cost is greater or less than the maximum we allow in CenterPoint then place the word "error" onto the text box like in Centerpoint.
		if (LTotal>= 922337203685400 || LTotal <= -922337203685400)
		{
				LTotalCostControl.value = "Error";
		}
		else
		{
			LTotalCostControl.value = CurrencyString(LTotal);
		}
	}
	else
	{
		// No value, set it to blank
		LTotalCostControl.value = "";
	}				
}

// [REQ00451] +++ RP 06/01/2006: <END>

//############################################# Validation Functions ##################################################### 

// Function to perform a pattern match.  If the pattern is invalid ie. attribute cppattern does not
// exist or is blank, true is returned to the caller.  If the pattern exists and is matched true 
// is returned to the caller else false is returned.
function ValidatePattern(PControl)
{
	var LCPPattern = GetAttributetValue("cppattern",PControl, PControl.cppattern);
	var LCPPatternMessage = GetAttributetValue("cppatternmessage",PControl, PControl.cppatternmessage);
	var LCPDisplayName = GetAttributetValue("cpdisplayname",PControl, PControl.cpdisplayname);
	
	if (LCPPattern != null && !IsBlank(LCPPattern))
	{
		var LRegExp = GetRegularExpression(LCPPattern);
		if (!LRegExp.test(PControl.value))
		{
			if (LCPPatternMessage != null && !IsBlank(LCPPatternMessage))
			{
				DisplayErrorMessage(LCPPatternMessage)
				PControl.select();
				return false;
			}
			else
			{
				LErrorMessage = MSG_FIELD_FAILED_PATTERN_VALIDATION.replace(/\[FIELDNAME\]/g,LCPDisplayName);
				DisplayErrorMessage(LErrorMessage);
				PControl.select();
				return false;
			}
		}
	}
	return true;
}

//Function to parse a regular expression string and return the regular expression object
function GetRegularExpression(PRegExp)
{
	var LFormattedRegExp = PRegExp;
	var LRegExpAttributes = "";

	if ((LFormattedRegExp.charAt(0) == "/"))
	{
		LFormattedRegExp = LFormattedRegExp.substr(1);
		var LSubReplaceText = LFormattedRegExp.substr(LFormattedRegExp.length - 2);
		switch (LSubReplaceText)
		{
			case "/i":
				LFormattedRegExp = LFormattedRegExp.substr(0,LFormattedRegExp.length - 2);
				LRegExpAttributes = "i";
				break;
			case "/g":
				LFormattedRegExp = LFormattedRegExp.substr(0,LFormattedRegExp.length - 2);
				LRegExpAttributes = "g";
				break;
			case "gi":
				if (LFormattedRegExp.charAt(LFormattedRegExp.length-3) == "/")
				{
					LFormattedRegExp = LFormattedRegExp.substr(0,LFormattedRegExp.length - 3);
					LRegExpAttributes = "gi";
				}
				else
				{
					LFormattedRegExp = PRegExp;
					LRegExpAttributes = "";
				}
				break;
			default:
				if (LFormattedRegExp.charAt(LFormattedRegExp.length -1) == "/")
				{
					LFormattedRegExp = LFormattedRegExp.substr(0,LFormattedRegExp.length - 1);
					LRegExpAttributes = "";
				}
				else
				{
					LFormattedRegExp = PRegExp;
					LRegExpAttributes = "";
				}
				break;
		}
	}
	return CreateRegularExpression(LFormattedRegExp, LRegExpAttributes);
}

//Function to create the regular expression object.
function CreateRegularExpression(PRegExp, PAttributes)
{
	if (PAttributes.length > 0)
	{
		var LRegExp = new RegExp(PRegExp,PAttributes);
		return LRegExp;
	}
	else
	{
		var LRegExp = new RegExp(PRegExp);
		return LRegExp;
	}
}

//This function handles the onchange event for the HTML controls.  The PDayControl and PYearControl are only passed in for 
//the three HTML input date controls or the three HTML select date controls where PControl represents the month control.
function HandleOnChange(PControlId, PDayControlId, PYearControlId)
{
	if (PControlId) {
		var LControl = GetElementById(PControlId);
		if (LControl == null) return true;
	}
	
	if (PDayControlId != null) {
		var LDayControl = GetElementById(PDayControlId);
		if (LDayControl == null) return true;
	}
	
	if (PYearControlId != null) {
		var LYearControl = GetElementById(PYearControlId);
		if (LYearControl == null) return true;
	}
	
	var LCPDataType = GetAttributetValue("cpdatatype", LControl, LControl.cpdatatype);
	var LCPFullDate = GetAttributetValue("cpfulldate", LControl, LControl.cpfulldate);
	
	switch(LCPDataType)
	{
		case "3" :		
			if (!ValidateStringField(LControl)) 
			{
				LControl.select();
				return false;
			}
			break;
		case "4" :		
			if (!ValidateStringField(LControl))
			{
				LControl.select();
				return false;
			}
			break;
		case "1" :		
			if (!ValidateNumberField(LControl))
			{
				LControl.select();
				return false;
			}
			break;
		case "5" :	
			if (!ValidateNumberField(LControl))
			{
				LControl.select();
				return false;
			}
			// 20008001 SS +++ 06/02/06: Added call to routine to format the currency string when it is tabbed away from.
			LControl.value = CurrencyString(LControl.value)
			break;
		case "8" :		
			if (LCPFullDate != null)
			{
				SetFullDate(LControl, LDayControl, LYearControl);
				if (LControl.type != "text") 
				{
					PopulateDateCombo(LYearControl,LControl,LDayControl);
				}
				return true;
			}
			break;
		case "6" :
			if (LCPFullDate != null)
			{
				SetFullDate(LControl, LDayControl, LYearControl);
				if (PControl.type != "text") 
				{
					PopulateDateCombo(LYearControl,LControl,LDayControl);
				}
				return true;
			}
			break;
	  	default :
			break;
	}
	return true;
}

// This function validates the form and all its controls against the criteria set by
// CenterPoint and the form designer. 
function FormValidation(PForm)
{
	var LNotSelectedArray = new Array;
	var LSelectedArray = new Array;
	
    for(var LCount = 0; LCount < PForm.elements.length; LCount++) 
    {
        var LElement = PForm.elements[LCount];
		var LControlValue = LElement.value;
		
		var LCPMandatory = GetAttributetValue("cpmandatory", LElement, LElement.cpmandatory);
		if (LCPMandatory == null || LCPMandatory == "0") LIsMandatory = 0;
		else LIsMandatory = 1;
		
		var LCPFieldId = GetAttributetValue("cpfieldid", LElement,LElement.cpfieldid);
		var LElementId = GetAttributetValue("id", LElement, LElement.id);
		var LCPDisplayName = GetAttributetValue("cpdisplayname", LElement, LElement.cpdisplayname);
		var LIsDrillDown = (GetAttributetValue("cpdrilldown", LElement, LElement.cpdrilldown) != null);
		
		if ((LElement.type != "hidden") && (LElement.type != "button")  && (LCPFieldId != null) && (!LElement.disabled) && (!LIsDrillDown))
		{
			if (LIsMandatory)
			{
				if (LElement.type == "checkbox" || LElement.type == "radio")
					{
						if (LElement.checked == false)
						{
							if (!SearchArray(LSelectedArray,LElement.id)) 
							{
								if (!SearchArray(LNotSelectedArray,LElement.id)) LNotSelectedArray[LNotSelectedArray.length] = LElementId;
							}
						}
						else
						{
							if (!SearchArray(LSelectedArray,LElement.id)) LSelectedArray[LSelectedArray.length] = LElementId;
							if (SearchArray(LNotSelectedArray,LElement.id)) LNotSelectedArray = RemoveElementFromArray(LNotSelectedArray,LElementId); 
						}												
				}
				else if ((LElement.type == "select-one") || (LElement.type == "select-multiple"))
					{
						var LOptionSelected = false;
						
						for (var LOptionsCount = 0; LOptionsCount < LElement.options.length; LOptionsCount++)
						{
							if ((LElement.options[LOptionsCount].selected) && (LElement.options[LOptionsCount].value != ""))
							{
								LOptionSelected = true;
								break;
							}
						}
						if (LOptionSelected == false) 
						{
							var LErrorMessage = MSG_MANDATORY_FIELD_NOT_POPULATED.replace(/\[FIELDNAME\]/g,LCPDisplayName);
							DisplayErrorMessage(LErrorMessage);
							PForm.elements[LCount].focus();
							return false;
						}
					}
				else if (LControlValue == null || LControlValue == "" || (IsBlank(LControlValue)))
					{
						var LErrorMessage = MSG_MANDATORY_FIELD_NOT_POPULATED.replace(/\[FIELDNAME\]/g,LCPDisplayName);
						DisplayErrorMessage(LErrorMessage);
						PForm.elements[LCount].focus();
						return false;
					}
			}
			if (!ValidatePattern(LElement))return false;
			if (!ValidateField(LElement)) return false;
		}
		
		// Validate drill down controls
		// If the first drill down combo is disabled then this is a search field and
		// 'set' or 'not set' is being used as search operator so dont validate
		if (LIsDrillDown && !LElement.disabled)
		{
		  if (!ValidateDrillDownComboBox(LElement, LCPDisplayName))
		  {
        LElement.focus();          
        return false;
      }
    }
     }
     
     if (LNotSelectedArray.length != 0)
     {
		if (objIE4) alert(MSG_UNKOWN_MANDATORY_FIELD_NOT_POPULATED);
		else
		{
			var LMandatoryFields = new String;
			for(var LCount = 0; LCount < LNotSelectedArray.length; LCount++)
			{
				var LElementId = LNotSelectedArray[LCount];
				var LElement = GetElementById(LElementId);
				var LCPDisplayName = GetAttributetValue("cpdisplayname", LElement, LElement.cpdisplayname);
				LMandatoryFields = LMandatoryFields + "\t" + LCPDisplayName + "\n";
			}
			
			if (LNotSelectedArray.length == 1) var LErrorMessage = MSG_NAMED_MANDATORY_FIELD_NOT_POPULATED.replace(/\[FIELDNAMES\]/g,LMandatoryFields);
			else var LErrorMessage = MSG_NAMED_MANDATORY_FIELDS_NOT_POPULATED.replace(/\[FIELDNAMES\]/g,LMandatoryFields);
			DisplayErrorMessage(LErrorMessage);
		}
		
		LArray = null;
		return false;
     } 
     
     return true;
}

//Function to remove the specified element from the specified array.  The function
//returns an array minus the specified element.
function RemoveElementFromArray(PArray,PElementToRemove)
{
	var LReindexedArray = new Array;
	var LReindexedArrayCount = 0;
	
	for(var LCount = 0; LCount < PArray.length; LCount++)
	{
		if (PArray[LCount])
		{
			if (PArray[LCount] != PElementToRemove)
				{
					LReindexedArray[LReindexedArrayCount] = PArray[LCount];
					LReindexedArrayCount++;
				}
		}	
	}
	return LReindexedArray
}

//Function to seacrh the array passed in for the search element passed in.
function SearchArray(PArray, PSearchElement)
{	
	for(var LCount = 0; LCount < PArray.length; LCount++)
	{
		if (PArray[LCount])
		{
			if (PArray[LCount] == PSearchElement)
				{
					return true;
				}
		}	
	}
	return false
}

//Function that calls the relevant validation functions depending on the field's cpdatatype.
function ValidateField(PControl)
{
	var LNoValidation = GetAttributetValue("cpnovalidation", PControl, PControl.cpnovalidation);
	if (LNoValidation !== null) return true;
	var LCPDatatype = GetAttributetValue("cpdatatype", PControl, PControl.cpdatatype);
	var LCPFullDate = GetAttributetValue("cpfulldate", PControl, PControl.cpfulldate);

	switch (LCPDatatype)
	{
		case "3" :		
			if (!ValidateStringField(PControl)) return false;
			break;
		case "4" :		
			if (!ValidateStringField(PControl))	return false;
			break;
		case "1" :		
			if (!ValidateNumberField(PControl)) return false;
			break;
		case "5" :		
			if (!ValidateNumberField(PControl)) return false;
			break;
		case "8" :		
			var LDate = (LCPFullDate != null)?LCPFullDate:PControl.value;
			if (!ValidateDate(LDate,PControl)) return false;
			break;
		case "6" :		
			var LDate = (LCPFullDate != null)?LCPFullDate:PControl.value;
			if (!ValidateDate(LDate,PControl)) return false;
			break;
		default :
			break;
	}
	return true;	
}

//This function validates the string against the specified parameters cpmaxnumber and cpminnumber.
function ValidateStringField(PControl)
{
	var LStringValue = PControl.value;
	var LCPMaxLength = GetAttributetValue("cpmaxlength", PControl, PControl.cpmaxlength);
	var LCPMinLength = GetAttributetValue("cpminlength", PControl, PControl.cpminlength);
	var LCPDisplayName = GetAttributetValue("cpdisplayname", PControl, PControl.cpdisplayname);

	if (LCPDisplayName == null) LCPDisplayName = "";
	
	if ((LCPMaxLength !=null) && (!IsBlank(LCPMaxLength)))
	{
		if (LStringValue.length > LCPMaxLength) 
		{
			var LErrorMessage = MSG_MAX_LENGTH_EXCEEDED.replace(/\[FIELDNAME\]/g,LCPDisplayName);
			LErrorMessage = LErrorMessage.replace(/\[MAXLENGTH\]/g,LCPMaxLength);
			DisplayErrorMessage(LErrorMessage);
			return false;
		}
	}
	if ((LCPMinLength != null) && (!IsBlank(LCPMinLength)))
	{
		if (LStringValue.length < LCPMinLength)
		{
			var LErrorMessage = MSG_MIN_LENGTH_NOT_EXCEEDED.replace(/\[FIELDNAME\]/g,LCPDisplayName);
			LErrorMessage = LErrorMessage.replace(/\[MINLENGTH\]/g,LCPMinLength);
			DisplayErrorMessage(LErrorMessage);
			return false;
		}
	}
	return true;
}

//This function validates the string against the specified parameters.
function ValidateNumberField(PControl)
{
	var LNumberValue = parseFloat(PControl.value);
	var LCPDisplayName = GetAttributetValue("cpdisplayname", PControl, PControl.cpdisplayname);
	var LCPMaxNumber = GetAttributetValue("cpmaxnumber", PControl, PControl.cpmaxnumber);
	var LCPMinNumber = GetAttributetValue("cpminnumber", PControl, PControl.cpminnumber);
	
	if ((LNumberValue != Number(PControl.value)) && !IsBlank(PControl.value))
	{
		var LErrorMessage = MSG_INVALID_NUMBER.replace(/\[FIELDNAME\]/g,LCPDisplayName);
		DisplayErrorMessage(LErrorMessage);
		return false;
	}
	
	if ((LCPMaxNumber != null) && (!IsBlank(LCPMaxNumber)))
	{
		if (LNumberValue > Number(LCPMaxNumber)) 
		{
			var LErrorMessage = MSG_MAX_NUMBER_EXCEEDED.replace(/\[FIELDNAME\]/g,LCPDisplayName);
			LErrorMessage = LErrorMessage.replace(/\[MAXNUMBER\]/g,LCPMaxNumber);
			DisplayErrorMessage(LErrorMessage);
			return false;
		}
	}
	
	if ((LCPMinNumber != null) && (!IsBlank(LCPMinNumber)))
	{
		if (LNumberValue < Number(LCPMinNumber)) 
		{
			var LErrorMessage = MSG_MIN_NUMBER_NOT_EXCEEDED.replace(/\[FIELDNAME\]/g,LCPDisplayName);
			LErrorMessage = LErrorMessage.replace(/\[MINNUMBER\]/g,LCPMinNumber);
			DisplayErrorMessage(LErrorMessage);
			return false;
		}
	}
	return true;
}

//This function insures the date passed in is valid and it complies with the CenterPoint validation.
//The date passed in should always be in the American format "mm/dd/yyyy"
function ValidateDate(PDate, PControl)
{
	if (IsBlank(PDate)) return true;
	
	var LRegExp = /^(\d{1,2})(\/|-)(\d{1,2})\2(\d{4})(\s\d{2}:\d{2})?$/;
	var LMatchArray = PDate.match(LRegExp);	
	var LErrorMessage;
	
	// If the control is a popup selection then their is no 
	// need to format the date value.
	if (PControl.name.indexOf("CPDATEPOPUP") > -1) {return true;}
	if (PControl.name.indexOf("CPPOPUPLOWER") > -1) {return true;}
	if (PControl.name.indexOf("CPPOPUPUPPER") > -1) {return true;}

	if (LMatchArray == null)
	{
		LErrorMessage = MSG_INVALID_DATE.replace(/\[DATE\]/g,PDate);
		DisplayErrorMessage(LErrorMessage);
		return false;
	}
	
	var LMonth = Number(LMatchArray[1]);
	var LDay = Number(LMatchArray[3]);
	var LYear = Number(LMatchArray[4]);
	if (LMonth < 1 || LMonth > 12) 
	{ 
		LErrorMessage = MSG_INVALID_MONTH.replace(/\[MONTH\]/g,LMonth);
		DisplayErrorMessage(LErrorMessage);
		return false;
	}
	if (LDay < 1 || LDay > 31) 
	{
		LErrorMessage = MSG_INVALID_DAY.replace(/\[DAY\]/g,LDay);
		DisplayErrorMessage(LErrorMessage);
		return false;
	}
	if ((LMonth==4 || LMonth==6 || LMonth==9 || LMonth==11) && LDay==31) 
	{	
		var LMonthName = objMonthNames[LMonth - 1];
		LErrorMessage = MSG_INVALID_DAYS_IN_MONTH.replace(/\[MONTH\]/g,LMonthName);
		DisplayErrorMessage(LErrorMessage);
		return false
	}
	if (LMonth == 2) 
	{ // check for february 29th
		var LIsleap = (LYear % 4 == 0 && (LYear % 100 != 0 || LYear % 400 == 0));
			if (LDay>29 || (LDay==29 && !LIsleap)) 
			{
				LErrorMessage = MSG_INVALID_LEAP_YEAR.replace(/\[YEAR\]/g,LYear);
				LErrorMessage = LErrorMessage.replace(/\[DAY\]/g,LDay);
				DisplayErrorMessage(LErrorMessage);
				return false;
			}
	}
	if (LYear < 1900 || LYear > 9999)
	{
		DisplayErrorMessage(MSG_INVALID_YEAR);
		return false;
	}
	
	var LCPForceFutureDate = GetAttributetValue("cpforcefuturedate", PControl, PControl.cpforcefuturedate);
	var LCPForcePastDate = GetAttributetValue("cpforcepastdate", PControl, PControl.cpforcepastdate);
	
	if (LCPForceFutureDate != null || LCPForcePastDate != null)
	{
		var LCurrentDate = new Date()
		var LCurrentDateInMilliSecs = LCurrentDate.valueOf();
		var LDateInMilliSecs = Date.parse(PDate);
		
		if (LDateInMilliSecs < LCurrentDateInMilliSecs &&  LCPForceFutureDate != null)
		{
			LErrorMessage = MSG_FORCE_FUTURE_DATE.replace(/\[DATE\]/g,PDate);
			DisplayErrorMessage(LErrorMessage);
			return false;
		}
		if (LDateInMilliSecs > LCurrentDateInMilliSecs &&  LCPForcePastDate != null)
		{
			LErrorMessage = MSG_FORCE_PAST_DATE.replace(/\[DATE\]/g,PDate);
			DisplayErrorMessage(LErrorMessage);
			return false;
		}
	}
	return true;
}

//Function to set the passed in attributes value.  This function can only be
//used when the client browser is IE6, NS6 or greater.  Set the attributes directly
//if the client browser does not meet these requirements.
function SetAttributetValue(PAttributeName, PControl, PValue)
{
	if (objNS4) return null;
	else if (objIE4 || objIE5) 
	{
		PControl.className = PValue
	}
	else 
	{
		var LAttribute = PControl.attributes.getNamedItem(PAttributeName);
		if (LAttribute == null) return null;
		else 
		{
			LAttribute.value = PValue;
			return;
		}
	}
}

//##################################################### Date manipulation functions ########################################################

// Function to move the focus to the next field when the current field exceeds its maximum length.
function AutoTabWhenFull(PCurrentTextbox,PNextTextboxID, PEvent) 
{	
	var LNextControl = GetElementById(PNextTextboxID);
	if (LNextControl == null) return true;
	
	if (PEvent.which) var LKeyCode = PEvent.which;
	else var LKeyCode = PEvent.keyCode;
	
	// Do not auto tab if the user has pressed tab or shift + tab
	if ((LKeyCode == 9) || (LKeyCode == 16)) return true; 
	
	if (PCurrentTextbox.value.length >= PCurrentTextbox.size) 
		{
			LNextControl.focus();
			LNextControl.select();
		}
	return true;
}

//Ths function is used in conjunction with either a three HTML text date controls or the
//three HTML select date controls to store the full date on the control in the attribute cpfulldate.
//cpfulldate is always held in the American format "mm/dd/yyyy"
function SetFullDate(PMonth,PDay,PYear)
{
	if (IsBlank(PMonth.value) || PMonth == null) var LMonth = "  ";
	else LMonth = PMonth.value;
	
	if (IsBlank(PDay.value) || PDay == null) var LDay = "  ";
	else LDay = PDay.value;
	
	if (IsBlank(PYear.value) || PYear == null) var LYear = "  ";
	else LYear = PYear.value;
	
	if (IsBlank(LMonth + LDay + LYear)) LDate = "";
	else LDate = LMonth + "/" + LDay + "/" + LYear;
	
	// Cannot set attribute value using SetAttributetValue so must set it directly.
	if (objIE4 || objIE5) {
		PMonth.cpfulldate = LDate;
		PDay.cpfulldate = LDate;
		PYear.cpfulldate = LDate;
	}
	else {
		SetAttributetValue("cpfulldate", PMonth, LDate);
		SetAttributetValue("cpfulldate", PDay, LDate);
		SetAttributetValue("cpfulldate", PYear, LDate);
	}
}

//Function to return the number of days in a specified month.
function GetDaysInMonth(year, month) 
{
    var LDate = new Date(year, month, 32);
    var LDays = 32 - LDate.getDate(); 
    return LDays;
}

// Function to populate the date select controls with the correct number of days, months and years.
function PopulateDateCombo(PYearCombo, PMonthCombo, PDayCombo)
{
	// Get the currently selected year and month.
    	var LSelectedYear = PYearCombo.options[PYearCombo.selectedIndex].value;
    	var LSelectedMonth = PMonthCombo.options[PMonthCombo.selectedIndex].value;
	var LNumDaysInNewMonth = GetDaysInMonth(LSelectedYear,LSelectedMonth-1);
	var LNumDaysInPreviousMonth =  PDayCombo.options[PDayCombo.length-1].value;

	if (LNumDaysInNewMonth != LNumDaysInPreviousMonth)
	{
		// If there are more days in the month that was selected then add
		// them to the combo, otherwise remove them.
		if (LNumDaysInNewMonth > LNumDaysInPreviousMonth)
		{
			LNumDaysInPreviousMonth ++;
			for (var LCount = LNumDaysInPreviousMonth;  LCount <= LNumDaysInNewMonth; LCount++) 
			{
				PDayCombo.options[PDayCombo.length] = new Option(LCount, LCount);
			}
		}
		else
		{ 
			var LFirstDayIndex = 0
			for (var LCount = PDayCombo.length-1; LCount >= 0; LCount--)
			{
				if (PDayCombo.options[LCount].value == 1) {LFirstDayIndex = LCount;}
				if (PDayCombo.options[LCount].selected == true) {var LSelectedIndex = LCount;}
				if (PDayCombo.options[LCount].value == LNumDaysInNewMonth) {var LNewComboSize = LCount;}
			}
			PDayCombo.length = LNewComboSize + 1;
			// Ensure the selected index remains in the constaints of the combo
			if (LSelectedIndex>PDayCombo.length - 1) {LSelectedIndex=0;}
			PDayCombo.options[LSelectedIndex].selected = true;
		}
	}
	
	SetFullDate(PMonthCombo, PDayCombo, PYearCombo);
}

// Function to populate the hour combo depending on the value of the minute combo.
function PopulateHourCombo(PMinuteCombo, PHourComboId)
{
	var LControl = GetElementById(PHourComboId);
	if (LControl == null) return true;
	
    	var LSelectedMinute = PMinuteCombo.options[PMinuteCombo.selectedIndex].value;
   	var LSelectedHour = LControl.options[LControl.selectedIndex].value;
	for (var LCount = 0; LCount < LControl.length; LCount++)
	{
		var LControlValue = LControl.options[LCount].value;
		if (((LControlValue == "") && (LSelectedMinute == "")) || ((LControlValue !== "") && (LSelectedMinute !== "") && (LSelectedHour =="")))
		{
			LControl.options[LCount].selected = true;
			return true;
		}		
	}
}

// Function to populate the minute combo depending on the value of the hour combo.
function PopulateMinuteCombo(PHourCombo, PMinuteComboId)
{
	var LControl = GetElementById(PMinuteComboId);
	if (LControl == null) return true;

	var LSelectedHour = PHourCombo.options[PHourCombo.selectedIndex].value;
	var LSelectedMinute = LControl.options[LControl.selectedIndex].value;

	for (var LCount = 0; LCount < LControl.length; LCount++)
	{
		var LControlValue = LControl.options[LCount].value;
		if (((LControlValue == "") && (LSelectedHour == "")) || ((LControlValue !== "") && (LSelectedHour !== "") && (LSelectedMinute =="")))
		{
			LControl.options[LCount].selected = true;
			return true;
		}		
	}
}

// Function to display the specified error message in a label or a message box.
function DisplayErrorMessage(PErrorMessage) {
	var LLabelFound = false;
	var LWarningLabelArray = document.getElementsByName(HTML_NAME_WARNING_MESSAGE_LABEL);
	if ((LWarningLabelArray.length > 0) && objUseErrorLabel) {
		for(var LCount = 0; LCount < LWarningLabelArray.length; LCount++) {
			LWarningLabelArray[LCount].innerHTML = PErrorMessage;
			LLabelFound = true;
		}
	}
	if (!LLabelFound) alert(PErrorMessage);
}

function LZ(x) {
	return(x<0||x>9?"":"0")+x
}

// Returns true if date string matches format of format string and
// is a valid date. Else returns false.
// It is recommended that you trim whitespace around the value before
// passing it to this function, as whitespace is NOT ignored!
function isDate(val,format) {
	var date=getDateFromFormat(val,format);
	if (date==0) { return false; }
	return true;
	}

//   Compare two date strings to see which is greater.
//   Returns:
//   1 if date1 is greater than date2
//   0 if date2 is greater than date1 of if they are the same
//  -1 if either of the dates is in an invalid format
function compareDates(date1,dateformat1,date2,dateformat2) {
	var d1=getDateFromFormat(date1,dateformat1);
	var d2=getDateFromFormat(date2,dateformat2);
	if (d1==0 || d2==0) {
		return -1;
		}
	else if (d1 > d2) {
		return 1;
		}
	return 0;
	}

// Returns a date in the output format specified.
// The format string uses the same abbreviations as in getDateFromFormat()
function formatDate(PDate, PFormat) {
	PFormat=PFormat+"";
	var LFormattedString="";
	var LFormatIndex=0;
	var LCharacter="";
	var token="";
	var LYear=PDate.getYear()+"";
	var LMonth=PDate.getMonth()+1;
	var LDay=PDate.getDate();
	var LHours=PDate.getHours();
	var LMinutes=PDate.getMinutes();
	var LSeconds=PDate.getSeconds();
	var LAmPmIndex=0;
	var LFormat = new String();

	// Convert the format to lower case so the interpretation is not case sensitive.
	LFormat = PFormat.toLowerCase();

	// Convert real date parts into formatted versions
	var value=new Object();
	if (LYear.length < 4)  {LYear=""+(LYear-0+1900);}

	value["y"]=""+LYear;
	value["yyyy"]=LYear;
	value["yy"]=LYear.substring(2,4);
	value["m"]=LMonth;
	value["mm"]=LZ(LMonth);
	value["mmm"]=MONTH_NAMES[LMonth-1];
	value["d"]=LDay;
	value["dd"]=LZ(LDay);
	value["h"]=LHours;
	value["hh"]=LZ(LHours);
	value["n"]=LMinutes;
	value["nn"]=LZ(LMinutes);
	value["s"]=LSeconds;
	value["ss"]=LZ(LSeconds);

	// If the format string contains AMPM then display the time in 12hr format
	LAmPmIndex = LFormat.indexOf("ampm");
	if (LAmPmIndex > -1) {
		if (LHours==0) {value["h"]=12;}
		else if (LHours>12) {value["h"]=LHours-12;}
		else {value["h"]=LHours;}

		value["hh"]=LZ(value["h"]);

		if (LHours > 11) { value["ampm"]="PM"; }
		else { value["ampm"]="AM"; }
		}

	// Iterate through the format string and build the formatted date string. 
	while (LFormatIndex < LFormat.length) {
		LCharacter = LFormat.charAt(LFormatIndex);
		token="";
		while ((LFormat.charAt(LFormatIndex)==LCharacter) && (LFormatIndex < LFormat.length)) {
			token += LFormat.charAt(LFormatIndex++);
			}
		if (value[token] != null) { LFormattedString=LFormattedString+ value[token]; }
		else { LFormattedString = LFormattedString + token; }
		}
	return LFormattedString;
	}
	

// Routine to check whether the value is an integer
function IsInteger(val) {
	var digits="1234567890";
	for (var i=0; i < val.length; i++) {
		if (digits.indexOf(val.charAt(i))==-1) { return false; }
		}
	return true;
	}

function GetInt(str,i,minlength,maxlength) {
	for (var x=maxlength; x>=minlength; x--) {
		var token=str.substring(i,i+x);
		if (token.length < minlength) { return null; }
		if (IsInteger(token)) { return token; }
		}
	return null;
	}
	

// This function takes a date string and a format string. It matches
// If the date string matches the format string, it returns the 
// getTime() of the date. If it does not match, it returns 0.
function getDateFromFormat(PValue, PFormat) {
	PValue=PValue+"";
	PFormat=PFormat+"";
	var i_val=0;
	var LFormatIndex=0;
	var c="";
	var token="";
	var token2="";
	var LMinLength,LMaxLength;
	var LCurrentDate=new Date();
	var LYear=LCurrentDate.getYear();
	var LMonth=LCurrentDate.getMonth()+1;
	var LDays=LCurrentDate.getDate();
	var LHours=LCurrentDate.getHours();
	var LMinutes=LCurrentDate.getMinutes();
	var LSeconds=LCurrentDate.getSeconds();
	var ampm="";

	while (LFormatIndex < PFormat.length) {
		// Get next token from format string
		c=PFormat.charAt(LFormatIndex);
		token="";
		while ((PFormat.charAt(LFormatIndex)==c) && (LFormatIndex < PFormat.length)) {
			token += PFormat.charAt(LFormatIndex++);
			}
		// Extract contents of value based on format token
		if (token=="yyyy" || token=="yy" || token=="y" || token=="YYYY" || token=="YY" || token=="Y") {
			if (token=="yyyy" || token=="YYYY" ) { LMinLength=4;LMaxLength=4; }
			if (token=="yy" || token=="YY")     { LMinLength=2;LMaxLength=2; }
			if (token=="y" || token=="Y")       { LMinLength=2;LMaxLength=4; }
			LYear=GetInt(PValue,i_val,LMinLength,LMaxLength);
			if (LYear==null) { return 0; }
			i_val += LYear.length;
			if (LYear.length==2) {
				if (LYear > 70) { LYear=1900+(LYear-0); }
				else { LYear=2000+(LYear-0); }
				}
			}
		else if (token=="MMM" || token=="mmm"){
			LMonth=0;
			for (var i=0; i<MONTH_NAMES.length; i++) {
				var month_name=MONTH_NAMES[i];
				if (PValue.substring(i_val,i_val+month_name.length).toLowerCase()==month_name.toLowerCase()) {
					LMonth=i+1;
					if (LMonth>12) { LMonth -= 12; }
					i_val += month_name.length;
					break;
					}
				}
			if ((LMonth < 1)||(LMonth>12)){return 0;}
			}
		else if (token=="MM" || token=="M" || token=="mm" || token=="m") {
			LMonth=GetInt(PValue,i_val,token.length,2);
			if(LMonth==null||(LMonth<1)||(LMonth>12)){return 0;}
			i_val+=LMonth.length;}
		else if (token=="dd" || token=="d" || token=="DD" || token=="D") {
			LDays=GetInt(PValue, i_val,token.length, 2);
			if(LDays==null||(LDays<1)||(LDays>31)){return 0;}
			i_val+=LDays.length;}
		else if (token=="hh"||token=="h") {
			LHours=GetInt(PValue, i_val,token.length, 2);
			if(LHours==null||(LHours<1)||(LHours>12)){return 0;}
			i_val+=LHours.length;}
		else if (token=="HH"||token=="H") {
			LHours=GetInt(PValue, i_val,token.length, 2);
			if(LHours==null||(LHours<0)||(LHours>23)){return 0;}
			i_val+=LHours.length;}
		else if (token=="KK"||token=="K") {
			LHours=GetInt(PValue, i_val, token.length, 2);
			if(LHours==null||(LHours<0)||(LHours>11)){return 0;}
			i_val+=LHours.length;}
		else if (token=="kk"||token=="k") {
			LHours=GetInt(PValue,i_val,token.length, 2);
			if(LHours==null||(LHours<1)||(LHours>24)){return 0;}
			i_val+=LHours.length;LHours--;}
		else if (token=="mm"||token=="m") {
			LMinutes=GetInt(PValue, i_val, token.length, 2);
			if(LMinutes==null||(LMinutes<0)||(LMinutes>59)){return 0;}
			i_val+=LMinutes.length;}
		else if (token=="ss"||token=="s") {
			LSeconds=GetInt(PValue, i_val, token.length, 2);
			if(LSeconds==null||(LSeconds<0)||(LSeconds>59)){return 0;}
			i_val+=LSeconds.length;}
		else if (token=="a") {
			if (PValue.substring(i_val,i_val+2).toLowerCase()=="am") {ampm="AM";}
			else if (PValue.substring(i_val,i_val+2).toLowerCase()=="pm") {ampm="PM";}
			else {return 0;}
			i_val+=2;}
		else {
			if (PValue.substring(i_val,i_val+token.length)!=token) {return 0;}
			else {i_val+=token.length;}
			}
		}
	// If there are any trailing characters left in the value, it doesn't match
	if (i_val != PValue.length) { return 0; }

	// Is date valid for month?
	if (LMonth==2) {
		// Check for leap years
		if ( ( (LYear%4==0)&&(LYear%100 != 0) ) || (LYear%400==0) ) { // leap year
			if (LDays > 29){ return false; }
			}
		else { if (LDays > 28) { return false; } }
		}
	if ((LMonth==4)||(LMonth==6)||(LMonth==9)||(LMonth==11)) {
		if (LDays > 30) { return false; }
		}

	// Correct hours value
	if (LHours<12 && ampm=="PM") { LHours+=12; }
	else if (LHours>11 && ampm=="AM") { LHours-=12; }
	var newdate=new Date(LYear,LMonth-1,LDays,LHours,LMinutes,LSeconds);
	return newdate.getTime();
	}

// ************ Drill Down Combo Boxes. ************
// Constructor for Items array
function Item(PText, PValue)
{
  this.text = PText;
  this.value = PValue;
  this.items = new Array;
  this.linkedFieldValues = null;
}

// This routine is called from the onLoad event in the form to initialise the drill down controls
function InitialiseDrillDownControls()
{
  var LElement;
  var LElementId;
  var LDrillDown;
  var LItemArrayName;
  var LItemArray;
  var LForm;
  var LItemArrayId;
  var LItemArray;

  LForm = document.forms["frmNetPoint"];
	if ( LForm == null )
		return;

  // Iterate through the elements in the form
	for (var i = 0; i < LForm.elements.length; i++) 
  {
    // Get the drill down attribute
		LElement = LForm.elements[i];
		LDrillDown = (GetAttributetValue("cpdrilldown", LElement, LElement.cpdrilldown) != null);
		
		// Check this element is a drill down combo box
		if (!LDrillDown) continue;
		
    // Get the item array associated with this combobox
    LItemArrayName = GetAttributetValue("cpitemarrayname", LElement, LElement.cpitemarrayname);
    LElementId = GetAttributetValue("id", LElement, LElement.id);
    LItemArray = eval(LItemArrayName);
    if (LItemArray == null) continue;
    
    // Get the default value of the combo
    LDefaultValue = GetDefaultValue(LElementId, LElement);

    // Initialise the combo
    InitialiseCombo(LElement, LElementId, LItemArray, LDefaultValue);
	}
}

// This function returns the default value for the combo boxes
function GetDefaultValue(PBaseComboId, PBaseCombo)
{
  // PBaseComboId                         I: The base combo ID
  // PBaseCombo                           I: The base combo object to populate
  
  var LDefaultValue = "";
  var LOption;
  var LSelectIndex;
  var LControlId;
  var LControl;
  var LMultiSelect;
  
  if (PBaseComboId == null || PBaseCombo == null) 
    return LDefaultValue;
  
  // Get the multiselect attribute
  LMultiSelect = (GetAttributetValue("cpmultiselect", PBaseCombo, PBaseCombo.cpmultiselect) != null); 
  
  if (!LMultiSelect)
  {
    // Get the hidden control
    LControlId = GetAttributetValue("cphiddencontrolid", PBaseCombo, PBaseCombo.cphiddencontrolid); 
    LControl = GetElementById(LControlId);
    
    // Get the default value from the hidden control
    if (LControl != null) 
      LDefaultValue = GetAttributetValue("value", LControl, LControl.value);
  }
  
  // Return to caller
  LDefaultValue = InsertHTMLSpecialCharacters(LDefaultValue);
  return LDefaultValue;
}

// This function initialises a given base combo box and its children with values and 
// sets its default value
function InitialiseCombo(PBaseCombo, PBaseComboId, PItems, PDefaultValue)
{
  // PCBaseombo                       I: The base combo object to populate
  // PBaseComboId                     I: The base combo ID
  // PItems                           I: The items array to populate the combos with
  // PDefaultValue                    I: The default value to set
  
  var LSelectedItem
  var LSelectedItemArray = new Array;
  var LComboControl;
  var LItemArray = PItems;
  var LElementId = PBaseComboId;
  var LAddButtonEnabled;
  var LLeafOnly;
  var LMultiSelect;
  var LSelectedListId;
  var LSelectedListControl;
  var LHiddenControlId;
  var LHiddenControl;
  var LIsDisabled;
  
  if (PBaseCombo == null || PBaseComboId == null || PItems == null) return;
  
  // Get the leaf only and multiselect attribute
  LLeafOnly = (GetAttributetValue("cpleafonly", PBaseCombo, PBaseCombo.cpleafonly) != null);
  LMultiSelect = (GetAttributetValue("cpmultiselect", PBaseCombo, PBaseCombo.cpmultiselect) != null);    
  LHiddenControlId = GetAttributetValue("cphiddencontrolid", PBaseCombo, PBaseCombo.cphiddencontrolid); 
  
  // Get the hidden control for this drill down combo
  LHiddenControl = GetElementById(LHiddenControlId);
  LIsDisabled = (LHiddenControl != null) && LHiddenControl.disabled
  
  LComboControl = GetElementById(LElementId);
  if (LComboControl != null)
    LIsDisabled = LComboControl.disabled

  // If the default value is an empty string or cannot be found then populate the first combo and return
  if ((PDefaultValue == "") || (!GetSelectedItemIndexes(LItemArray, PDefaultValue, 0, LSelectedItemArray)))
  {
    // Populate the combo box options
    PopulateCombo(LElementId, LItemArray, 0); 
    
    // If this drill down combo is being used as a search field then field may be set to 'set' or 'not set'
    // by default so need to disable the combo if one of these is set, the hidden field disabled value should
    // relect this
    if (LIsDisabled)
    {
      LComboControl = GetElementById(LElementId);
      if (LComboControl != null)
        LComboControl.disabled = true;
    }
    
    // If this is a multiselect combo then set button access and deselect items in list
    if (LMultiSelect)    
    {
      // Get the selected list ID
      LSelectedListId = GetAttributetValue("cpdrilldownselectedlistid", PBaseCombo, PBaseCombo.cpdrilldownselectedlistid);
      
      // Get the selected list control
      LSelectedListControl = GetElementById(LSelectedListId);
      if (LSelectedListControl == null) return;   
      
      // Deselect the items in the list
      LSelectedListControl.selectedIndex = -1;
      
      // Sort the option alphabetically
      SortSelectionListOptions(LSelectedListControl);
      
      SetAddRemoveButtonAccess(PBaseComboId, false, null);
      PerformListClick(PBaseComboId); 
    }

    return;
  }

  // Populate the first combo box
  LSelectedItem = LSelectedItemArray[0];
  PopulateCombo(LElementId, LItemArray, LSelectedItem + DRILL_DOWN_COMBO_DESELECTION_ITEM);  
  LItemArray = LItemArray[LSelectedItem].items; 
  
  // If this drill down combo is being used as a search field then field may be set to 'set' or 'not set'
  // by default so need to disable the combo if one of these is set, the hidden field disabled value should
  // relect this
  if (LIsDisabled)
  {
    LComboControl = GetElementById(LElementId);
    if (LComboControl != null)
      LComboControl.disabled = true;   
  }
  
  // Iterate through the selected item array, populating the combo controls
  for (var j = 1; j < LSelectedItemArray.length; j++)
  {          
    // Get the next selected item
    LSelectedItem = LSelectedItemArray[j];    
    if (LSelectedItem == null) continue;     

    // Populate the combo box
    LElementId = LElementId + DRILL_DOWN_COMBO_NEXT_IDENTIFIER;           
    PopulateCombo(LElementId, LItemArray, LSelectedItem + DRILL_DOWN_COMBO_DESELECTION_ITEM);
  
    // If the first combo is disabled then all other must be as well
    LComboControl = GetElementById(LElementId); 
    if (LComboControl != null && LIsDisabled)
      LComboControl.disabled = true;

    // Move to the next level in the item hierarchy
    LItemArray = LItemArray[LSelectedItem].items;
  }
   
  // If the next control is valid then populate it
  LElementId += DRILL_DOWN_COMBO_NEXT_IDENTIFIER;
  LComboControl = GetElementById(LElementId);
  if (LComboControl != null)
  {
    PopulateCombo(LElementId, LItemArray, 0);
    
    if (LIsDisabled)
       LComboControl.disabled = true;
  }
      
  if (LMultiSelect)
  {
    // Get the selected list ID
    LSelectedListId = GetAttributetValue("cpdrilldownselectedlistid", PBaseCombo, PBaseCombo.cpdrilldownselectedlistid);
    
    // Get the selected list control
    LSelectedListControl = GetElementById(LSelectedListId);
    if (LSelectedListControl == null) return;   
    
    // Deselect the items in the list
    LSelectedListControl.selectedIndex = -1; 
    
    // Sort the option alphabetically
    SortSelectionListOptions(LSelectedListControl);
    
    // If this is a multiselect combo then set the add and remove buttons access properties
    LAddButtonEnabled = (LSelectedItem >= 0) || !LLeafOnly;
    SetAddRemoveButtonAccess(PBaseComboId, LAddButtonEnabled, null);
    PerformListClick(PBaseComboId); 
  }     
}

// This routine populates a given combo box and selects its default value
// [Category Linker Server Enhancements] *** 24/05/2007: Provide the ability to prevent clearing down of the drill down combo
// function PopulateCombo(PComboId, PItems, PSelectedIndex)
function PopulateCombo(PComboId, PItems, PSelectedIndex, PClearDrillDown)
{
  // PComboId                             I: The ID of the combo box to populate
  // PItems                               I: The array of items to populate the combo box with
  // PSelectedIndex                       I: The item index used to select an item
  // PClearDrillDown                      I: Whether the drill down combo should be cleared
  
  var LComboBox;
  var LItem;
  var LDecodedItemText;
  var LClearDrillDown;
  
  if (PComboId == null || PItems == null || PSelectedIndex == null) return;

  if(PClearDrillDown == null)
    LClearDrillDown = true;
  else
    LClearDrillDown = PClearDrillDown;
    
  // Get the control 
  LComboBox = GetElementById(PComboId);
  if (LComboBox == null) return;

  // Clear the combo box and its child combo boxes and set their
  // access properties
  if(LClearDrillDown)
    ClearDrillDownCombo(LComboBox);
  
  // Add a blank option for deselection
  LComboBox.options[0] = new Option("", "");
  
  // Iterate through the items and add them to the list
  for (var i = DRILL_DOWN_COMBO_DESELECTION_ITEM; i <= PItems.length; i++)
  {
    LItem = PItems[i - DRILL_DOWN_COMBO_DESELECTION_ITEM];
    LDecodedItemText = ReplaceHTMLSpecialCharacters(LItem.text);
		LComboBox.options[i] = new Option(LDecodedItemText, LItem.value);
		// Need to ensure that if the field has linked field values these are added to the option.
		if ( LItem.linkedFieldValues != null )
		{
			LComboBox.options[i].cplinkedfieldvalues = LItem.linkedFieldValues;
		}
  }  

  LComboBox.disabled = false;
  LComboBox.readOnly = false;
  
  // Set the selected index if it is valid
  if (PSelectedIndex >= 0 && PSelectedIndex < LComboBox.options.length)
    LComboBox.selectedIndex = PSelectedIndex;
  
  // Disable the combo box if there are no options available
  if (LComboBox.options.length == 1)
  {    
    LComboBox.disabled = true;
    LComboBox.readOnly = true;  
  }

  if(!LComboBox.disabled) 
  {
      // [REQ00442] +++ RP 05/01/06: Resets the colour of the combo box depending on its state
      LMandatory = GetAttributetValue("cpmandatory", LComboBox, LComboBox.cpmandatory) == "0" ? false : true;
      LCategoryField = GetAttributetValue("cpdatatype", LComboBox, LComboBox.cpdatatype) == "9" ? false : true;
      if(LMandatory)
      {
          if(LCategoryField)
	  {
              SetAttributetValue(CLASS_ATTRIBUTE_NAME, LComboBox, "FormPulldownMand");
	  }
	  else
	  {
	      SetAttributetValue(CLASS_ATTRIBUTE_NAME, LComboBox, "FormPulldownMand");
	  }
      }
      else
      { 
          if(LCategoryField)
	  {
	      SetAttributetValue(CLASS_ATTRIBUTE_NAME, LComboBox, "FormPulldown");   	
	  }
	  else
	  {
              SetAttributetValue(CLASS_ATTRIBUTE_NAME, LComboBox, "FormPulldown");
	  }
      }
   }
   else
   {
      SetAttributetValue(CLASS_ATTRIBUTE_NAME, LComboBox, "FormPullDownDisabled")
   }
    // [REQ00442] +++ RP 05/01/06: <END>    
}

// This function returns the selected item object by recursing through the combo boxes
function GetSelectedItem(PBaseComboId, PSelectedItem, PDisplayStyle)
{
  // PBaseComboId                           I: The ID of the base combo
  // PSelectedItem                          IO: The selected item 
  // PDisplayStyle                          I: The display style for the category
  
  var CHARS_SEPARATOR = "/";
  var CHARS_OPEN_BRACKET = "(";
  var CHARS_CLOSE_BRACKET = ")";
  var CHARS_SPACE = " ";
  var LBaseComboBoxControl;
  var LSelectedIndex;
  var LSelectedItem;
  var LNextSelectedItem;
  var LNextCombo;
  var LNextComboIsValid;

  if (PBaseComboId == null || PSelectedItem == null) return;
  
  // Get the base combo box control
  LComboBoxControl = GetElementById(PBaseComboId);
  if (LComboBoxControl == null) return;
  
  // Get the selected index and check it is valid (greater than zero and
  // not zero, i.e. the blank option)
  LSelectedIndex = LComboBoxControl.options.selectedIndex;
  if (LSelectedIndex <= 0) return;
  
  // Set the return value
  LSelectedItem = LComboBoxControl.options[LSelectedIndex];

  // Format the selected option text according to display style
  switch (PDisplayStyle)
  {
  case DISPLAY_TYPE_USE_FULL_HIERARCHY:
  {
    if (PSelectedItem.text == "")
      PSelectedItem.text = LSelectedItem.text;
    else
      PSelectedItem.text += CHARS_SEPARATOR + LSelectedItem.text;
    break;
  }
  case DISPLAY_TYPE_HIERARCHY_IN_BRACKETS:
  {
    // Determine whether next combo box is valid
    LNextCombo = GetElementById(PBaseComboId + DRILL_DOWN_COMBO_NEXT_IDENTIFIER);    
    LNextComboIsValid = ((LNextCombo != null) && (LNextCombo.selectedIndex > 0));
    
    // If the next combo box is not valid then finish the selected item text
    if (!LNextComboIsValid)
    {
      if (PSelectedItem.text == "")
        PSelectedItem.text = LSelectedItem.text
      else
        PSelectedItem.text = LSelectedItem.text + CHARS_SPACE + PSelectedItem.text + CHARS_CLOSE_BRACKET;
    }
    // Else if it is valid then just add the current option text to the hierarchy
    else
    {
      if (PSelectedItem.text == "")
        PSelectedItem.text = CHARS_OPEN_BRACKET + LSelectedItem.text;
      else
        PSelectedItem.text += CHARS_SEPARATOR + LSelectedItem.text;
    }
    break;
  }
  case DISPLAY_TYPE_LEAF_ONLY:
  default:
  {
    PSelectedItem.text = LSelectedItem.text;
    break;
  }
  }

  // Set the selected item value to the current item value
  PSelectedItem.value = LSelectedItem.value;
  
  // Check the next combo box for values further down hierarchy
  GetSelectedItem(PBaseComboId + DRILL_DOWN_COMBO_NEXT_IDENTIFIER, PSelectedItem, PDisplayStyle);
}

// This function returns an array of the indexes needed to find the selected value in the 
// combo boxs' items array
function GetSelectedItemIndexes(PItems, PSelectedItemValue, PLevel, PItemIndexes)
{
  // PItems                                 I: The combo boxs items array
  // PSelectedItemValue                     I: The selected item to find the index for
  // PLevel                                 I: The level of the items array currently being searched
  // PItemIndexes                           O: The array of item indexes
  
  var LFound = false;
  var LNextItemArray;
  
  if (PItems == null || PSelectedItemValue == null || 
      PLevel == null || PItemIndexes == null) return;
  
  // Iterate through the items array
  for (var i = 0; i < PItems.length; i++)
  {
    // Check to see if we have found the value
    if (PItems[i].value == PSelectedItemValue)
    {
      // Set the index of this value and return 
      PItemIndexes[PLevel] = i;
      
      // 20007153 - TPH - 16/01/2006 - Ensure that the selected items are found if the user and usergroup names
      // are the same.
      // Check if this item has its own array of items
      LNextItemArray = PItems[i];
      if (LNextItemArray != null)
      {        
        // Check this item array for the selected value
        if (GetSelectedItemIndexes(LNextItemArray.items, PSelectedItemValue, PLevel + 1, PItemIndexes)) 
        {
          PItemIndexes[PLevel] = i;          
        }
      }
      // 20007153 - TPH - 16/01/2006 - END
      
      return true;
    }
    else
    {
      // Check if this item has its own array of items
      LNextItemArray = PItems[i];
      if (LNextItemArray != null)
      {        
        // Check this item array for the selected value
        if (LFound = GetSelectedItemIndexes(LNextItemArray.items, PSelectedItemValue, PLevel + 1, PItemIndexes)) 
        {
          PItemIndexes[PLevel] = i;
          return true;
        }
      }
    }
  }
    
  // Return to caller
  return LFound;
}

// This routine is called when the user changes an option in one of the combo boxes
// [Category Linker Server Enhancements] *** 23/05/2007: Allow a flag to be passed in to indicate whether the drill down combo should be cleared
// function PerformDrillDown(PBaseComboId, PIndex)
function PerformDrillDown(PBaseComboId, PIndex, PClearDrillDownCombo)
{
  // PBaseComboId                           I: The ID of the base combo
  // PIndex                                 I: The level of the selected combo box
  // PClearDrillDownCombo                   I: Whether to clear the drill down combo
  
  var USER_FIELD_TYPE = "13"
  var LSelectedIndexes = new Array;
  var LSelectedIndex;
  var LBaseComboBoxControl;
  var LComboBoxControl;
  var LComboBoxId;
  var LItemArrayName;  
  var LItemArray;
  var LNextComboId;
  var LAddButtonEnabled;
  var LLeafOnly;
  var LIsLeaf;
  var LMandatory
  var LClearDrillDown;
  
  var LIsUserField;
  
  if (PBaseComboId == null || PIndex == null) return;
  
  if(PClearDrillDownCombo == null)
    LClearDrillDown = true;
  else
    LClearDrillDown = PClearDrillDownCombo;
  
  // Get the selected indexes of the combo boxes
  for (var i = 0; i <= PIndex; i++)
  {
    // Get the combobox control
    LComboBoxId = GenerateDrillDownComboId(PBaseComboId, i);
    LComboBoxControl = GetElementById(LComboBoxId);
    if (LComboBoxControl == null) return;
    
    // Save the selected index, -1 to account for empty option
    LSelectedIndexes[i] = LComboBoxControl.selectedIndex - DRILL_DOWN_COMBO_DESELECTION_ITEM;
 
  }
  
  // Get the base combo box control and check it is valid
  LBaseComboBoxControl = GetElementById(PBaseComboId);
  if (LBaseComboBoxControl == null) return;
  
  // Get the multiselect and leafonly attributes
  LMultiSelect = (GetAttributetValue("cpmultiselect", LBaseComboBoxControl, LBaseComboBoxControl.cpmultiselect) != null);  
  LLeafOnly = (GetAttributetValue("cpleafonly", LBaseComboBoxControl, LBaseComboBoxControl.cpleafonly) != null);  
  
  // 20007030 - TimH - 12/05/2005 - User fields should be treated as leaf only
  LIsUserField = (GetAttributetValue("cpdatatype", LBaseComboBoxControl, LBaseComboBoxControl.cpdatatype) == USER_FIELD_TYPE);
    
  if (LIsUserField)
    LLeafOnly = true
  // 20007030 - TimH - 12/05/2005 - END 
  
  // Get the array associated with this combobox
  LItemArrayName = GetAttributetValue("cpitemarrayname", LBaseComboBoxControl, LBaseComboBoxControl.cpitemarrayname);
  LItemArray = eval(LItemArrayName);
  
  // Set LItemArray to point to the correct level within the allocation array item hierarchy
  for (var i = 0; i < PIndex; i++)
  {
    if(LSelectedIndexes[i] > -1)
    {
      LItemArray = LItemArray[LSelectedIndexes[i]].items;
    }
  }
   
  LSelectedIndex = LSelectedIndexes[PIndex];

  // Get the next combobox control
  LComboBoxId = GenerateDrillDownComboId(PBaseComboId, PIndex + 1);
  LComboBoxControl = GetElementById(LComboBoxId);
  
  // Determine whether the selected item is a leaf value and leaf only is enabled
  LIsLeaf = LLeafOnly && ((LSelectedIndex > -1) && (LItemArray[LSelectedIndex].items.length == 0));
   
  // If this is the last combo box then set button access and hidden field values and return
  if (LComboBoxControl == null)
  {    
    if (LMultiSelect)
    {
      if (LLeafOnly) 
        LAddButtonEnabled = LIsLeaf;
      else
        LAddButtonEnabled = (LBaseComboBoxControl.selectedIndex > 0);
      
      SetAddRemoveButtonAccess(PBaseComboId, LAddButtonEnabled, null);
    }
    
  UpdateDrillDownKnownLeaf(LLeafOnly, LIsLeaf, PBaseComboId, PIndex);
    
    return;
  }
      
  // Clear the controls options and its child combo boxes options
  if(LClearDrillDown)
    ClearDrillDownCombo(LComboBoxControl);

  // Check the index is valid
  if (LSelectedIndex >= 0)
  {  
    // Populate the combo box using the items from the item array
    // [Category Linker Server] *** 23/05/2007: Pass in whether the drilldown combo should be cleared
    // PopulateCombo(LComboBoxId, LItemArray[LSelectedIndex].items, 0)
    PopulateCombo(LComboBoxId, LItemArray[LSelectedIndex].items, 0, LClearDrillDown)

  }
  else
  {
    LComboBoxControl.disabled = true;
    LComboBoxControl.readOnly = true;
    
    // [REQ00422] +++ RP 05/01/06: Change the colour of the combo box to indicate that it has been disabled
    SetAttributetValue(CLASS_ATTRIBUTE_NAME, LComboBoxControl, "FormPullDownDisabled")
    // [REQ00442] +++ RP 05/01/06: <END>
  }

  if (LMultiSelect)
  {  
    if (LLeafOnly) 
      LAddButtonEnabled = LIsLeaf;
    else
      LAddButtonEnabled = (LBaseComboBoxControl.selectedIndex > 0);
      
    SetAddRemoveButtonAccess(PBaseComboId, LAddButtonEnabled, null);
  }
    
  UpdateDrillDownKnownLeaf(LLeafOnly, LIsLeaf, PBaseComboId, PIndex);

}

// Routine to update a drill down control.
function UpdateDrillDownKnownLeaf(PFieldLeafOnly, PValueIsLeaf, PBaseComboId, PIndex)
{
    if (PFieldLeafOnly)
    {
      if (PValueIsLeaf)
        UpdateHiddenControlValue(PBaseComboId, PIndex);
      // [Category Linker Server Enhancements] +++ 22/05/2007: Clear the hidden control value if not at the leaf
      else
      ClearHiddenControlValue(PBaseComboId);
      // [Category Linker Server Enhancements] +++ 22/05/2007: <END>
    }
    else
    {
      UpdateHiddenControlValue(PBaseComboId, PIndex);
    }
}

// Routine to update a drill down control.
function UpdateDrillDown(PBaseComboId, PIndex)
{   
    var USER_FIELD_TYPE = "13"
  
    // Get the base combo box control and check it is valid
    var LBaseComboBoxControl = GetElementById(PBaseComboId);
    if (LBaseComboBoxControl == null) return;
  
    var LFieldLeafOnly = (GetAttributetValue("cpleafonly", LBaseComboBoxControl, LBaseComboBoxControl.cpleafonly) != null);  
    var LIsUserField = (GetAttributetValue("cpdatatype", LBaseComboBoxControl, LBaseComboBoxControl.cpdatatype) == USER_FIELD_TYPE);
        
    // User fields should always be treated as leaf only fields.
    if (LIsUserField)
    {
        LFieldLeafOnly = true
    }
    
    // Get the array associated with this combobox
    var LItemArrayName = GetAttributetValue("cpitemarrayname", LBaseComboBoxControl, LBaseComboBoxControl.cpitemarrayname);
    var LItemArray = eval(LItemArrayName);
    
    // Get the selected indexes of the combo boxes.
    var LSelectedIndexes = new Array;
    var LComboBoxId;
    var LComboBoxControl;
    for (var i = 0; i <= PIndex; i++)
    {
      // Get the combobox control
      LComboBoxId = GenerateDrillDownComboId(PBaseComboId, i);
      LComboBoxControl = GetElementById(LComboBoxId);
      if (LComboBoxControl == null) return;
      
      // Save the selected index, -1 to account for empty option
      LSelectedIndexes[i] = LComboBoxControl.selectedIndex - DRILL_DOWN_COMBO_DESELECTION_ITEM;
    }    
  
    // Set LItemArray to point to the correct level within the allocation array item hierarchy
    for (var i = 0; i < PIndex; i++)
    {
      if(LSelectedIndexes[i] > -1)
      {
        LItemArray = LItemArray[LSelectedIndexes[i]].items;
      }
    }
     
    LSelectedIndex = LSelectedIndexes[PIndex];
    
    // Determine whether the selected item is a leaf value and leaf only is enabled
    LValueIsLeaf = LFieldLeafOnly && ((LSelectedIndex > -1) && (LItemArray[LSelectedIndex].items.length == 0));
    
    UpdateDrillDownKnownLeaf(LFieldLeafOnly, LValueIsLeaf, PBaseComboId, PIndex);
}

// This routine sets the Add and Remove button access properites
function SetAddRemoveButtonAccess(PBaseComboId, PAddButtonEnabled, PRemoveButtonEnabled)
{
  // PBaseComboId                           I: The ID of the base combo
  // PAddButtonEnabled                      I: Whether the Add button is enabled
  // PRemoveButtonEnabled                   I: Whether the Remove button is enabled
  
  var LAddButton;
  var LRemoveButton;
  
  if (PBaseComboId == null) return;
  
  // Set the Add button access properties
  LAddButton = GetElementById(PBaseComboId + DRILL_DOWN_COMBO_ADD_BUTTON);
  if ((LAddButton != null) && (PAddButtonEnabled != null))   
    LAddButton.disabled = !PAddButtonEnabled;

  // Set the Remove button access properties
  LRemoveButton = GetElementById(PBaseComboId + DRILL_DOWN_COMBO_REMOVE_BUTTON);
  if (LRemoveButton != null && PRemoveButtonEnabled != null)
    LRemoveButton.disabled = !PRemoveButtonEnabled;
}

// This routine is called when the user adds or removes an item to/from the list box.
function PerformListClick(PBaseComboId)
{
  // PBaseComboId                           I: The ID of the base combo
  
  var LSelectedListControl;
  var LRemoveButtonEnabled;
  var LOption;
  var LBaseCombo;
  var LSelectedListId;
  
  if (PBaseComboId == null) return;
  
  LBaseCombo = GetElementById(PBaseComboId);
  LSelectedListId = GetAttributetValue("cpdrilldownselectedlistid", LBaseCombo, LBaseCombo.cpdrilldownselectedlistid);
  
  // Get the selected list control
  LSelectedListControl = GetElementById(LSelectedListId);
  if (LSelectedListControl == null) return;
  
  // Set the Remove buttons access properties
  LRemoveButtonEnabled = (LSelectedListControl.selectedIndex >= 0);
  SetAddRemoveButtonAccess(PBaseComboId, null, LRemoveButtonEnabled);
}

// This routine updates the hidden control with the value from a combo
function UpdateHiddenControlValue(PBaseComboId, PIndex)
{
  // PBaseComboId                           I: The ID of the base combo
  // PIndex                                 I: The level of the selected combo

  var LBaseComboBoxControl;
  var LHiddenControlId;
  var LHiddenControl;
  var LSelectedItem;
  var LNextComboId;
  var LNextComboControl;
  var LSelectedComboId;
  var LSelectedComboControl;
  var LHiddenValue = "";
  
  if (PBaseComboId == null || PIndex == null) return;
  
  // Get the base combo control
  LBaseComboBoxControl = GetElementById(PBaseComboId);
  if (LBaseComboBoxControl == null) return;
  
  // If the selected index of the leaf combo is the empty value and leaf only
  // attribute is set then return
  LSelectedComboId = GenerateDrillDownComboId(PBaseComboId, PIndex);
  LSelectedComboControl = GetElementById(LSelectedComboId);
  if (LSelectedComboControl == null) return;
  
  // Get the hidden control
  LHiddenControlId = GetAttributetValue("cphiddencontrolid", LBaseComboBoxControl, LBaseComboBoxControl.cphiddencontrolid); 
  LHiddenControl = GetElementById(LHiddenControlId);
  if (LHiddenControl == null) return;
  
  LSelectedItem = new Item("", "");
  
  // Set the hidden value
  GetSelectedItem(PBaseComboId, LSelectedItem, DISPLAY_TYPE_LEAF_ONLY);
  if (LSelectedItem != null)
    LHiddenValue = LSelectedItem.value;
    
  LHiddenControl.value = ReplaceHTMLSpecialCharacters(LHiddenValue);
}

// This routine clears the hidden control with a blank value
function ClearHiddenControlValue(PBaseComboId)
{
  // PBaseComboId                           I: The ID of the base combo

  var LBaseComboBoxControl;
  var LHiddenControlId;
  var LHiddenControl;
  var LSelectedItem;
  var LNextComboId;
  var LNextComboControl;
  var LSelectedComboId;
  var LSelectedComboControl;
  var LHiddenValue = "";
  
  if (PBaseComboId == null) return;
  
  // Get the base combo control
  LBaseComboBoxControl = GetElementById(PBaseComboId);
  if (LBaseComboBoxControl == null) return;
    
  // Get the hidden control
  LHiddenControlId = GetAttributetValue("cphiddencontrolid", LBaseComboBoxControl, LBaseComboBoxControl.cphiddencontrolid); 
  LHiddenControl = GetElementById(LHiddenControlId);
  if (LHiddenControl == null) return;
  
  LSelectedItem = new Item("", "");
      
  LHiddenControl.value = LHiddenValue;
}

// This routine clears the given combo box and all its children of any options
function ClearDrillDownCombo(PCombo)
{
  // PCombo                 I: The combo box control to clear
  
  var LNextComboBoxControl;
  
  if (PCombo == null) return;
  
  // Remove the options from this combobox
  for (var i = PCombo.options.length - 1; i >= 0 ; i--)
    PCombo.options[i] = null;

  // Get the next combobox, and check it is valid
  LNextComboBoxControl = GetElementById(PCombo.name + DRILL_DOWN_COMBO_NEXT_IDENTIFIER);
  if (LNextComboBoxControl == null) return;
  
  // [REQ00442] +++ RP 05/01/06: Change the colour of the combo box when it is disabled
  SetAttributetValue(CLASS_ATTRIBUTE_NAME, LNextComboBoxControl, "FormPullDownDisabled")
  // [REQ00442] +++ RP 05/01/06: <END>

  // If the next combobox is valid then remove its options
  ClearDrillDownCombo(LNextComboBoxControl);
  
  // Disable the combobox
  LNextComboBoxControl.disabled = true;  
  LNextComboBoxControl.readOnly = true;
}

// This routine adds the selected item from the combo boxes to the selection list
function AddSelectedItem(PBaseComboId)
{
  // PBaseComboId                   I: The base combo box ID

  var LSelectedItem;
  var LSelectionListControl;
  var LOption;
  var LBaseCombo;
  var LSelectedListId;
  
  var LSelectedItemText;
  var LDisplayStyle;
  
  if (PBaseComboId == null) return;
  
  LBaseCombo = GetElementById(PBaseComboId);
  if (LBaseCombo == null) return;
    
  LSelectedListId = GetAttributetValue("cpdrilldownselectedlistid", LBaseCombo, LBaseCombo.cpdrilldownselectedlistid);
  LDisplayStyle = GetAttributetValue("cpcategorydisplaytype", LBaseCombo, LBaseCombo.cpcategorydisplaytype);
  
  // Get the selected item
  LSelectedItem = new Item("", "");
  
  GetSelectedItem(PBaseComboId, LSelectedItem, LDisplayStyle);
  if ((LSelectedItem == null) || ((LSelectedItem.text.length == 0) && (LSelectedItem.value.length == 0))) 
    return;
  
  // Get the selection list control
  LSelectionListControl = GetElementById(LSelectedListId);
  if (LSelectionListControl == null)
    return;
  
  LOption = new Option(LSelectedItem.text, LSelectedItem.value);
  
  // If the option already exists then return
  if (OptionExists(LSelectionListControl, LOption)) return;
  
  // Add the new option to the list
  LSelectionListControl.options[LSelectionListControl.length] = LOption;
  
  // Select the newly added option
  LSelectionListControl.selectedIndex = LSelectionListControl.length - 1;
  
  // Ensure the buttons access properties are set
  PerformListClick(PBaseComboId);
  
  // Clear the drill down combo boxes
  LBaseCombo.selectedIndex = 0
  PerformDrillDown(PBaseComboId, 0);
  
  // Sort the option alphabetically
  SortSelectionListOptions(LSelectionListControl);
}

// This routine sorts a selection lists option by their text value
function SortSelectionListOptions(PSelectList) 
{
	var sortArray = new Array();
	
	if (PSelectList == null) return;
	
	// Add all the options into an array
	for (var i = 0; i < PSelectList.options.length; i++) 
  {
		sortArray[sortArray.length] = new Option(PSelectList.options[i].text, PSelectList.options[i].value, PSelectList.options[i].defaultSelected, PSelectList.options[i].selected);
	}
	
	// Sort the options array 
	if (sortArray.length == 0) { return; }
  sortArray = sortArray.sort(SortOptions);

  // Add the options back into the selection list
	for (var i = 0; i < sortArray.length; i++) 
  {
		PSelectList.options[i] = new Option(sortArray[i].text, sortArray[i].value, sortArray[i].defaultSelected, sortArray[i].selected);
	}
}

// This function is used by the Array.sort function to sort options by text value
function SortOptions(LOptionA, LOptionB) 
{ 
	if ((LOptionA.text + "") < (LOptionB.text + "")) { return -1; }
	if ((LOptionA.text + "") > (LOptionB.text + "")) { return 1; }
	return 0;
} 

// This function returns whether a specific option exists within a given list box
function OptionExists(PSelectionListControl, POption)
{
  // PSelectionListControl                I: The list box object to search for option in
  // POption                              I: The option to search for

  var LOption;
  var LDecodedListValue;
  var LDecodedOptionValue;
  
  if (PSelectionListControl == null || POption == null) return;

  // Iterate through the options array 
  for (var i = 0; i < PSelectionListControl.length; i++)
  {
    LOption = PSelectionListControl.options[i];
    
    // Replace all special HTML characters so comparison can be made
    LDecodedListValue = ReplaceHTMLSpecialCharacters(LOption.value);
    LDecodedOptionValue = ReplaceHTMLSpecialCharacters(POption.value);
    
    // If the value has been found then return true
    if (LDecodedOptionValue == LDecodedListValue)
      return true;
  }
  
  // The value was not found so return false
  return false;
}

// This routine removes any selected options from a multiselect list box
function RemoveSelectedItem(PBaseComboId)
{
  // PBaseComboId                     I: The base combo ID, from which to generate the list box ID

  var LSelectionListControl;
  var LSelectedIndex;
  var LOption;
  var LBaseCombo;
  var LSelectedListId;
  
  if (PBaseComboId == null) return;
  
  LBaseCombo = GetElementById(PBaseComboId);
  if (LBaseCombo == null) return;
  
  LSelectedListId = GetAttributetValue("cpdrilldownselectedlistid", LBaseCombo, LBaseCombo.cpdrilldownselectedlistid);

  // Get the selection list control
  LSelectionListControl = GetElementById(LSelectedListId);
  if (LSelectionListControl == null) return;
  
  // Iterate through the options array 
  for (var i = LSelectionListControl.length; i >= 0; i--)
  {
    LOption = LSelectionListControl.options[i];
    if (LOption == null) continue;
    
    // If the item is selected then remove it from the list
    if (LOption.selected)
      LSelectionListControl.remove(i);
  }
  
  // Set the selected index to the first in the list
  if ( LSelectionListControl.length > 0 )
	  LSelectionListControl.selectedIndex = 0;
  
  // Ensure the buttons access properties are set
  PerformListClick(PBaseComboId);
}

// This routine validates a drill down combo box
function ValidateDrillDownComboBox(PElement, PDisplayName)
{
  // PElement                     I: The drill down combo box to validate
  // PDisplayName                 I: The controls display name
  // ValidateDrillDownComboBox    O: Whether the combo is valid

  var USER_FIELD_TYPE = "13"
  var LSelectedListId;
  var LMultiSelect;
  var LSelectedList;
  var LLeafOnly;
  var LHiddenControlId;
  var LHiddenControl;
  var LElementId;
  var LSelectedItem;
  var LHiddenControlValue;
  var LSelectedItemValue;
  var LErrorMessage;
  var LManadatory;
  var LOption;
  var LIsUserField;
  
  if (PElement == null) return false;
  
  LErrorMessage = null;
		
	LMultiSelect = (GetAttributetValue("cpmultiselect", PElement, PElement.cpmultiselect) != null);
  LLeafOnly = (GetAttributetValue("cpleafonly", PElement, PElement.cpleafonly) != null);  
  LElementId = GetAttributetValue("id", PElement, PElement.id);
  LManadatory = GetAttributetValue("cpmandatory", PElement, PElement.cpmandatory) == "0" ? false : true;
  LIsUserField = (GetAttributetValue("cpdatatype", PElement, PElement.cpdatatype) == USER_FIELD_TYPE);
    
  // Check single select drill down combo boxes
  if (!LMultiSelect)
  {
    // Get the hidden control 
    LHiddenControlId = GetAttributetValue("cphiddencontrolid", PElement, PElement.cphiddencontrolid); 
    LHiddenControl = GetElementById(LHiddenControlId);

    if (LHiddenControl != null)
    {            
      // Check if base combo value is set to empty if it is then we need to set the hidden control 
      // value to empty as well so the value is saved
      if (PElement.selectedIndex == 0)
      {
        if (LManadatory)
        {
      		LErrorMessage = MSG_MANDATORY_FIELD_NOT_POPULATED.replace(/\[FIELDNAME\]/g, PDisplayName);
      		DisplayErrorMessage(LErrorMessage);
          return false;
        }
        else
          LHiddenControl.value = "";  
      }
      
      // Check leaf only drill down combos
      if (LLeafOnly || LIsUserField)
      {    
        LSelectedItem = new Item("", "");
        
        // If the hidden control value does not equal the current drill down value then a leaf value is not selected
        // so can return false. This works beacause PerformDrillDown() will only update the hidden control if a leaf
        // value has been selected.
        GetSelectedItem(LElementId, LSelectedItem, DISPLAY_TYPE_LEAF_ONLY);
        
        // Replace special characters so we can make a comparison
        LHiddenControlValue = ReplaceHTMLSpecialCharacters(LHiddenControl.value);
        LSelectedItemValue = ReplaceHTMLSpecialCharacters(LSelectedItem.value);
        
        if (LHiddenControlValue != LSelectedItemValue)
        {
      		LErrorMessage = MSG_DRILL_DOWN_COMBO_LEAF_OPTION_NOT_SELECTED.replace(/\[FIELDNAME\]/g, PDisplayName);
      		DisplayErrorMessage(LErrorMessage);
          return false;
        }
      }
    } 
    
   	// If this is a single select combo box then return
    return true;   
  }     
  
  // Get the selected list control
  LSelectedListId = GetAttributetValue("cpdrilldownselectedlistid", PElement, PElement.cpdrilldownselectedlistid);
  LSelectedList = GetElementById(LSelectedListId);
  if (LSelectedList == null)
  {
		LErrorMessage = MSG_DRILL_DOWN_COMBO_LEAF_OPTION_NOT_SELECTED.replace(/\[FIELDNAME\]/g, PDisplayName);
		DisplayErrorMessage(LErrorMessage);
    return false;
  }
  
  // If the field is mandatory check that a value(s) has been selected
  if (LManadatory && (LSelectedList.length == 0))
  {
		LErrorMessage = MSG_MANDATORY_FIELD_NOT_POPULATED.replace(/\[FIELDNAME\]/g, PDisplayName);
		DisplayErrorMessage(LErrorMessage);
    return false;
  }
      
  // Iterate through the options and select them all
  for (var j = 0; j < LSelectedList.length; j++)
  {
    LOption = LSelectedList.options[j];
    if (LOption == null) continue;
      
    LOption.value = ReplaceHTMLSpecialCharacters(LOption.value);
    LOption.selected = true;
  }
  
  return true;
}

// This function returns a combo box ID based on the base combo ID and the level of the 
// combo wanted
function GenerateDrillDownComboId(PBaseComboId, PIndex)
{
  // PBaseComboId               I: The ID of the base combo box
  // PIndex                     I: The level of the combo to generate ID for
  
  var LComboId = PBaseComboId;
  
  if (PBaseComboId == null || PIndex == null) return;
  
  // Add PIndex number of "n"'s to the ID
  for (var i = 0; i < PIndex; i++)
    LComboId += DRILL_DOWN_COMBO_NEXT_IDENTIFIER;
  
  // Return to caller
  return LComboId;
}

// This function inserts any special characters with encoded values so that strings are 
// stored correctly
function InsertHTMLSpecialCharacters(PString)
{
  var LString = PString;
  
  if ((PString == "") || (PString == null)) return "";
  
  LString = LString.replace(/&/g, "&amp;");
  LString = LString.replace(/</g, "&lt;");
  LString = LString.replace(/>/g, "&gt;");
  LString = LString.replace(/"/g, "&quot;");  
  LString = LString.replace(/{/g, "&#123;");
  LString = LString.replace(/}/g, "&#125;");
  LString = LString.replace(/Ö/g, "&#214;");
  LString = LString.replace(/Ù/g, "&#217;");
  LString = LString.replace(/Ú/g, "&#218;");
  LString = LString.replace(/Û/g, "&#219;");
  LString = LString.replace(/Ü/g, "&#220;");
  LString = LString.replace(/¬/g, "&#172;");
  LString = LString.replace(/«/g, "&#171;");
  LString = LString.replace(/»/g, "&#187;");
  LString = LString.replace(/£/g, "&#163;");
  LString = LString.replace(/¤/g, "&#164;");
  LString = LString.replace(/¥/g, "&#165;");
  LString = LString.replace(/§/g, "&#167;");
  LString = LString.replace(/¼/g, "&#188;");
  LString = LString.replace(/½/g, "&#189;");
  LString = LString.replace(/¾/g, "&#190;");
  LString = LString.replace(/À/g, "&#192;");
  LString = LString.replace(/Á/g, "&#193;");
  LString = LString.replace(/Â/g, "&#194;");
  LString = LString.replace(/Ã/g, "&#195;");
  LString = LString.replace(/Ä/g, "&#196;");
  LString = LString.replace(/Å/g, "&#197;");
  LString = LString.replace(/Æ/g, "&#198;");
  LString = LString.replace(/Ç/g, "&#199;");
  LString = LString.replace(/È/g, "&#200;");
  LString = LString.replace(/É/g, "&#201;");
  LString = LString.replace(/Ê/g, "&#202;");
  LString = LString.replace(/Ë/g, "&#203;");
  LString = LString.replace(/Ì/g, "&#204;");
  LString = LString.replace(/Í/g, "&#205;");
  LString = LString.replace(/Î/g, "&#206;");
  LString = LString.replace(/Ï/g, "&#207;");
  LString = LString.replace(/Ð/g, "&#208;");
  LString = LString.replace(/Ñ/g, "&#209;");
  LString = LString.replace(/Ò/g, "&#210;");
  LString = LString.replace(/Ó/g, "&#211;");
  LString = LString.replace(/Ô/g, "&#212;");
  LString = LString.replace(/Õ/g, "&#213;");
     
  // Return to caller
  return LString;
}

// This function replaces any encoded values with special characters 
function ReplaceHTMLSpecialCharacters(PString)
{
  var LString = PString;
  
  if ((PString == "") || (PString == null)) return "";
  
  LString = LString.replace(/&amp;/g, "&");
  LString = LString.replace(/&lt;/g, "<");
  LString = LString.replace(/&gt;/g, ">");
  LString = LString.replace(/&quot;/g, "\"");  
  LString = LString.replace(/&#123;/g, "{");
  LString = LString.replace(/&#125;/g, "}");
  LString = LString.replace(/&#214;/g, "Ö");
  LString = LString.replace(/&#217;/g, "Ù");
  LString = LString.replace(/&#218;/g, "Ú");
  LString = LString.replace(/&#219;/g, "Û");
  LString = LString.replace(/&#220;/g, "Ü");
  LString = LString.replace(/&#172;/g, "¬");
  LString = LString.replace(/&#171;/g, "«");
  LString = LString.replace(/&#187;/g, "»");
  LString = LString.replace(/&#163;/g, "£");
  LString = LString.replace(/&#164;/g, "¤");
  LString = LString.replace(/&#165;/g, "¥");
  LString = LString.replace(/&#167;/g, "§");
  LString = LString.replace(/&#188;/g, "¼");
  LString = LString.replace(/&#189;/g, "½");
  LString = LString.replace(/&#190;/g, "¾");
  LString = LString.replace(/&#192;/g, "À");
  LString = LString.replace(/&#193;/g, "Á");
  LString = LString.replace(/&#194;/g, "Â");
  LString = LString.replace(/&#195;/g, "Ã");
  LString = LString.replace(/&#196;/g, "Ä");
  LString = LString.replace(/&#197;/g, "Å");
  LString = LString.replace(/&#198;/g, "Æ");
  LString = LString.replace(/&#199;/g, "Ç");
  LString = LString.replace(/&#200;/g, "È");
  LString = LString.replace(/&#201;/g, "É");
  LString = LString.replace(/&#202;/g, "Ê");
  LString = LString.replace(/&#203;/g, "Ë");
  LString = LString.replace(/&#204;/g, "Ì");
  LString = LString.replace(/&#205;/g, "Í");
  LString = LString.replace(/&#206;/g, "Î");
  LString = LString.replace(/&#207;/g, "Ï");
  LString = LString.replace(/&#208;/g, "Ð");
  LString = LString.replace(/&#209;/g, "Ñ");
  LString = LString.replace(/&#210;/g, "Ò");
  LString = LString.replace(/&#211;/g, "Ó");
  LString = LString.replace(/&#212;/g, "Ô");
  LString = LString.replace(/&#213;/g, "Õ");
     
  // Return to caller
  return LString;
}

//################ Function start new processes from control button presses ####################################
//## This function can be used to popup a new window that starts a new process passing the complaint reference number 
//## into the new process. The button should be a standard button with the following attributes:
//##
//##   <input type="button" 
//##       value="Notes" 
//##       name="" 
//##       onclick="javascript:StartProcess(this, document.frmNetPoint)" 
//##       cpstartprocess="ComplaintActivities" 
//##       cpnewwindowfeatures="menubar=yes,toolbar=yes,location=yes">
//##
//##
//##   note: for anchors <a> add the following into the href="javascript:void(0);"
//##              use <img> rather than <input type="image">
//####################################################################################################
function StartProcess(PButton, PForm)
{
	if (PButton == null) return;

	var LProcessToStart = GetAttributetValue("cpstartprocess", PButton, PButton.cpstartprocess);
	var REF_NUM_FIELD_ID= "FLD00012";
	var LFieldIncluded = false;

	// Get the search part of the URL, and remove the ?
	var Lurl = window.location.search;
	Lurl = Lurl.replace ( "?", "");

	// Start building the destination URL
	var targetHRef = window.location.protocol + "//" + window.location.host + window.location.pathname + "?";
	targetHRef += "process=" + LProcessToStart;

	// Iterate through the controls on the form looking for the complaints reference number
	for(var LCount = 0; LCount < PForm.elements.length; LCount++) 
	{
		var LControl = PForm.elements[LCount];
		var LControlName = GetAttributetValue("name", LControl, LControl.name);

		if (LControlName.indexOf(REF_NUM_FIELD_ID) > -1)
		{
			targetHRef += "&" + REF_NUM_FIELD_ID + "=" + GetAttributetValue("value", LControl, LControl.value);
			LFieldIncluded = true;
		}
	}


	// Iterate through the existing URL and extract the authentication, database and complaint reference number
	parameterArray = Lurl.split ("&");
	for (var i=0; i<parameterArray.length; i++)
	{
		if (parameterArray[i].indexOf(REF_NUM_FIELD_ID) > -1)
		{
			if (!LFieldIncluded)
				targetHRef += "&" + parameterArray[i];
		}

		if (parameterArray[i].indexOf("database") > -1)
		{
			targetHRef += "&" + parameterArray[i];
		}

		if (parameterArray[i].indexOf("auth") > -1)
		{
			targetHRef += "&" + parameterArray[i];
		}
	}

	var windowFeatures = GetAttributetValue("cpnewwindowfeatures", PButton, PButton.cpnewwindowfeatures);
	window.open( targetHRef, window.name, windowFeatures);

}

// This function will check whether the right button was pressed, if it was no action will be
// performed.
function DisableRightClick(buttonClicked)
{
    var errorMessage = "Right click functionality has been disabled.";
    if (navigator.appName == "Netscape" && buttonClicked.which == 3)
    { 
        alert(errorMessage);//Alert for display a message
        return false;
    }
     else if (navigator.appName =="Microsoft Internet Explorer" && event.button == 2)
    {
        alert(errorMessage);
        return false;
    }
}


// [REQ00471] ++ SR 10/01/2006: Include the phone call infrormation.
function padZeros(PValue, PNumberOfChars) 
{
	// PValue				I: The value to pad.
	// PNumberOfChars		I: Number of chars to pad to.
	
	// Get the value of the parameter
	var LValue = PValue.toString();
	
	// Get the length of the parameter
	var LLength = LValue.length;

	// Add the correct number of zeros to the field
	if (LLength < PNumberOfChars)
		for (var i = LLength; i < PNumberOfChars; i++)
			LValue = "0" + LValue;
	return LValue;
}

function YearAndMonthDate(PDateTime) 
{
	// PDateTime			I: The current date to parse.
	
	var LDateString;
	
	// Returns the date as a string value	
	if (Date.parse(PDateTime)) 
	{
		LDateString = padZeros(PDateTime.getDate(),2) + "/" + padZeros(PDateTime.getMonth()+1,2) + "/" + PDateTime.getFullYear().toString();
		return LDateString;
	} else
		return ("<Invalid Date>");
}

function HoursAndMinutes(PDateTime) 	
{	
	// PDateTime			I: The current date.
	
	// Returns the datetime as a string value
	if (Date.parse(PDateTime))
		return (new String (padZeros(PDateTime.getHours(),2) + ":" + padZeros(PDateTime.getMinutes(),2)));
	else
		return ("<Invalid Date>");
}

function dateTimeString(PDateTime) 
{
	// PDateTime			I: The current date.
	
	var LDateString;
	
	// Concatenates the date and time
	LDateString = YearAndMonthDate(PDateTime) + " " + HoursAndMinutes(PDateTime);
	return LDateString;
}

function IncludePhoneCallInformation(PNoteField) 
{
	//	PNoteField 			I: The note field object.
	
	var LNow = new Date();
	var LNoteText;	
	
	// if the check box cannot be found then return.
	if(document.all.chkIncomingCall == null)
	{
		return ("<Incoming calls check box not found>");
	}
	
	// If the field does not exists.
	if(PNoteField == null)
	{
		return ("<Note Field not found>");
	}
			
	// Add the details to the field
	if (document.all.chkIncomingCall.checked)
	{
		LNoteText = "Incoming Call: " + dateTimeString(LNow) + "\n" + PNoteField.value;
	}
	else
	{
		LNoteText = "Outgoing Call: " + dateTimeString(LNow) + "\n" + PNoteField.value;
	}
	
	PNoteField.value = LNoteText;

}
// [REQ00471] ++ SR 10/01/2006:

// [REQ00471] ++ SR 10/01/2006: Task type security - add done date check box and set to now on click.
function SetTaskDoneDate(PHiddenDoneDateField)
{
	//	PHiddenDoneDateField			I: The hidden done date field.
	
	if(PHiddenDoneDateField == null)
	{
		alert("The done date field can not be found on the page.");
		return;
	}
	
	if(document.all.chkDoneDate == null)
	{
		alert("The done date check box can not be found on the page.");
		return;
	}
	
	if(document.all.chkDoneDate.checked)
	{
		var LDateNow = new Date();
		PHiddenDoneDateField.value = dateTimeString(LDateNow);
	}
	else
	{
		PHiddenDoneDateField.value = "";		
	}
}
// [REQ00471] ++ SR 10/01/2006:

// [20007796] +++ RP 17/01/2006: Disable all buttons once the form has been submitted
function DisableNetpointButtons(PForm)
{
	var NETPOINT_BUTTON = "cmdNetPoint";

	// Iterate through the controls on the form looking for the complaints reference number
	for(var LCount = 0; LCount < PForm.elements.length; LCount++) 
	{
		var LControl = PForm.elements[LCount];
		var LControlName = GetAttributetValue("name", LControl, LControl.name);

		if (LControlName.indexOf(NETPOINT_BUTTON) > -1)
		{
			LControl.disabled = true;
		}
	}
}
// [20007796] +++ RP 17/01/2006: <END>

// [20007166] +++ 18/01/2006: Allow update of AllocatedToGroup control when the drill down
// AllocatedTo control is changed
function UpdateAllocatedToGroup(PAllocatedToControlId, PAllocatedToGroupControlId)
{
  // PAllocatedToControlId                    I: The AllocatedTo control ID
  // PAllocatedToGroupControlId               I: The AllocatedToGroup control ID

  var LAllocatedToControl;
  var LAllocatedToGroupControl;

  if ((PAllocatedToControlId == null) || (PAllocatedToGroupControlId == null)) 
    return;
  
  // Get the controls
  LAllocatedToControl = GetElementById(PAllocatedToControlId);
  if (LAllocatedToControl == null) 
    return;
  
  LAllocatedToGroupControl = GetElementById(PAllocatedToGroupControlId);
  if (LAllocatedToGroupControl == null) 
    return;
  
  // Set the selected index to match the AllocatedTo control index
  LAllocatedToGroupControl.selectedIndex = LAllocatedToControl.selectedIndex;
}
// [20007166] +++ 18/01/2006: END

