//********************************************************************************************************************
//********************************************************************************************************************
//** 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, datepartial, 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.
//**
//**				f. custommsg
//**					Override the default error message with a custom message.				
//**
//**				
//**			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;
			}
				
		
		field = eval('document.'+ formName+'.'+ fieldObj['name']);
							
		// RADIO
		if(field[0] && field[0].type == 'radio')
			{
			fieldObj['val'] = '';
			for(j=0; j<field.length; j++)
				{
				if(field[j].checked == true)
					{
					fieldObj['val'] = field[j].value;
					}
				}
			}
			
		// MULTIPLE CHECKBOXES
		else if(field[0] && field[0].type == 'checkbox')
			{
			fieldObj['val'] = '';
			for(j=0; j<field.length; j++)
				{
				if(field[j].checked == true)
					{
					fieldObj['val'] = field[j].value;
					}
				}
			}
			
		// SINGLE CHECKBOX
		else if(field.type == 'checkbox')
			{
			fieldObj['val'] = '';
			if(field.checked == true)
				{
				fieldObj['val'] = field.value;
				}
			}
			
		// DROP-DOWN
		else if(field.type == 'select-one' || field.type == 'select-multiple')
			{
			fieldObj['val'] = '';
			for(j=0; j<field.length; j++)
				{
				if(field[j].selected == true)
					{
					fieldObj['val'] = field[j].value;
					}
				}
			}
			
					
		// REGULAR VALUE FIELD
		else
			{
			fieldObj['val'] = field.value;
			}
			
		// NO VALIDATION, JUST REQUIRE FIELD
		if(fieldObj['val'] == '' && (!fieldObj['allowNull'] || fieldObj['allowNull'] != 'true'))
			{
			if (typeof fieldObj['custommsg'] == 'string')
				{
				errorAlert += fieldObj['custommsg'] + '\n';
				}
			else
				{
				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']))
						{
							if (typeof fieldObj['custommsg'] == 'string') {
								errorAlert += fieldObj['custommsg'] + '\n';
							} else {
								errorAlert += fieldObj['desc'] + ' must be a number.\n';									
							}							
						}
					break;
				case 'integer':
					if(!isInteger(fieldObj['val']))
						{
							if (typeof fieldObj['custommsg'] == 'string') {
								errorAlert += fieldObj['custommsg'] + '\n'; 
							} else {
								errorAlert += fieldObj['desc'] + ' must be an integer.\n';									
							}							
						}
					break;
				case 'float':
					if(!isFloat(fieldObj['val']))
						{
							if (typeof fieldObj['custommsg'] == 'string') {
								errorAlert += fieldObj['custommsg'] + '\n'; 
							} else {
								errorAlert += fieldObj['desc'] + ' must be in numeric format.\n';									
							}							
						}
					break;					
				case 'ssn':
					if(!isSSN(fieldObj['val']))
						{
							if (typeof fieldObj['custommsg'] == 'string') {
								errorAlert += fieldObj['custommsg'] + '\n'; 
							} else {
								errorAlert += fieldObj['desc'] + ' must be a properly formatted Social Security Number.\n';									
							}							
						}
					break;
				case 'email':
					if(!isEmail(fieldObj['val']))
						{
							if (typeof fieldObj['custommsg'] == 'string') {
								errorAlert += fieldObj['custommsg'] + '\n';
							} else {
								errorAlert += fieldObj['desc'] + ' must be a properly formatted email address.\n';									
							}							
						}
					break;
				case 'phone':
					if(!isPhone(fieldObj['val']))
						{
							if (typeof fieldObj['custommsg'] == 'string') {
								errorAlert += fieldObj['custommsg'] + '\n'; 
							} else {
								errorAlert += fieldObj['desc'] + ' must be a properly formatted phone number.\n';									
							}							
						}
					break;
					
				case 'date':
					if(!isDate(fieldObj['val']))
						{
							if (typeof fieldObj['custommsg'] == 'string') {
								errorAlert += fieldObj['custommsg'] + '\n';
							} else {
								errorAlert += fieldObj['desc'] + ' must be a properly formatted date.\n';									
							}							
						}
					break;

				case 'datepartial':
					if(!isDatePartial(fieldObj['val']))
						{
							if (typeof fieldObj['custommsg'] == 'string') {
								errorAlert += fieldObj['custommsg'] + '\n';
							} else {
								errorAlert += fieldObj['desc'] + ' must be a properly formatted date.\n';
							}	
						}
					break;
					
				case 'zip':
					if(!isZip(fieldObj['val']))
						{
							if (typeof fieldObj['custommsg'] == 'string') {
								errorAlert += fieldObj['custommsg'] + '\n'; 
							} else {
								errorAlert += fieldObj['desc'] + ' must be a properly formatted zip/postal code.\n';									
							}							
						}
					break;
					
				case 'credit':
					if(!isCreditCard(fieldObj['val']))
						{
							if (typeof fieldObj['custommsg'] == 'string') {
								errorAlert += fieldObj['custommsg'] + '\n';
							} else {
								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'])
			{
			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; i<user.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 (len<2) {
		// 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 (Full) - mm/dd/yyyy
//************************************************************
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;
	}
	
//************************************************************
//** DATE (partial) - mm/yyyy
//************************************************************

function isDatePartial(inDate)
	{
		if(inDate != '')
			{
			if(inDate.indexOf('/') == -1)
				{
				return false;
				}
			else
				{
				var dateArray = inDate.split('/');
				if(dateArray.length != 2 || 
					!isNumber(dateArray[0]) ||
					dateArray[0].length != 2 ||
					dateArray[0] < 1 || 
					dateArray[0] > 12 ||
					!isNumber(dateArray[1]) ||
					dateArray[1].length != 4 ||
					dateArray[1] < 1900)
					{
					return false;
					}			
				}

			/*if(failCheck == 1)
				{
				alert('Please enter question 3 with a valid birth date in \'MM/YYYY\' format.');
				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;
	}
	