//********************************************************************************************************************
//********************************************************************************************************************
//** Creator: 		Brad LaMotte
//** Version		1.0
//** Date: 			11/29/2005
//**
//** Description:	This script will require designated fields, validate designated field values, and check
//**				designated field value length for a given form.
//**
//** Impact:		This function can be used with multiple forms on the same page.  It will validate fields
//**				of the following type:
//**
//**				Text Field
//**				Hidden Field
//**				File Browse Field
//**				Password Field
//**				Textarea
//**				Radio Button
//**				Select Box (one selection)
//**				Select Box (multiple selections)
//**				Checkbox
//**
//**			And will validate the following formats (optional):
//**
//**				Number
//**				Integer
//**				Float
//**				SSN
//**				Email
//**				Phone
//**				Date
//**				Zip
//**				Credit Card
//**
//** Implementation:	Run this script when the form is submitted, usually using an onClick() or onSubmit() call.  The
//**			script expects two parameters: the form name and the list of fields to check.
//**
//**				onClick="return validateFormFields('test','name=firstName|desc=First Name,name=age|desc=Age field|validate=number');"
//**
//**				The first parameter, 'formName', is the name of the form you are calling from
//**
//**				The second parameter consists if a comma-delimited list of form fields to check.
//**				Each item in the comma-delimited list consists of another list, delimited by '|' (pipe)  This
//**				pipe-delimited list is an associative array.  The following are keys that can be passed
//**				within this associative array:
//**
//**				a. name
//**					Name of the form field being checked.
//**
//**				b. desc
//**					Description of the form field being checked.  This is used when reporting an
//**					error with this field.
//**
//**				c. validate
//**					Type of validation to run on the value of this field.  Options are:
//**					number, integer, float, ssn, email, phone, date, zip, credit
//**
//**				d. allowNull
//**					Used when you want to validate a value if a value was entered, but also
//**					allow no value.  To not allow nulls, either do not include this key or set
//**					to false. To allow nulls, set to true.
//**
//**				e. length
//**					This must be a number and field value length must equal this number.
//**
//**
//**			When the call to validateFormFields function gets too long and hard to manage, place
//**			each field on it's own line like this:
//**
//**				onClick="return validateFormFields('formName',''+
//**					'name=firstName|desc=First Name|validate=number|allowNull=false|length=4,'+
//**					'name=age|desc=Age field|validate=number');"
//**
//**
//**			You may also call the individual field type validators separately.  They all accept one parameter:
//**				the value to be validated.  And they all return either true or false.  Here they are:
//**
//**				isEmail(emailStr)
//**				isNumber(inNum)
//**				isInteger(inNum)
//**				isFloat(inNum)
//**				isSSN(thisSSN)
//**				isPhone(inStr)
//**				isDate(inDate)
//**				isZip(inZip)
//**				isCreditCard(inCC)
//**
//**
//**			Global variables for this script are located in the next section below called 'Editable Vars'.
//**			These are variables that control validation and error alerts.  These are the only things that
//**			should be edited in this script.
//**
//**
//********************************************************************************************************************
//********************************************************************************************************************






//************************************************************
//** EDITABLE VARS
//************************************************************

var dateSeparator = '/';		// Separator between date parts for date validation
var dateMinMonth = 	1;			// Number of digit place holders required for the month
var dateMinDay = 	1;			// Number of digit place holders required for the day
var dateMinYear = 	4;			// Number of digit place holders required for the year
var allowZipSuffix = true;		// If true, allow the 4-digit zip code suffix. If false, do not allow
var reqFldMsgPre = '';			// When alerting that a field is required, the part of the message BEFORE the field desc.
var reqFldMsgPost = ' is a required field.';// When alerting that a field is required, the part of the message AFTER the field desc.




//************************************************************
//** DO NOT EDIT BELOW THIS LINE
//************************************************************

function validateFormFields(formName, fieldList)
	{
	var fieldsArray = fieldList.split(',');
	var fieldsArrayLength = fieldsArray.length;
	var thisFieldArray;
	var field;
	var fieldObj;
	var thisField;
	var fieldName;
	var fieldDesc;
	var fieldVal;
	var validationType;
	var errorAlert = '';


	for(i=0; i<fieldsArrayLength; i++)
		{
		thisFieldArray = fieldsArray[i].split('|');

		// CREATE FIELD OBJECT
		fieldObj = new Object();
		for(k=0; k<thisFieldArray.length; k++)
			{
			thisField = thisFieldArray[k].split('=');
			fieldObj[thisField[0]] = thisField[1];
			}


		// CHECK IF FIELD EXISTS
		if(!eval('document.'+formName+'.'+fieldObj['name']))
			{
			alert('The field \''+ fieldObj['name'] +'\' does not exist.');
			return false;
			}


		fieldObj['val'] = getFieldVal(formName, fieldObj['name']);
		
		
		// NO VALIDATION, JUST REQUIRE FIELD
		if(fieldObj['val'] == '' && (!fieldObj['allowNull'] || fieldObj['allowNull'] != 'true'))
			{
			errorAlert += reqFldMsgPre + fieldObj['desc'] + reqFldMsgPost + '\n';
			continue;
			}


		// FIELD REQUIRED WITH VALIDATION
		if(fieldObj['validate'])
			{
			// LET THROUGH IF ALLOW NULL
			if(fieldObj['val'] == '' && fieldObj['allowNull'] && fieldObj['allowNull'] == 'true')
				{
				continue;
				}

			switch(fieldObj['validate'])
				{
				case 'number':
					if(!isNumber(fieldObj['val']))
						{
						errorAlert += fieldObj['desc'] + ' must be a number.\n';
						}
					break;
				case 'integer':
					if(!isInteger(fieldObj['val']))
						{
						errorAlert += fieldObj['desc'] + ' must be an integer.\n';
						}
					break;
				case 'float':
					if(!isFloat(fieldObj['val']))
						{
						errorAlert += fieldObj['desc'] + ' must be in numeric format.\n';
						}
					break;
				case 'ssn':
					if(!isSSN(fieldObj['val']))
						{
						errorAlert += fieldObj['desc'] + ' must be a properly formatted Social Security Number.\n';
						}
					break;
				case 'email':
					if(!isEmail(fieldObj['val']))
						{
						errorAlert += fieldObj['desc'] + ' must be a properly formatted email address.\n';
						}
					break;
				case 'phone':
					if(!isPhone(fieldObj['val']))
						{
						errorAlert += fieldObj['desc'] + ' must be a properly formatted phone number.\n';
						}
					break;

				case 'date':
					if(!isDate(fieldObj['val']))
						{
						errorAlert += fieldObj['desc'] + ' must be a properly formatted date.\n';
						}
					break;

				case 'zip':
					if(!isZip(fieldObj['val']))
						{
						errorAlert += fieldObj['desc'] + ' must be a properly formatted zip/postal code.\n';
						}
					break;

				case 'credit':
					if(!isCreditCard(fieldObj['val']))
						{
						errorAlert += fieldObj['desc'] + ' must be a properly formatted credit card number.\n';
						}
					break;

				default:
					alert('Unknown validation for field \''+ fieldObj['name'] +'\'.');return false;
				}
			}

		// FIELD LENGTH
		if(fieldObj['length'])
			{alert('hello');
			if(!isNumber(fieldObj['length']))
				{
				alert('Unknown length for field \''+ fieldObj['name'] +'\'.');return false;
				}

			if(fieldObj['val'].length != fieldObj['length'])
				{
				errorAlert += fieldObj['desc'] + ' must be ' + fieldObj['length'] + ' characters in length.\n';
				}
			}
		}



	// RETURN ERROR ALERT
	if(errorAlert != '')
		{
		alert(errorAlert);
		return false;
		}

	return true;
	}


//************************************************************
//** EMAIL
//************************************************************
function isEmail(emailStr)
	{
	/* Indicates whether or not to verify that the address ends in a two-letter
	country or well-known TLD.  1 means check it, 0 means don't. */
	var checkTLD=1;

	/* The list of known TLDs that an e-mail address must end with (if
	checkTLD=1). */
	var knownDomsPat=/^(com|net|org|edu|int|mil|gov|arpa|biz|aero|name|coop|info|pro|museum)$/;

	/* Pattern used to check if the entered e-mail address fits the user@domain
	format.  It also is used to separate the username from the domain. */
	var emailPat=/^(.+)@(.+)$/;

	/* Pattern for matching all special characters.  We don't want to allow
	special characters in the address.  These characters include
	( )  > @ , ; : \ " . [ ] */
	var specialChars="\\(\\)><@,;:\\\\\\\"\\.\\[\\]";

	/* Range of characters allowed in a username or domainname.  It really
	states which chars aren't allowed.*/
	var validChars="\[^\\s" + specialChars + "\]";

	/* Pattern which applies if the "user" is a quoted string (in which case,
	there are no rules about which characters are allowed and which aren't;
	anything goes).  E.g. "jiminy cricket"@disney.com is a legal e-mail
	address. */
	var quotedUser="(\"[^\"]*\")";

	/* Pattern which applies for domains that are IP addresses, rather than
	symbolic names.  E.g. joe@[123.124.233.4] is a legal e-mail address.
	NOTE: The square brackets are required. */
	var ipDomainPat=/^\[(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\]$/;

	/* An atom (basically a series of non-special characters.) */
	var atom=validChars + '+';

	/* Represents one word in the typical username.  For example, in
	john.doe@somewhere.com, john and doe are words.  Basically, a word is 
either
	an atom or quoted string. */
	var word="(" + atom + "|" + quotedUser + ")";

	// Pattern describeing the structure of the user
	var userPat=new RegExp("^" + word + "(\\." + word + ")*$");

	/* Pattern describing the structure of a normal symbolic domain, as opposed
	to ipDomainPat, shown above. */
	var domainPat=new RegExp("^" + atom + "(\\." + atom +")*$");

	/* Finally, let's start trying to figure out if the supplied address is
	valid. */

	/* Begin with the coarse pattern to simply break up user@domain into
	different pieces that are easy to analyze. */
	var matchArray=emailStr.match(emailPat);
	if (matchArray==null) {
		/* Too many/few @'s or something; basically, this address doesn't
		even fit the general mould of a valid e-mail address. */
		return false;
	}

	var user=matchArray[1];
	var domain=matchArray[2];

	// Start by checking that only basic ASCII characters are in the strings
	// (0-127).
	for (i=0; iuser.length; i++) {
		if (user.charCodeAt(i)>127) {
			// Ths username contains invalid characters.
			return false;
	   }
	}
	for (i=0; i<domain.length; i++) {
		if (domain.charCodeAt(i)>127) {
			// This domain name contains invalid characters.
			return false;
	   }
	}

	// See if "user" is valid
	if (user.match(userPat)==null) {
		// The username doesn't seem to be valid.
		return false;
	}

	/* if the e-mail address is at an IP address (as opposed to a symbolic
	host name) make sure the IP address is valid. */
	var IPArray=domain.match(ipDomainPat);
	if (IPArray!=null) {
		// this is an IP address
		for (var i=1;i<=4;i++) {
			if (IPArray[i]>255) {
				// Destination IP address is invalid!
				return false;
		   }
		}
		return true;
	}

	// Domain is symbolic name.  Check if it's valid.
	var atomPat=new RegExp("^" + atom + "$");
	var domArr=domain.split(".");
	var len=domArr.length;
	for (i=0;i<len;i++) {
		if (domArr[i].search(atomPat)==-1) {
			// The domain name does not seem to be valid.
			return false;
	   }
	}

	/* domain name seems valid, but now make sure that it ends in a
	known top-level domain (like com, edu, gov) or a two-letter word,
	representing country (uk, nl), and that there's a hostname preceding
	the domain or country. */
	if (checkTLD && domArr[domArr.length-1].length!=2 &&
			domArr[domArr.length-1].search(knownDomsPat)==-1) {
		// The address must end in a well-known domain or two letter country.
		return false;
	}

	// Make sure there's a host name preceding the domain.
	if (len2) {
		// This address is missing a hostname!
		return false;
	}

	// If we've gotten this far, everything's valid!
	return true;
	}




//************************************************************
//** NUMERIC
//************************************************************
function isNumber(inNum)
	{
	if(inNum == '')
		{
		return false;
		}

	var allowables = '0123456789';
	var numLen = inNum.length;

	for(z=0; z<numLen; z++)
		{
		if(allowables.indexOf(inNum.charAt(z)) < 0)
			{
			return false;
			}
		}
	return true;
	}


//************************************************************
//** INTEGER
//************************************************************
function isInteger(inNum)
	{
	if(inNum.length < 1)
		{
		return false;
		}

	var allowables = '-0123456789';
	var numLen = inNum.length;

	for(z=0; z<numLen; z++)
		{
		if(allowables.indexOf(inNum.charAt(z)) < 0)
			{
			return false;
			}
		}
	return true;
	}


//************************************************************
//** FLOAT
//************************************************************
function isFloat(inNum)
	{
	if(inNum.length < 1)
		{
		return false;
		}

	var allowables = '-0123456789.';
	var numLen = inNum.length;

	for(z=0; z<numLen; z++)
		{
		if(allowables.indexOf(inNum.charAt(z)) < 0)
			{
			return false;
			}
		}
	return true;
	}


//************************************************************
//** SOCIAL SECURITY NUMBER
//************************************************************
function isSSN(thisSSN)
	{
	if(thisSSN.length > 0)
		{
		temp = thisSSN.replace(/-/g,'');

		if(!isNumber(temp) || temp.length != 9)
			{
			return false;
			}
		}
	else
		{
		return false;
		}
	return true;
	}


//************************************************************
//** PHONE
//************************************************************
function isPhone(inStr)
	{
	if(inStr.length < 1)
		{
		return false;
		}

	var stripped = inStr.replace(/[\(\)\.\-\ ]/g, '');

	if(isNaN(parseInt(stripped)) || stripped.length != 10)
		{
	   	return false;
		}
	}

//************************************************************
//** DATE
//************************************************************
/*function isDate(inDate)
	{
	if(inDate.length < 1)
		{
		return false;
		}

	var parsedDate = inDate.split(dateSeparator);
	var day, month, year;

	if(parsedDate.length != 3) return false;

	month = parsedDate[0]-1;
	day = parsedDate[1];
	year = parsedDate[2];

	var objDate = new Date (month+1+'/'+day+'/'+year);

	if (month != objDate.getMonth() || parsedDate[0].length < dateMinMonth){alert('here'); return false};
	if (day != objDate.getDate() || parsedDate[1].length < dateMinDay) return false;
	if (year != objDate.getFullYear() || parsedDate[2].length < dateMinYear) return false;

	return true;
	}*/


//************************************************************
//** ZIP CODE
//************************************************************
function isZip(inZip)
	{
	if(inZip.length < 1)
		{
		return false;
		}

	var parsedZip = inZip.replace(' ', '');
	parsedZip = parsedZip.split('-');

	if(parsedZip.length > 2 || !isNumber(parsedZip[0]) || parsedZip[0].length != 5)
		{
		return false;
		}

	if(parsedZip.length == 2)
		{
		if(!allowZipSuffix || !isNumber(parsedZip[1]) || parsedZip[1].length != 4)
			{
			return false;
			}
		}
	return true;
	}


//************************************************************
//** CREDIT CARD
//************************************************************
function isCreditCard(inCC)
	{
	if(inCC.length < 1)
		{
		return false;
		}

	var parsedCC = inCC.replace(' ', '');
	parsedCC = parsedCC.replace('-', '');

	if(!isNumber(parsedCC) || parsedCC.length != 16)
		{
		return false;
		}
	return true;
	}
	
	
//************************************************************
//** GET FIELD VAL
//************************************************************
function getFieldVal(formName, fieldName)
	{
	var field = eval('document.'+ formName+'.'+ fieldName);
	var fieldVal = '';

		// RADIO
		if(field[0] && field[0].type == 'radio')
			{
			for(j=0; j<field.length; j++)
				{
				if(field[j].checked == true)
					{
					fieldVal = field[j].value;
					}
				}
			}

		// MULTIPLE CHECKBOXES
		else if(field[0] && field[0].type == 'checkbox')
			{
			for(j=0; j<field.length; j++)
				{
				if(field[j].checked == true)
					{
					if(fieldVal != '')
						{
						fieldVal += ',';
						}
					fieldVal += field[j].value;
					}
				}
			}

		// SINGLE CHECKBOX
		else if(field.type == 'checkbox')
			{
			if(field.checked == true)
				{
				fieldVal = field.value;
				}
			}

		// DROP-DOWN
		else if(field.type == 'select-one' || field.type == 'select-multiple')
			{
			for(j=0; j<field.length; j++)
				{
				if(field[j].selected == true)
					{
					fieldVal = field[j].value;
					}
				}
			}

		// REGULAR VALUE FIELD
		else
			{
			fieldVal = field.value;
			}
	return fieldVal;
	}

