/***********************************************/
/* Script: validator.js                        */
/* Author: Jim Salyer                          */
/***********************************************/

var frm = null;
var isIE = false;
var fldValue = null;
var j = 0;
var fields = new Array();
var dynFields = new Array();

/**** Validator Loader Function ****/
function loadValidator(frmObj, isSecure)
{
	// check if a valid form was passed in
	if (!frmObj)
	{
		alert("You did not pass a valid form to the validator.");
		return;
	}
	frm = frmObj;
	
	// load the page securely (if necessary)
	if (isSecure && location.protocol != "https:")
	{
		var loc = location.href.replace(/http:/i, "https:");
		location.href = loc;
	}
	loadDynFields();
	
	// load the event handlers for the form's submit button
	if (frm.btnSubmit)
		frm.btnSubmit.onclick = new Function("validate();");
	else if (frm.btnDelete)
		frm.btnDelete.onclick = new Function("validate();");
} 
/**** Validator Loader Function ****/

/**** Dynamic Length Checker Functions ****/
function checkBrowser()
{
	if(document.all)
		isIE = true;
}

function isSpecial(key)
{
	// see if the key given is a special (non-character) key
	var specKeys = [8, 35, 36, 37, 38, 39, 40, 46];
	for (i=0; i<specKeys.length; i++)
		if (key == specKeys[i])
			return true;
	return false;
}

function checkLength(field, countfield, maxlimit, displayName, evt)
{
	// get the key that was pressed
	var keyPressed;
	if (isIE)
		keyPressed = event.keyCode;
	else
		keyPressed = evt.which;
		
	// check the field's value length
	if (field.value.length >= maxlimit && !isSpecial(keyPressed))
	{
		countfield.focus();
		alert('You have reached the maximum length allowed for ' + displayName + '.\nClick at the end of the text or somewhere in the text and use the backspace key to edit.');
		if (j==0)
		{
			field.value = field.value.substring(maxlimit, 0);
			fldValue = field.value;
			j = 1;
		}
		else
			field.value = fldValue;
		countfield.value = maxlimit - field.value.length;
	}
	else
	{
		j = 0
		countfield.value = maxlimit - field.value.length;
	}
}
/**** Dynamic Length Checker Functions ****/

/**** Dynamic Field Setup Functions ****/
function DynField(fldName, fldCounterName, fldMaxLength, fldDisplayName)
{
	// create the dynamic field object
	this.name = fldName;
	this.counterName = fldCounterName;
	this.maxLength = fldMaxLength;
	this.displayName = fldDisplayName;
}

function loadDynFields()
{
	var i, j;
	var fldItem;
	var fld, fldCounter;
	
	// check that a valid form was passed in
	if (!frm || !frm.elements) return;
	
	for (i=0; i<dynFields.length; i++)
	{
		// check if the current field is a valid one
		fldItem = dynFields[i];
		if (!frm.elements[fldItem.name])
		{
			alert(fldItem.name + " is not a valid field.");
			return;
		}
		
		// set the field up accordingly
		fld = frm.elements[fldItem.name];
		fldCounter = frm.elements[fldItem.counterName];
		fldCounter.value = fldItem.maxLength - fld.value.length;
		fld.onkeyup = function(evt)
		{
			if (!evt) evt = event;
			checkLength(fld, fldCounter, fldItem.maxLength, fldItem.displayName, evt);
		};
	}
}
/**** Dynamic Field Setup Functions ****/

/**** Field Setup Functions ****/
function Field(fldName, fldFormat, fldDisplayName)
{
	// create an object that the validator can parse
	this.name = fldName;
	this.format = fldFormat;
	this.displayName = fldDisplayName;
}

function getValue(arr, str)
{
	var val = "";
	
	// get the value of the specified array item
	for (var i=0; i<arr.length; i++)
	{
		if (arr[i].indexOf(str) != -1)
		{
			val = arr[i].split("=")[1];
			return val;
		}
	}
	return val;
}

function toggleBackground(fld, isError)
{
	// toggle the given field's background color
	if (isError && fld.style)
	{
		fld.style.backgroundColor = "red";
		fld.style.color = "white";
	}
	else if (!isError && fld.style)
	{
		fld.style.backgroundColor = "white";
		fld.style.color = "black";
	}
}
/**** Field Setup Functions ****/

/**** Credit Card Check Functions ****/
function getDigitsOnly(s)
{
	// strip the digits out of the credit card number
	var digitsOnly = "";
	var c;
	for (var i=0; i<s.length; i++)
	{
		c = s.charAt(i);
		if (!isNaN(c) && c != " ")
			digitsOnly += c;
	}
	return digitsOnly;
}

function luhnCheck(fld)
{
	// check the credit card number using the Luhn algorithm
	var cardNumber = fld.value;
	var digitsOnly = getDigitsOnly(cardNumber);
	var sum = 0, digit = 0, addend = 0;
	var timesTwo = false;

	for (var i=digitsOnly.length-1; i>=0; i--)
	{
		digit = parseInt(digitsOnly.substring(i, i+1));
		if (timesTwo)
		{
			addend = digit * 2;
			if (addend > 9)
				addend -= 9;
		}
		else
			addend = digit;
		sum += addend;
		timesTwo = !timesTwo;
	}

	var modulus = sum % 10;
	if (modulus == 0)
	{
		fld.value = digitsOnly;
		return true;
	}
	else
	{
		fld.value = cardNumber;
		return false;
	}
}
/**** Credit Card Check Functions ****/

/**** Main Validation Function ****/
function validate()
{
	// working variables
	var i, j;
	var fld, fldItem;
	var hasErrors = false, doCheck = false;
	var message = "There were errors on the form:";
	var checks;
	var pattern;
	
	// check that a valid form was passed in
	if (!frm || !frm.elements) return;
	
	// only check the fields defined in the fields array
	for (i=0; i<fields.length; i++)
	{
		// get the field item and check that the field object exists
		fldItem = fields[i];
		if (!frm.elements[fldItem.name])
		{
			alert(fldItem.name + " is not a valid field.");
			return;
		}
		
		// get the field trim its value of any leading or trailing spaces
		fld = frm.elements[fldItem.name];
		doCheck = false;
		if (fld.value)
		{
			tempValue = fld.value;
			pattern = /^(\s*)$/;
		
		  // check for all spaces
		  if (pattern.test(tempValue))
			{
		    tempValue = tempValue.replace(pattern, '');
		    if (tempValue.length == 0) fld.value = tempValue;
		  }
		    
		  // check for leading and trailing spaces
		  pattern = /^(\s*)([\W\w]*)(\b\s*$)/;
		  if (pattern.test(tempValue))
		     tempValue = tempValue.replace(pattern, '$2');
		 	fld.value = tempValue;
			
			// handle leading and trailing spaces in a textarea
			tempValue = escape(fld.value);
			pattern = /^(%20)*([\W\w]*)(%20$)*/;
			if (pattern.test(tempValue))
				tempValue = tempValue.replace(pattern, '$2');
			fld.value = unescape(tempValue);
		}
		
		// check the field for being required
		if (fldItem.format.indexOf("req") != -1)
		{
			// check for a value in the field
			doCheck = true;
			if (fld.length && !fld.type)
			{
				// validate an array of check boxes with the same name
				var isChecked = false;
				for (j=0; j<fld.length; j++)
				{
					if (fld[j].checked)
					{
						isChecked = true;
						break;
					}
				}
				if (!isChecked)
				{
					doCheck = false;
					hasErrors = true;
					message += "\nYou must select an item for " + fldItem.displayName + ".";
					toggleBackground(fld, true);
				}
				else
					toggleBackground(fld, false);
			}
			else if (fld.value == "" || (!fld.value && fld.options[fld.selectedIndex].value == ""))
			{
				doCheck = false;
				hasErrors = true;
				message += "\nYou must enter a value in " + fldItem.displayName + ".";
				toggleBackground(fld, true);
			}
			else
				toggleBackground(fld, false);
		}
		else if (fld.value != "") doCheck = true;
		
		checks = fldItem.format.split(","); // get the field's formatting options
		if (doCheck && fldItem.format.indexOf("numeric") != -1)
		{
			// check for a numeric value
			if (isNaN(fld.value))
			{
				doCheck = false;
				hasErrors = true;
				message += "\nYou have entered a non-numeric value in " + fldItem.displayName + ".";
				toggleBackground(fld, true);
			}
			else
				toggleBackground(fld, false);
		}
		if (doCheck && fldItem.format.indexOf("minlen") != -1)
		{
			// check for a minimum length
			if (fld.value.length < getValue(checks, "minlen"))
			{
				doCheck = false;
				hasErrors = true;
				message += "\nThe length of " + fldItem.displayName + " must be greater than or equal to " + getValue(checks, "minlen") + ".";
				toggleBackground(fld, true);
			}
			else
				toggleBackground(fld, false);
		}
		if (doCheck && fldItem.format.indexOf("maxlen") != -1)
		{
			// check for a maximum length
			if (fld.value.length > getValue(checks, "maxlen"))
			{
				doCheck = false;
				hasErrors = true;
				message += "\nThe length of " +  fldItem.displayName + " must be less than or equal to " + getValue(checks, "maxlen") + ".";
				toggleBackground(fld, true);
			}
			else
				toggleBackground(fld, false);
		}
		if (doCheck && fldItem.format.indexOf("ccdate") != -1)
		{
			// identify the separator character within the given date
			var sep;
			if (fld.value.indexOf("/") != -1)
				sep = "/";
			else if (fld.value.indexOf("-") != -1)
				sep = "-";
				
			// split the date into its parts
			var dateParts = fld.value.split(sep);
			
			// if there are date errors, process accordingly
			if (dateParts.length != 2 || dateParts[0] < 1 || dateParts[0] > 12)
			{
				doCheck = false;
				hasErrors = true;
				message += "\nAn invalid expiration date has been entered in " + fldItem.displayName + ".";
				toggleBackground(fld, true);
			}
			else
				toggleBackground(fld, false);
		}
		else if (doCheck && fldItem.format.indexOf("date") != -1)
		{
			// identify the separator character within the given date
			var sep;
			if (fld.value.indexOf("/") != -1) 
				sep = "/";
			else if (fld.value.indexOf("-") != -1) 
				sep = "-";
			
			// get a derived date for validation
			var checkDate = new Date(fld.value);
			var derivedDate = checkDate.getDate();
			var dateParts = fld.value.split(sep);
						
			// if there are date errors, process accordingly
			if (dateParts.length != 3 || derivedDate != dateParts[1])
			{
				doCheck = false;
				hasErrors = true;
				message += "\nAn invalid date has been entered in " + fldItem.displayName + ".";
				toggleBackground(fld, true);
			}
			else
				toggleBackground(fld, false);
		}
		if (doCheck && fldItem.format.indexOf("ccnum") != -1)
		{
			// check for a valid credit card number
			var isValid = luhnCheck(fld);
			if (!isValid)
			{
				doCheck = false;
				hasErrors = true;
				message += "\nYou have entered an invalid credit card number in " + fldItem.displayName + ".";
				toggleBackground(fld, true);
			}
			else
				toggleBackground(fld, false);
		}
		if (doCheck && fldItem.format.indexOf("email") != -1)
		{
			// check for a valid e-mail address
			pattern = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*\.(\w{2}|(com|net|org|edu|int|mil|gov|arpa|biz|aero|name|coop|info|pro|museum))$/i;
			if (!pattern.test(fld.value))
			{
				doCheck = false;
				hasErrors = true;
				message += "\nAn invalid e-mail address has been entered in " + fldItem.displayName + ".";
				toggleBackground(fld, true);
			}
			else
				toggleBackground(fld, false);
		}
		if (doCheck && fldItem.format.indexOf("usphone") != -1)
		{
			// check for a valid phone number
			fld.value = getDigitsOnly(fld.value);
			pattern = /^\d{10}$/;
			if (!pattern.test(fld.value)) 
			{
				doCheck = false;
				hasErrors = true;
				message += "\nYou have entered an invalid phone number in " + fldItem.displayName + ".";
				toggleBackground(fld, true);
			}
			else
				toggleBackground(fld, false);
		}
		if (doCheck && fldItem.format.indexOf("uszip") != -1)
		{
			// check for a valid zip code
			pattern = /^\d{5}(-\d{4})?$/i;
			if (!pattern.test(fld.value))
			{
				doCheck = false;
				hasErrors = true;
				message += "\nThe value of " + fldItem.displayName + " must be in the form 55555 or 55555-5555.";
				toggleBackground(fld, true);
			}
			else
				toggleBackground(fld, false);
		}
	}
	
	// don't submit and display a message if errors were found
	if (hasErrors)
		alert(message);
	else
	{
		// handle submitting special characters and submit
		for (i=0; i<fields.length; i++)
		{
			fld = frm.elements[fields[i].name];
			if (fld.value)
			{
				fld.value = fld.value.replace(/'/g, "'");
				fld.value = fld.value.replace(/"/g, "&quot;");
			}
		}
		if ((frm.btnDelete && confirm("Press OK to process deletion.")) || 
			!frm.btnDelete)
			frm.submit();
	}
}
/**** Main Validation Function ****/