/********************
* Validation Processing methods
*********************/
var numErrors,errorMsg;
function validate(stepNum) {
	numErrors=0; //initialisation
	errorMsg = "Please ensure that fields marked * are filled \nPlease enter the following\n-------------------------------------------------";
	eval("validateStep"+stepNum+"()"); //this will determine numErrors
	if (numErrors>0) {
		alert(errorMsg);
		return false; //form validation failed
	}
	else return true; //form validated ok
}
function addError(name) {
	numErrors++;
	errorMsg=errorMsg+"\n"+numErrors+". "+name;
}


var frmEl;
function getFrmEl(gFrmName,fldName) {
	frmEl= new Object(eval("document."+gFrmName+"."+fldName));
}

/********************
* Population methods
*********************/
//populates TF with 'value'
function setTFValue(gFrmName,fldName,value) {
	getFrmEl(gFrmName,fldName);
	frmEl.value=unescape(value);
}
//populates CB group with values from 'ary'
function setCBValue(gFrmName,fldName,value) {
	if (value.length==0) return; //if no values were recorded for this CB group
	if (typeof(value)=='string') ary = value.split("%2C"); //split by %2C, the escaped code for commas
	else ary = value;
	getFrmEl(gFrmName,fldName);
	resetCBRBValue(gFrmName,fldName);
	if (!frmEl.length) { //this is a single CB, only one value was recorded
		if (frmEl.value==unescape(ary[0])) frmEl.checked=true;
	}
	else { //group of CBs
		for (var j=0;j<ary.length;j++) {	
			for (var i=0;i<frmEl.length;i++) {
				if (frmEl[i].value==unescape(ary[j])) frmEl[i].checked=true;
			}
		}
	}
}
//selects RB group with value 'value'
function setRBValue(gFrmName,fldName,value) {
	if (value=='') return; //if no value was recorded for this RB
	getFrmEl(gFrmName,fldName);
	for (var i=0;i<frmEl.length;i++) {
		if (frmEl[i].value==unescape(value)) {
			frmEl[i].checked=true;
			return;
		}
	}
}
function resetCBRBValue(gFrmName,fldName) {
	getFrmEl(gFrmName,fldName);
	for (var i=0;i<frmEl.length;i++) {
		frmEl[i].checked=false;
	}
}
//set DL to item with value 'value'
function setDLValue(gFrmName,fldName,value) {
	if (value=='') return; //if no value was recorded for this DL
	getFrmEl(gFrmName,fldName);
	for (var i=0;i<frmEl.length;i++) {
		if (frmEl[i].value==unescape(value)) {
			frmEl[i].selected=true;
			return;
		}
	}
}



/********************
* Validation/Retrieval methods
*********************/
//returns the value entered in the textfield
//returns false if no value entered
function getTFValue(gFrmName,fldName) {
	getFrmEl(gFrmName,fldName);
	if (frmEl.value!=='') return frmEl.value;
	else return false; //no value entered
}
//returns the VALUE of the selected radio button in the group where name=fldName
//returns false if none selected
function getRBValue(gFrmName,fldName) {
	getFrmEl(gFrmName,fldName);
	for (var i=0;i<frmEl.length;i++) {
		if (frmEl[i].checked)
			return frmEl[i].value;
	}
	return false; //no radio selected
}
//returns the VALUE of the selected item in the DDL fldName
//returns false if default value selected, thus A DEFAULT VALUE MUST BE SET AT FIRST POSITION
function getDLValue(gFrmName,fldName) {
	getFrmEl(gFrmName,fldName);
	var index = frmEl.selectedIndex;
	if (index!=0) return frmEl.options[index].value;
	else return false; //default value selected
}
//returns as an array the VALUES of the selected checkboxes in the group where name=fldName
//returns false if none selected
function getCBValue(gFrmName,fldName) {
	getFrmEl(gFrmName,fldName);
	var aryCBValue= new Array();
	if (!frmEl.length && frmEl.checked) aryCBValue = frmEl.value; //this is a single CB
	else { //this is a group CB
		for (var i=0;i<frmEl.length;i++) {
			if (frmEl[i].checked) aryCBValue.push(frmEl[i].value);
		}
	}
	if (aryCBValue.length>0) return aryCBValue;
	else return false; //no checkbox selected
}


/********************
* Misc Validation methods
*********************/
//checks that email is of the format a@b.c
//returns false otherwise
function getEmailValue(gFrmName,fldName) {
	getFrmEl(gFrmName,fldName);
	var str = new String(frmEl.value);
	posAt = str.indexOf('@');
	posDot = str.indexOf('.', posAt);
	//if can find @ and . and . is after @ and there is at least one character between @ and .
	if (posAt!= -1 && posDot!= -1 && posDot>posAt && posDot-posAt>1) return frmEl.value; 
	else return false; //email invalid
}
//checks that the value in the DL/RB/CB fldName equals reqValue
//1. called onkeypress in 'Others' textfield: onkeypress="chkValueSpecific(this.form.name,fldName,'[O] Others',event);"
//   otherwise prompts user and doesnt allow any values to be entered in the textfield
//2. called onchange of DL, or onclick of RB/CB: onchange/onclick="chkValueSpecific(this.form.name,this.name,'[O] Others',resetFldName);"
//   otherwise empties value in the textfield
function chkValueSpecific(gFrmName,fldName,reqValue,eventOrResetFldName) {
	switch (getFrmElType(fldName)) { //retrieve form element type
		case 'RB':
			if (getRBValue(gFrmName, fldName)==reqValue) return true;
			break;
		case 'DL':
			if (getDLValue(gFrmName, fldName)==reqValue) return true;
			break;
		case 'CB':
			var ary=getCBValue(gFrmName,fldName);
			for (var i=0; i<ary.length; i++) {
				if (ary[i]==reqValue) return true;
			}
			break;
	}
	if (typeof(eventOrResetFldName)=='object') { //parameter passed is the onkeypress event
		alert("Please choose '"+reqValue+"' to enter data");
		cancelIllegal(eventOrResetFldName);
	}
	else setTFValue(gFrmName,eventOrResetFldName,''); //parameter passed is the name of the TF to reset
}
//checks that the value in fldName is numeric
//prevents user input from being displayed otherwise
function chkNumeric(e) {
	var keyCode = (e.keyCode) ? e.keyCode : e.which;
	//keyCode==8 - backspace
	//keyCode==9 - tab
	//keyCode==13 - enter
	if ((keyCode<45 || keyCode>57 || keyCode==46) && keyCode!=8 && keyCode!=9 && keyCode!=13) { 
		alert("Only digits to be entered");
		cancelIllegal(e);
	}
}
//checks that NRIC is of the form S8019776D with correct first and last alphabet
function chkNRIC(gFrmName,fldName) {
	getFrmEl(gFrmName,fldName);
	var nric = new String(frmEl.value);

	var nricValid=false;
	if (nric.length==9 && !isNaN(parseInt(nric.substring(1,8)))) { //must have 9 characters, chars 2-8 must be numbers
		var nricFirstAlp = nric.charAt(0).toUpperCase();
		if (nricFirstAlp=='S' || nricFirstAlp=='T') { //first char must be S or T
			var d0 = (nricFirstAlp=='S') ? 0 : 4; //d0 = 0 if first letter is S, else 4 if first letter is T
			var d1 = parseInt(nric.charAt(1));
			var d2 = parseInt(nric.charAt(2));
			var d3 = parseInt(nric.charAt(3));
			var d4 = parseInt(nric.charAt(4));
			var d5 = parseInt(nric.charAt(5));
			var d6 = parseInt(nric.charAt(6));
			var d7 = parseInt(nric.charAt(7));
			var correctLastAlpNum = mod11(d0 + mod11(2*d1) + mod11(7*d2) + mod11(6*d3) + mod11(5*d4) + mod11(4*d5) + mod11(3*d6) + mod11(2*d7)); //compute number reference for last alphabet

			var correctLastAlp;
			switch (correctLastAlpNum) { //check alphabet for computed number reference
				case 0: correctLastAlp='J'; break;
				case 1: correctLastAlp='Z'; break;
				case 2: correctLastAlp='I'; break;
				case 3: correctLastAlp='H'; break;
				case 4: correctLastAlp='G'; break;
				case 5: correctLastAlp='F'; break;
				case 6: correctLastAlp='E'; break;
				case 7: correctLastAlp='D'; break;
				case 8: correctLastAlp='C'; break;
				case 9: correctLastAlp='B'; break;
				case 10: correctLastAlp='A'; break;
				default: alert('error');
			}

			var nricLastAlp = nric.charAt(8); //get user-entered last alphabet
			if (nricLastAlp.toUpperCase()==correctLastAlp) nricValid=true;
		}
	}

	if (nricValid) return true;
	else return false;
}

function mod11(myNum) {
	var num=myNum;
	while (num>=11) num-=11;
	return num;
}
//takes values from 3 separate textfields, returns a single string of date in the format DD/MM/YYYY
//given the generic term 'frmTFdob', this function will autom check values from the textfields - 'frmTFdobDay', 'frmTFdobMonth', 'frmTFdobYear'
//checks that the day/month/year entries are valid values: correct num days for each month incl feb for leap years, year must be between 1900 and current year inclusive
function chkDate(gFrmName,fldName) {
	getFrmEl(gFrmName,fldName);
	var usrDay=eval("document."+gFrmName+"."+fldName+"Day.value");
	var usrMonth=eval("document."+gFrmName+"."+fldName+"Month.value");
	var usrYear=eval("document."+gFrmName+"."+fldName+"Year.value");

	if (usrDay.toString().length!=2 || usrMonth.toString().length!=2 || usrYear.toString().length!=4) return false; //invalid format, eg. entered 2 for Feb instead of 02

	var usrDay=parseInt(usrDay); //get number values of user-entered dates
	var usrMonth=parseInt(usrMonth);
	var usrYear=parseInt(usrYear);

	var currDate=new Date();
	var currYear = currDate.getFullYear();

	var dateValid=false;
	if (usrYear>=1900 && usrYear<=currYear) { //valid year (between 1900 and current year inclusive)
		if (usrMonth>=1 && usrMonth<=12) { //valid month
			if (usrMonth==2) {
				if (chkLeapYear(usrYear) && usrDay<=29) dateValid=true; //valid day of leap year
				else if (usrDay<=28) dateValid=true; //valid day of non-leap year
			}
			else if ((usrMonth==1 || usrMonth==4 || usrMonth==6 || usrMonth==9 || usrMonth==11) && usrDay<=30) dateValid=true; //valid day
			else if (usrDay<=31) dateValid=true; //valid day
		}
	}

	if (dateValid) return usrDay+"/"+usrMonth+"/"+usrYear; //returns dob in the format DD/MM/YYYY
	else return false;
}
//checks if year is a leap year
//A year will be a leap year if it is divisible by 4 but not by 100. 
//If a year is divisible by 4 and by 100, it is not a leap year unless it is also divisible by 400.
//eg. 1996, 1992, 1988 are leap years (divisible by 4 but not by 100)
//century years eg. 1900, 1800 and 1700 are not leap years (divisible by 4 and 100 but not by 400)
//called from chkDate()
function chkLeapYear(year) {
	if (year%4==0) { //divisible by 4
		if (year%100==0) { //divisible by 100
			if (year%400==0) return true; //divisible by 400 too, therefore is leap year
			else return false; //divisible by 4 and 100, not leap year
		}
		else return true; //divisible by 4 but not by 100, therefore is leap year
	}
	else return false;
}
//checks that age is between minAge (inclusive) and maxAge (exclusive), ie. age>=minAge and age<maxAge
function chkAge(gFrmName,fldName,minAge,maxAge) {
	var usrDay=parseInt(eval("document."+gFrmName+"."+fldName+"Day.value"));
	var usrMonth=parseInt(eval("document."+gFrmName+"."+fldName+"Month.value"));
	var usrYear=parseInt(eval("document."+gFrmName+"."+fldName+"Year.value"));
	var today=new Date();
	
	var usrDOB=new Date();
	usrDOB.setDate(usrDay);
	usrDOB.setMonth(usrMonth-1);
	usrDOB.setYear(usrYear);
	var usrAge=today.getFullYear()-usrYear;

	var maxDOB=new Date();
	maxDOB.setDate(today.getDate()+1);
	maxDOB.setMonth(today.getMonth());
	maxDOB.setYear(today.getFullYear()-maxAge);
	
	var minDOB=new Date();
	minDOB.setDate(today.getDate());
	minDOB.setMonth(today.getMonth());
	minDOB.setYear(today.getFullYear()-minAge);
	
	var oneDayMs = 1000 * 60 * 60 * 24 ;// The number of milliseconds in one day
	var usrDOBms=Math.round(Math.abs(usrDOB.getTime())/oneDayMs); //convert to number of days
	var maxDOBms=Math.round(Math.abs(maxDOB.getTime())/oneDayMs);
	var minDOBms=Math.round(Math.abs(minDOB.getTime())/oneDayMs);
	
	var ageValid=false;
	if (usrAge>minAge && usrAge<maxAge) ageValid=true; //age between 19-59
	else if (usrAge==minAge || usrAge==maxAge) { //age at 18 or 60
		var usrDOBcloserToMaxAge= Math.abs(maxDOBms-usrDOBms)<Math.abs(minDOBms-usrDOBms) ? true : false; 
		if (usrDOBcloserToMaxAge && usrDOBms<=maxDOBms) ageValid=true;
		else if (!usrDOBcloserToMaxAge && usrDOBms<=minDOBms) ageValid=true;
	}
	
	/*alert('usrDOB : '+usrDOB +'\n' + 
		  'usrAge : '+usrAge +'\n' + 
		'\n' +
		  'minDOB : '+minDOB +'\n' + 
		  'maxDOB : '+maxDOB +'\n' + 
		'\n' +
		  'minDOBms : '+minDOBms + '\n' + 
		  'maxDOBms : '+maxDOBms + '\n' + 
		  'usrDOBms : '+usrDOBms + '\n' +  
		'\n' +
		  'ageValid : '+ageValid +'\n' + 
		'\n');  */
	
	return ageValid;
}
//converts value of fldName into uppercase
function convertUppercase(gFrmName,fldName) {
	getFrmEl(gFrmName,fldName);
	frmEl.value=frmEl.value.toUpperCase();
}
//prevents illegal character from appearing
function cancelIllegal(e) {
	(e.keyCode) ? e.returnValue = false : e.preventDefault(); //for IE & Netscape respectively
}
function trim(S) {
	reg=/\s/ig;
	var c= new String();
	var D = new String();
	D = S;
	var newstring=new String("");
	for (i=1; i<=D.length; i++){
		c=D.charAt(i-1);
		if (c.search(reg) == "-1") newstring=newstring.concat(c);
	}
	return newstring;
}