/**
 * Title: validation.js
 * Author: Helina Cheung
 * Date:  20-8-2002  
 */
var whitespace = " \t\n\r";
var decimalPointDelimiter = ".";
var defaultEmptyOK = true;

/**
 * Function: trim
 * Description: Trim the string value
 * Parameters: str - the string to trim
 * Returns: trimmed string
 */
function trim(str)  {
	if ( str == null || str.length == 0 )
		return str;
	while(''+str.charAt(str.length-1)==' ')
		str=str.substring(0,str.length-1);
	while(''+str.charAt(0)==' ')
		str=str.substring(1,str.length);
	return str;
}

/**
 * Function: trimInteger
 * Description: remove leading zero(s) & empty space(s) in integer string
 * Parameters: str - the string to trim
 * Returns: trimmed string
 */
function trimInteger(str)  {
	var sRetVal;
	sRetVal = trim(str);
	
	if ( sRetVal == null || sRetVal.length == 0 )
		return sRetVal;

	while(''+sRetVal.charAt(0)=='0' && (sRetVal.length != 1))
		sRetVal=sRetVal.substring(1,sRetVal.length);

	return sRetVal;
}

/**
 * Function: trimFloat
 * Description: remove leading zero(s) & empty space(s) in integer string
 * Parameters: str - the string to trim
 * Returns: trimmed string
 */
function trimFloat(str)  {
	var sRetVal;
	sRetVal = trim(str);
	
	if ( sRetVal == null || sRetVal.length == 0 )
		return sRetVal;

	while((''+sRetVal.charAt(0)=='0' && (sRetVal.length != 1)) && ( (sRetVal.length > 1) && ''+sRetVal.charAt(0)=='0' && ''+sRetVal.charAt(1)!='.' ))
		sRetVal=sRetVal.substring(1,sRetVal.length);

	return sRetVal;
}

/**
 * Function: isEmtpy
 * Description: Check if the string is empty
 * Parameters: str - the string to check
 * Returns: true if emtpy; false if non-empty
 */
function isEmpty(s) {
	return ((s == null) || (s.length == 0))
}

/**
 * Function: isWhitespace
 * Description: Check if the string is whitespace
 * Parameters: str - the string to check
 * Returns: true if whitespace; false if non whitespace
 */
function isWhitespace (s)
{   var i;
    
    if (isEmpty(s)) return true;
   
    for (i = 0; i < s.length; i++)   {   
        var c = s.charAt(i);
        if (whitespace.indexOf(c) == -1) return false;
    }

    return true;
}

/**
 * Function: isWhitespaceInside
 * Description: Check if the whitespace is include in string
 * Parameters: str - the string to check
 * Returns: true if whitespace; false if non whitespace
 */
function containWhitespace (s)
{   var i;
	i = 0;
    for (i = 0; i < s.length; i++)   {   
        var c = s.charAt(i);
        if ( c == ' ' )
        	return true;
	} 
    return false;
}

/**
 * Function: isInteger
 * Description: Check if the string is an integer
 * Parameters: str - the string to check
 * Returns: true if it's an integer; false if not an integer
 */
function isInteger(str)
{	
    if (isEmpty(str)) 
       if (isInteger.arguments.length == 1) return defaultEmptyOK;
       else return (isInteger.arguments[1] == true);
       
	if (str.indexOf('+') != -1 || str.indexOf('.') != -1 || isNaN(str)) return false;

	var nValue = parseInt(str, 10);
	if (nValue != parseFloat(str)) {
		return false;
	}

	return true;
}

/**
 * Function: isPostiveInteger
 * Description: Check if the string is a positive integer
 * Parameters: str - the string to check
 * Returns: true if it's a positive integer; false if not a positive integer
 */
function isPositiveInteger(str) 
{
    if (isEmpty(str)) 
       if (isPositiveInteger.arguments.length == 1) return defaultEmptyOK;
       else return (isPositiveInteger.arguments[1] == true);

	if (str.indexOf('+') != -1 || str.indexOf('.') != -1 || isNaN(str)) return false;	
	
	var nValue = parseInt(str, 10);
	if (nValue<0 || nValue != parseFloat(str)) 
		return false;
	
	return true;
} 

/**
 * Function: isIntegerInRange
 * Description: Check if the string is within the integer range
 * Parameters: 	s - the string to check
 *				a - the minimum integral value (inclusive)
 *				b - the maximumm integral value (inclusive)
 * Returns: 	true if a<=s<=b; false otherwise
 */
function isIntegerInRange (s, a, b)
{
   if (!isInteger(s)) return false;

    var num = parseInt (s);
    return ((num >= a) && (num <= b));
}

/**
 * Function: 	extractNumFormat
 * Description: Extract the number of decimal places allowed from the format specified
 * Parameters: 	str - specifying the number format in struture [number of decimal before].[number of decimal after]
 * Returns: 	String[2] array of the decimal place before and after
 */
function extractNumFormat(str) {
	var i = str.indexOf(".");
	var asFormat = [0, 0];
	if (i != -1) {    		
		asFormat[0] = str.substring(0, i);
		asFormat[1] = str.substring(i+1);
	} else
		asFormat[0] = parseInt(str);	
	return asFormat;
}

/**
 * Function:	isFloat
 * Description:	Check if the string is a float number. 
 *				Assuming the value has already removed the commas
 * Parameters:	s - the string to examine
 * Returns:		true if the string is a float number; false otherwise
 */
function isFloat (s)
{	
	var seenDecimalPoint = false;	
	var bHasDigit = false;
	
    if (isEmpty(s)) 
       if (isFloat.arguments.length == 1) return defaultEmptyOK;
       else return (isFloat.arguments[1] == true);
	
    if (s == decimalPointDelimiter) return false;
    
    for (var i = 0; i < s.length; i++)
    {   
        var c = s.charAt(i);		
        
		if (c == decimalPointDelimiter) {
        	if (seenDecimalPoint)  return false;
    		else seenDecimalPoint = true;    		
    	} else if (!isDigit(c)) {
    		return false;
    	} else {
    		bHasDigit = true;	
    	}    	
    } 
    if (bHasDigit)    return true;
    else 	return false;
}

/**
 * Function:	chkFloatDecimal
 * Description:	Check if the string is in correct decimal number format. 
 *				Assuming the value has already removed the commas.
 *				Popup alert message here.
 * Parameters:	s - the string to examine
 *				oField - the field object, for obtaining the format
 * Returns:		true if the string is in correct float number format; false otherwise
 */
function chkFloatDecimal(s, oField)
{
	if (typeof oField.format == 'undefined') {	// return true if format is not defined
		return true;
	}

	var decPoint=0;
	var seenDecimalPoint = false;
	var asFormat= extractNumFormat(oField.format);
	var bValid = true;
    for (var i = 0; i < s.length; i++)
    {   
        var c = s.charAt(i);		
		if (c == decimalPointDelimiter) {
        	decPoint = i;
        	seenDecimalPoint = true;	        
    	} else if (!seenDecimalPoint) {			
			if (i >= asFormat[0]) {
				// exceed the integral # of decimal places
        		bValid = false;        	
        		break;
        	}
        } else if ((i-decPoint) > asFormat[1]) { // exceed the # of decimal places after
        	bValid = false;
        	break;
        }        
    }
	if (!bValid) {
		alert("Field " + oField.desc + " should be a number with maximum of " + 
			asFormat[0] + " integral digits and " + asFormat[1] + 
			" decimal places.\nPlease enter again.");
    } else return true;
}

/**
 * Function:	isLetter
 * Description:	Check if the character is an alphabet 
 * Parameters:	c - the character
 * Returns:		true if the character is an alphabet; false otherwise
 */
function isLetter (c){
   return ( ((c >= "a") && (c <= "z")) || ((c >= "A") && (c <= "Z")) )
}

/**
 * Function:	isDigit
 * Description:	Check if character c is a digit 
 * Parameters:	c - the character
 * Returns:		true if character c is a digit; false otherwise
 */
function isDigit (c) {
   return ((c >= "0") && (c <= "9"))
}

/**
 * Function:	isLetterOrDigit
 * Description:	Check if character c is a letter or digit 
 * Parameters:	c - the character
 * Returns:		true if character c is a letter or digit; false otherwise
 */
function isLetterOrDigit (c)
{   return (isLetter(c) || isDigit(c))
}

/**
 * Function:	isAlphanumeric
 * Description:	Check if the string is alphanumeric
 * Parameters:	s - the string
 * Returns:		true if s contains only letters and digits; false otherwise
 */
function isAlphanumeric (s)
{
    if (isEmpty(s)) {
       if (isAlphanumeric.arguments.length == 1) return defaultEmptyOK;
       else return (isAlphanumeric.arguments[1] == true);
	}
	
	for (var i = 0; i < s.length; i++) {   
        var c = s.charAt(i);
        if (!isLetterOrDigit(c))     return false;
    }
    return true;
}

/**
 * Function:	isTextValid
 * Description:	Check if the string contains non-english language letters
 * Parameters:	s - the string
 * Returns:		false if s contains non-english language letters; true otherwise
 */
function isTextValid(s) {
	var x;
	
	for (var i=0; i<s.length; i++) {
		x = s.charCodeAt(i);
		if ( x< 10 || x> 126) return false;		
	}
	return true;
}

/**
 * Function:	isYear
 * Description:	Check if the string is a year
 * Parameters:	y - the string of 4 digit year
 * Returns:		true if y is a 4 digit year; false otherwise
 */
function isYear(y) {
	if (y.length != 4 || !isPositiveInteger(y, false) || parseInt(y, 10) < 1) 	{
		return false;
	}
	return true;
}

/**
 * Function:	isMonth
 * Description:	Check if the string is a month
 * Parameters:	m - the string of month
 * Returns:		true if m is a valid month; false otherwise
 */
function isMonth(m) {	
	if (!isPositiveInteger(m, false)) return false;		
	var nMonth = parseInt(m, 10);	
	if (nMonth < 1 || nMonth>12) return false; 
	return true;
}

/**
 * Function:	isDay
 * Description:	Check if the string is a day in a month
 * Parameters:	d - the string of day
 * Returns:		true if d is a valid day; false otherwise
 */
function isDay(d) {	
	if (!isPositiveInteger(d, false)) return false;
	var nDay = parseInt(d, 10);
	if (nDay > 0 && nDay <= 31) return true;
	else return false;
}

/**
 * Function:	compareNumeric
 * Description:	compare the 2 numbers and gives results. See return value.
 * Parameters:	i1 - the first nubmer to compare
 * 				i2 - the first nubmer to compare 
 * Returns:		>0 if i1>i2. 0 if i1=i2. <0 if i1<i2
 */
function compareNumeric(i1, i2) {
	return i1-i2;
}

var daysInMonth = new Array(12);
daysInMonth[1] = 31;
daysInMonth[2] = 29;   // must programmatically check this
daysInMonth[3] = 31;
daysInMonth[4] = 30;
daysInMonth[5] = 31;
daysInMonth[6] = 30;
daysInMonth[7] = 31;
daysInMonth[8] = 31;
daysInMonth[9] = 30;
daysInMonth[10] = 31;
daysInMonth[11] = 30;
daysInMonth[12] = 31;

/**
 * Function:	daysInFebruary
 * Description:	Get the days in february given a year
 * Parameters:	year - the year
 * Returns:		Number. 29 if leap year. 28 otherwise
 */
function daysInFebruary (year)
{   // February has 29 days in any year evenly divisible by four,
    // EXCEPT for centurial years which are not also divisible by 400.
    return (  ((year % 4 == 0) && ( (!(year % 100 == 0)) || (year % 400 == 0) ) ) ? 29 : 28 );
}

/**
 * Function:	isDate
 * Description:	check whether the date is a valid date
 * Parameters:	y - the year
 *				m - the month
 *				d - the day
 * Returns:		true if it's a valid date, otherwise false
 */
function isDate(y, m, d) 
{		   
	//check for numeric value
	if (!isYear(y) || !isMonth(m) || !isDay(d)) return false;
	
	var intYear = parseInt(y, 10);
    var intMonth = parseInt(m, 10);
    var intDay = parseInt(d, 10);
    
	if (intDay > daysInMonth[intMonth]) return false; 	
    if ((intMonth == 2) && (intDay > daysInFebruary(intYear))) return false;
    return true;	
}

/**
 * Function:	isEmail
 * Description:	check whether the string is a valid email. Only certain format checking.
 * Parameters:	s - the email
 * Returns:		true if it's a valid email format, otherwise false
 */
function isEmail (s)
{
	if (isEmpty(s)) 
       if (isEmail.arguments.length == 1) return defaultEmptyOK;
       else return (isEmail.arguments[1] == true);
   
    if (isWhitespace(s)) return false;

	if (containWhitespace(s)) return false;
  
    // there must be >= 1 character before @, so we
    // start looking at character position 1 
    // (i.e. second character)
    var i = 1;
    var sLength = s.length;

	var sSpecChar = "\"'<>!#$%^&,()";
	for (var j=0; j<s.length; j++) {
		if (sSpecChar.indexOf(s.charAt(j)) != -1) return false;			
	}

    // look for @
    while ((i < sLength) && (s.charAt(i) != "@"))   { i++; }

    if ((i >= sLength) || (s.charAt(i) != "@")) return false;
    else i += 1;

    // look for .
    while ((i < sLength) && (s.charAt(i) != ".") && (s.charAt(i) != "@")) 	{ i++;  }
    
    if (i<sLength && s.charAt(i) == "@") return false;

    // there must be at least one character after the .
    if ((i >= sLength - 1) || (s.charAt(i) != ".")) return false;
    else {
    	if (s.indexOf("@", i) != -1) return false;
    	return true;
    }
}

/**
 * Function:	compareDates
 * Description:	compare the two dates field object & gives the result
 *				If any of the field is empty, return 1.
 *				Popup alert message if -1.
 * Parameters:	oForm - the form object
 *				oField1 - the from date field
 *				oField2 - the to date field
 * Returns:		-1 if oField1 date is greater than oField2 date
 *				0 if oField 1 date is equal to oField2 date
 *				1 if oField 1 date is less than oField2 date
 *					or any one of the field is empty
 */
function compareDates(oForm, oField1, oField2) {
	var asDateComponent = ["Y", "M", "D"];	
	var sVal1, sVal2;
	
	for (var i=0; i<asDateComponent.length; i++) {
		sVal1 = oForm.elements[oField1.field + asDateComponent[i]].value;
		sVal2 = oForm.elements[oField2.field + asDateComponent[i]].value;
		
		if (sVal1 != "" && sVal2 != "") {
			if (parseInt(sVal1, 10) > parseInt(sVal2, 10)) {
				alert("The " + oField2.desc + " should not be earlier than the " + oField1.desc + ".\nPlease enter again.");
				oForm.elements[oField2.field + asDateComponent[i]].focus();				
				return -1;
			} else if (parseInt(sVal2, 10) > parseInt(sVal1, 10)) {
				return 1;
			}	
		} else return 1;	// if some fields are not filled in, default to be bigger than from		
	}
	return 0;
}

/**
 * Function:	validTextArea
 * Description:	Check if the text area value is according to specified format
 * Parameters:	sValue - the text araa string value
 *				nCol - the number of columns
 *				nRow - the number of rows
 * Returns:		true if the value is within format
 *				false if the number of rows is greater than the specified row or the 
 *				total number of characters is above limit (row*col)
 */
function validTextArea(sValue, nCol, nRow) {
	var nPos = 0;
	var nCount = 0;
	var nPrev = 0;
	
	do {
		nPos = sValue.indexOf('\n', nPos);
		
		if (nPos > -1) {
//			if (nPos-nPrev > nCol+1)
//				return false;
			nCount++; nPos++;
		}		
		nPrev = nPos;
	} while (nPos > -1 && nCount <= nRow)
	
//	if ((sValue.length - (nCount * 2)) > (nCol * nRow)) {
//		return false;
//	}	
	if (nCount >= nRow) return false;	

	return true;
}

var TYPE_TEXT = 0;
var TYPE_INT = 1;
var TYPE_FLOAT = 2;
var TYPE_DATE = 3;
var TYPE_SELECT = 4;
var TYPE_EMAIL = 5;
var TYPE_TA = 6;

/**
 * Function:	formField
 * Description:	Construct the formField object - for SF common validation
 *				For unncessary parameters, ignore the parameter or pass null
 * Parameters:	sField - the field id
 *				sDesc - the field description, used for error message
 *				bMandatory - boolean. Field is mandatory or not
 *				min - Integer. the minumum value of the field/number of rows for text area
 *				max - Integer. the maximum value of the field/number of columns for text area
 *				form - the format of numeric types, number of decimal places before and after, in string, e.g., "10.2"
 *				func - the optional function name to execute
 *				bMultiple - boolean. Field is multiple (indexed) or not. In HTML control, would have index suffix.
 *				lowIndex - the start index for the field index (applicable when bMultiple is true)
 *				upIndex - the end index for the field index (applicable when bMultiple is true)
 */
function formField(sField, sDesc, bMandatory, nType, min, max, form, func, bMultiple, lowIndex, upIndex) {	
	this.field = sField;
	this.desc = sDesc;
	this.mandatory= bMandatory;	
	this.dtype = TYPE_TEXT;
	this.dtype = nType;
	this.min = min;
	this.max = max;	
	this.format = form;
	this.func = func;
	this.multiple = bMultiple;
	this.lowIndex = lowIndex;
	this.upIndex = upIndex;
}

/**
 * Function:	checkLanguage
 * Description:	Check if the form field values does not contain other language's character
 * Parameters:	oform - the form field 
 *				asFields - the field array
 * Returns:		true if all the form fields are valid
 *				false if the any one of the field is invalid.
 */
function checkLanguage(oform) {
	// Check for non-english characters
	for (var i=0; i<oform.elements.length; i++) {
		if (oform.elements[i].type == "text" || 
			oform.elements[i].type == "textarea" ||
			oform.elements[i].type == "file" ) {
			if (!isTextValid(oform.elements[i].value)) { //disallowed character set
				alert("Please enter letters in English only.");
				oform.elements[i].focus();
				return false;
			}
		} 
	}
	return true;
}

/**
 * Function:	checkAllFields
 * Description:	Check if the form field is valid according to validation rules.
 *				If invalid field found, gives error message, focus at the error field
 *				and returns false immediately
 * Parameters:	oform - the form field 
 *				asFields - the field array
 * Returns:		true if all the form fields are valid
 *				false if the any one of the field is invalid.
 */
function checkAllFields(oform, asFields) {	
	var oField;
	var sValue;
	
	if (!checkLanguage(oform)) return;
	
	// Check for all list field in formField arrays
	for (var i=0; i<asFields.length; i++) {
		oField = asFields[i];		
		
		// Date type
		if (oField.dtype == TYPE_DATE) {
			if (oField.multiple) {
				for (var j=oField.lowIndex; j<=oField.upIndex; j++) {
					if (!checkDate(oform, oField, j))
						return false;
				}
			} else if (!checkDate(oform, oField, ""))
				return false;
			
		}
		// Mandatory selection list
		else if (oField.dtype == TYPE_SELECT ) {
			var oSelect = oform.elements[oField.field];
			
			if (oField.mandatory && oSelect.options[oSelect.selectedIndex].value == "") {
				alert("Please select a value for " + oField.desc + "."); 
				oSelect.focus();
				return false;	
			}		
		} 		
		else {
			if (oField.multiple == null || !oField.multiple ) { // single field handling				
				if (!checkValue(oform, oField, oField.field)) return false;				
			} else { //indexed field handling
				for (var j=oField.lowIndex; j<=oField.upIndex; j++) {
					if (!checkValue(oform, oField, oField.field + j)) return false;
				}
			}
		}

		if (oField.func != null && oField.func != "" && !eval(oField.func + "('" 
				+ trim(oform.elements[oField.field].value) + "', '" + oField.desc + "')")) {
			//alert("Invalid field value for field " + oField.desc + ".");
			oform.elements[oField.field].focus();
			return false;
		}
	}

	return true;	// Validation passed
}

/**
 * Function:	checkDate
 * Description:	Check if the date form field is valid
 * Parameters:	oform - the form object
 *				oField - the JS formField object
 *				index - the index for multiple field. If single field, empty string
 * Returns:		true if the date field is valid; false otherwise
 */
function checkDate(oform, oField, index) {
	sYear = trim(oform.elements[oField.field+ "Y" + index].value);
	sMonth = trim(oform.elements[oField.field+ "M" + index].value);
	sDay = trim(oform.elements[oField.field+ "D" + index].value);
		
	if ((sYear.length + sMonth.length + sDay.length > 0) 
			&& !isDate(sYear, sMonth, sDay)) {
		// Optional field but one or more of the component present
		alert("Field " + oField.desc + " is not a valid date.\nPlease enter again.");
		oform.elements[oField.field + "Y" + index].focus();
		return false;
	}	
	
	if (oField.mandatory) {
		var sMissing = (sYear.length==0)?"Y":((sMonth.length==0)?"M":((sDay.length==0)?"D":""));
		if (sMissing != "")  {
			alert("Field " + oField.desc + " is mandatory.\nPlease enter the value.");
			oform.elements[oField.field + sMissing + index].focus();
			return false;		
		}
	}
	
	if ( (sMonth.length == 1) ) {
		oform.elements[oField.field+ "M" + index].value = "0" + sMonth;
	}
	if ( (sDay.length == 1) ) {
		oform.elements[oField.field+ "D" + index].value = "0" + sDay;
	}
	
	return true;
}

/**
 * Function:	checkValue
 * Description:	Check if the field value is valid in accordance to validation rule
 * Parameters:	oform - the form object
 *				oField - the JS formField object
 *				sName - the exact field ID. This is specially cater for multiple field id
 * Returns:		true if the field is valid; false otherwise
 */
function checkValue(oform, oField, sName) {
	var sValue = trim(oform.elements[sName].value) + "";
	if (oField.mandatory && trim(sValue).length == 0)  {
		alert("Field " + oField.desc + " is mandatory.\nPlease enter the value.");
		oform.elements[sName].focus();
		return false;
	}
	
	switch (oField.dtype) {
		case 0:
		//case TYPE_TEXT:
		if ((sValue.length < oField.min || sValue.length > oField.max)) {
				alert("Value of field " + oField.desc + " is too long or too short.\nPlease enter again.");
				oform.elements[sName].focus();
				return false;
			}
			break;

		case 1:
		//case TYPE_INT:
			oform.elements[sName].value = trimInteger(oform.elements[sName].value);
			if (!isInteger(sValue)) {
				alert("Field " + oField.desc + " should be an integer.\nPlease enter again.");
				oform.elements[sName].focus();
				return false;
			}
			if (oField.min != null && compareNumeric(parseInt(sValue, 10), oField.min) < 0) {
				if (oField.min == 0) {
					alert("Field " + oField.desc + " must be an integer not less than " + oField.min + ".");
				} else {
					alert("Field " + oField.desc + " must be an integer greater than " + (oField.min-1) + ".");
				}
				oform.elements[sName].focus();
				return false;
			}
			if (oField.max != null && compareNumeric(parseInt(sValue, 10), oField.max) > 0) {
				alert("Field " + oField.desc + " must be an integer not greater than " + oField.max + ".");
				oform.elements[sName].focus();
				return false;
			}			
			break;			
		case 2:
			//case TYPE_FLOAT:
			oform.elements[sName].value = trimFloat(oform.elements[sName].value);	

			var pattern = /\d/g;
			// SIT Bugfix (Ref=5866)
			if (sValue.length > 0 && !pattern.test(sValue) && !isFloat(sValue) && !isInteger(sValue)) {
				alert("Field " + oField.desc + " should be a number.\nPlease enter again.");
				oform.elements[sName].focus();
				return false;
			}
			
			pattern = /,+/g;
			sValue = trim(sValue.replace(pattern, ""));
			if (sValue.length != 0 && !isFloat(sValue) && !isInteger(sValue)) {
				alert("Field " + oField.desc + " should be a number.\nPlease enter again.");
				oform.elements[sName].focus();
				return false;
			}
			if (oField.min != null && compareNumeric(parseFloat(sValue), oField.min) < 0) {
				alert("Field " + oField.desc + " must not be less than " + oField.min + ".");
				oform.elements[sName].focus();
				return false;
			}
			if (oField.max != null && compareNumeric(parseFloat(sValue), oField.max) > 0) {
				alert("Field " + oField.desc + " must not be greater than " + oField.max + ".");
				oform.elements[sName].focus();
				return false;
			}
			if (!chkFloatDecimal(sValue, oField)) {
				oform.elements[sName].focus();
				return false;
			}
			break;
		case 5:
			//case TYPE_EMAIL:					
			if (!isEmail(sValue)) {
				alert("Field " + oField.desc + " should be a valid e-mail address.\nPlease enter again.");
				oform.elements[sName].focus();
				return false;
			}
			break;
		case 6:
			//case TYPE_TA:
			if (!validTextArea(sValue, oField.min, oField.max)) {	// check the max. no. of rows
//				alert("Field " + oField.desc + " only can contain maximum of " + oField.max + 
//						" rows by " + oField.min + " characters only.\nPlease enter again.");
				alert("Field " + oField.desc + " only can contain maximum of " + oField.max + 
						" rows only.\nPlease enter again.");
				oform.elements[sName].focus();
				return false;						
			}
	}	
	return true;
}

/**
 * Function:	dateDiff
 * Description:	Find the number of days in between 2 given dates.
 * Parameters:	oform - the form object
 *				oFld1 - the 1st JS formField object of type DATE
 *				oFld2 - the 2nd JS formField object of type DATE
 * Returns:		no of days in between oFld1 & oFld2
 */
function dateDiff(oForm, oFld1, oFld2) {
	var oDate1, oDate2;
	oDate1 = new Date(oForm.elements[oFld1.field + "Y"].value + "/" + oForm.elements[oFld1.field + "M"].value + "/" + oForm.elements[oFld1.field + "D"].value);
	oDate2 = new Date(oForm.elements[oFld2.field + "Y"].value + "/" + oForm.elements[oFld2.field + "M"].value + "/" + oForm.elements[oFld2.field + "D"].value);

	diffMilli = oDate2.getTime()-oDate1.getTime();
	return Math.round(diffMilli/(1000*60*60*24));
}

/**
 * Function:	validateString
 * Description:	do not allow special characters in an input string
 * Parameters:	fieldname - field description,
 				str - string to check,
 				forbiddenChars - forbidden characters
 * Returns:		whether or not input string is valid
 */
function validateString(fieldname, str, forbiddenChars) {
	if (forbiddenChars == null) {
		forbiddenChars = "%_.[]-*^$";
	}

	for (i=0;i<str.length;i++) {
		ch = str.charAt(i);
		if (forbiddenChars.indexOf(ch) != -1) {
			alert(fieldname + " cannot contain invalid characters " + forbiddenChars + ". Please enter again.");
			return false;
		}
	}
	return true;
}