//|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//  AUTHOR: Kristian Ranstrom
// 	FILE:	validate.js
//	PURP:	input validation routines
//	
//	FUNCTIONS:
//|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//
//	FORM VALIDATION
//  errorMsg			(err)
//	errorMsgColor		(input)
//	errorMsgColorDetail	(input)
//	clearErrorColor		()
//	focusElement		(form object)
//  showErrorColor		()
//	showErrorSingle		(frm, name, msg)
//	
//	getValue			(input)
//	getRadioIndex		(input)
//	getSelectMultIndex	(input)
//
//	stripToAlphaNumeric	(str)
//
//	isPattern			(input,pattern)
//	isNumeric			(input)
//	isAlpha				(input)
//	isEmpty				(input)
//
//	isValidMonthDay		(int)
//	isLeapYear			()
//	isDay				(input)
//	isMonth				(input)
//	isYear				(input)
//	
//	isPhone				(input)
//	isPhone3			(input)
//	isPhone4			(input)
//	isPhoneAreaCode		(input)
//  formatPhone			(input)
//	formatPhoneNumeric	(input)
//
//	isThereSpaces		(input)
//	isOnlyLetters		(input)
//	isUsername			(input)
//
//	isStateAbbrev		(input)
//	isZip4				(input)
//	isZip				(input)
//	isEmail				(input)
//
//	isCreditCard		(input)
//	isMASTERCARD		(cardNumber)
//	isVISA				(cardNumber)
//	isAMEX				(cardNumber)
//	isDINERS			(cardNumber)
//	isDISCOVER			(cardNumber)
//	luhn				(cardNumber)
//	isValidExpiration	(month, year)
//
//|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

//Declare the globals
var errmsg = "";
var firstErrorField = "";
var arrErrorFields = new Array();
var errColor = "#ff0000";

//Browser Detection
var IE4up = document.all;
var NS4 = document.layers;
var NS6 = navigator.userAgent.indexOf("Netscape6") > 0;


//||ErrorMsg
//=========================================================
// FUNC:	errorMsg
// PARM: 	err
//=========================================================
function errorMsg (err) {
	if(errmsg.indexOf("following fields") == -1) {
		errmsg = "The following fields have errors: \n\n";
	}
	errmsg += err + "\n";
}

//||ErrorMsg
//=========================================================
// FUNC:	errorMsgColor
// PARM: 	err
//=========================================================
function errorMsgColor (input) {
	if(errmsg.indexOf("following fields") == -1) {
		clearErrorColor();
		errmsg = "Some fields have errors\nPlease review the form.";
		firstErrorField = getName(input.name);
	}
	arrErrorFields[arrErrorFields.length] = getName(input);
}

//||
//=========================================================
// FUNC:	errorMsgColorDetail
// PARM: 	err
//=========================================================
function errorMsgColorDetail (err, input) {
	if(errmsg.indexOf("following fields") == -1) {
		clearErrorColor();
		errmsg = "The following fields have errors: \n\n";
		firstErrorField = getName(input);
	}
	errmsg += err + "\n";
	arrErrorFields[arrErrorFields.length] = getName(input);
}

//=========================================================
// FUNC:	clearErrorColor
// PARM: 	input
//=========================================================
function clearErrorColor () {
	//If not IE4up or NS6 leave
	if(NS4) return;
	
	for(i=0; i < arrErrorFields.length; i++) {
		if(document.getElementById(arrErrorFields[i])) {
			document.getElementById(arrErrorFields[i]).style.color = "";
			document.getElementById(arrErrorFields[i]).style.fontWeight = "normal";
		}
	}
	
	arrErrorFields.length = 0;
}

//=========================================================
// FUNC:	focusElement
// PARM: 	f - form object
//=========================================================
function focusElement (f) {
	if(eval("document." + f.name + "." + firstErrorField + ".name") && eval("document." + f.name + "." + firstErrorField + ".type != 'hidden'")) eval("document." + f.name + "." + firstErrorField + ".focus()");
}

//=========================================================
// FUNC:	showErrorColor
// PARM: 	input
//=========================================================
function showErrorColor () {
	//If not IE4up or NS6 leave
	if(NS4) return;

	for(i=0; i < arrErrorFields.length; i++) {
		if(document.getElementById(arrErrorFields[i])) {
			document.getElementById(arrErrorFields[i]).style.color = errColor;
			document.getElementById(arrErrorFields[i]).style.fontWeight = "bold";
		}
	}
}

//=========================================================
// FUNC:	showErrorSingle
// PARM: 	input
//=========================================================
function showErrorSingle (frm, name, msg) {
	alert(msg);

	eval("document." + frm.name + "." + name + ".value = ''");
	eval("document." + frm.name + "." + name + ".focus()");
}


//_________________________________________________________________________________________________
//||[ GENERAL ]||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

//=========================================================
// FUNC:	getName
// PARM:	input - ref to form radio input
//=========================================================
function getName(input) {
	if(input.name) return input.name;
	else return input[0].name;
}

//=========================================================
// FUNC:	getValue
// PARM:	input - ref to form radio input
//=========================================================
function getValue(input) {

	switch (input.type) {
		case "text"				: 
		case "textarea"			: 
		case "checkbox"			: 
		case "hidden"			: 
		case "password"			: 
		case "file"				: 
		case "submit"			: 
		case "button"			:
		case "reset"			: return input.value;
		case "radio"			: return input.checked;
		case "select-one"		: return input.options[input.selectedIndex].value;
		case "select-multiple"	: var value="";
								  for (var i=0; i < input.options.length; i++)
									if (input.options[i].selected)
										value += (value == "" ? "" : ",") + i;
								  return value;
		default					: alert("getValue input type not supported."); return null;
	}
}

//=========================================================
// FUNC:	getRadioIndex
// PARM:	input - ref to form radio input
//=========================================================
function getRadioIndex(input) {
	
	for (var i=0; i < input.length; i++)
		if (input[i].checked) return i;
		
	return -1;
}

//=========================================================
// FUNC:	getSelectMultIndex
// PARM:	input - ref to form select-multiple input
//  RES:	array containing selected indexes
//=========================================================
function getSelectMultIndex(input) {
	if (input.type != "select-multiple") {
		alert("getSelectMultIndex: invalid input element");
		return -1;
	}
	
	var aResult = new Array();
	
	for (var i=0; i < input.options.length; i++)
		if (input.options[i].selected) aResult[aResult.length] = i;
		
	return aResult;
}

//=========================================================
// FUNC:	stripToAlphaNumeric
// PARM:	input - ref to form input
//			str - string to strip
// NOTE:
//			"9": numeral
//			"a": alpha
//			any other must match
//=========================================================
function stripToAlphaNumeric(str) {
	
	var i
	var temp = "";
	
	str = str.toString();

	for (i=0; i <= str.length; i++)	{
		
		if ((str.charAt(i) >= "0") && (str.charAt(i) <= "9")) temp += str.charAt(i);

		if ((str.charAt(i) >= "A") && (str.charAt(i) <= "Z")) temp += str.charAt(i);

		if ((str.charAt(i) >= "a") && (str.charAt(i) <= "z")) temp += str.charAt(i);
	}
	return temp;

}

//=========================================================
// FUNC:	isPattern
// PARM:	input - ref to form input
//			pattern - matching mask
// NOTE:
//			"9": numeral
//			"a": alpha
//			any other must match
//=========================================================
function isPattern(input,pattern) {

	var p,s,I,value;
	
	value = getValue(input);
	if (value == null) {
		alert("isPattern: getValue failed.");
		return null;
	}

	if (value.length != pattern.length) return false;
	
	for (i=1; i<=value.length; i++)	{
	
		p = pattern.charAt(i);
		s = value.charAt(i);
		
		if ((p == "9") && ((s < "0") || (s > "9"))) return false;
		else
		if ((p == "a") && ( ((s < "a") || (s > "z")) && ((s < "A") || (s > "Z")) ) ) return false;
		else
		if ((p != "9") && (p != "a") && (p != s)) return false;
	}
	return true;
}

//=========================================================
// FUNC:	isNumeric
// PARM:	input - ref to form input
//=========================================================
function isNumeric(input) {

	switch (input.type) {
		case "text"				: 
		case "textarea"			: 
		case "checkbox"			: 
		case "hidden"			: 
		case "password"			: 
		case "file"				: 
		case "submit"			: 
		case "reset"			: return (parseFloat(input.value) == input.value);
		
		case "radio"			: if ((ndx = getRadioIndex(input)) == -1) return false;
								  return (parseFloat(input[ndx].value) == input[ndx].value);
								  
		case "select-one"		: value = input.options[input.selectedIndex].value ? input.options[input.selectedIndex].value : input.options[input.selectedIndex].text;
								  return (parseFloat(value) == value);
									
		default					: alert("isNumeric: input type not supported."); return null;
	}
}

//=========================================================
// FUNC:	isAlpha
// PARM:	input - ref to form input
//=========================================================
function isAlpha(input) {
	var str = getValue(input);
	var i, temp;
	
	for (i=1; i <= str.length; i++)	{

		if ((str.charAt(i) >= "A") && (str.charAt(i) <= "Z")) temp += str.charAt(i);

		if ((str.charAt(i) >= "a") && (str.charAt(i) <= "z")) temp += str.charAt(i);
	}

	return temp == str;
}

//=========================================================
// FUNC:	isEmpty
// PARM:	input - ref to form input
//=========================================================
function isEmpty(input) {

	switch (input.type) {									// requires 4.0+ browsers
		case "text" 			: 
		case "textarea" 		: 
		case "hidden"			: 
		case "password" 		: 
		case "file"				: 
		case "submit"			: 
		case "reset"			: for(i=0;i<input.value.length;i++) { if(input.value.charAt(i) != " ") return false; } return true;
		case "checkbox" 		: return (input.checked);
		case "radio"			: return (getRadioIndex(input) == -1);
		case "select-one"		:
		case "select-multiple"	: return (input.selectedIndex == -1);
		default					: alert("isEmpty: input type not supported."); return null;	
	}
}


//=========================================================
// FUNC:	listFind
// PARM:	list, str {string to search for}, delim {opt}
//=========================================================
function listFind() {
	if(!listFind.arguments[0] || !listFind.arguments[1]) {
		alert("listFind: 2 or 3 parameters expected");
		return null;
	}
	
	list = listFind.arguments[0];
	str =  listFind.arguments[1];
	if(listFind.arguments[2]) delim = listFind.arguments[2];
	else delim = ","
	
	arrList = list.split(delim);
	
	for(i=0; i < arrList.length; i++) {
		if(str == arrList[i]) return true;
	}
	
	return false;
	
}


//_________________________________________________________________________________________________
//||[ DATE ]|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

//=========================================================
// FUNC:	isValidMonthDay
// PARM:	month	- int - 1-based
//			day		- int
//=========================================================
function isValidMonthDay(month,day) {

	month = parseInt(month,10);
	day = parseInt(day,10);

	switch (month) {
	// 31
		case 1:
		case 3:
		case 5:
		case 7:
		case 8:
		case 10:
		case 12: return (day <= 31);
	// 30
		case 4:
		case 6:
		case 9:
		case 11: return (day <= 30);
	//29 
		case 2: return (isLeapYear())?(day <= 29):(day <=28);
		default: alert("isValidMonthDay: invalid month"); return null;
	}
}

//=========================================================
// FUNC:	isLeapYear
//=========================================================
function isLeapYear() {
	
	var today, year;
	
	if(isLeapYear.arguments[0]) {
		year = isLeapYear.arguments[0];
	}
	else {
		today = new Date();
		year = today.getYear()
	}
	
	
	return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
	
}

//=========================================================
// FUNC:	isDay
// PARM:	input - ref to form input
//=========================================================
function isDay(input) {

	var value = getValue(input);
	
	return (isPattern(input,"99") || isPattern(input,"9")) && ((value >= 1) && (value <= 31));
}

//=========================================================
// FUNC:	isMonth
// PARM:	input - ref to form input
//=========================================================
function isMonth(input) {

	var value = getValue(input);

	return (isPattern(input,"99") || isPattern(input,"9")) && ((value >= 1) && (value <= 12));
}

//=========================================================
// FUNC:	isYear
// PARM:	input - ref to form input
//=========================================================
function isYear(input) {

	return isPattern(input,"9999");
}

//_________________________________________________________________________________________________
//||[ PHONE ]||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

//=========================================================
// FUNC:	isPhone
// PARM:	input - ref to form input
//=========================================================
function isPhone(input){ 

var val = isPattern(input,"(999) 999-9999") || 
		isPattern(input,"(999) 999.9999") ||
		isPattern(input,"999 999 9999") ||
		isPattern(input,"999-999-9999") || 
		isPattern(input,"999.999.9999") || 
		isPattern(input,"9999999999") ||
		isPattern(input,"999-9999") ||
		isPattern(input,"999.9999") ||
		isPattern(input,"999 9999") ||
		isPattern(input,"9999999");
	return val;
}

//=========================================================
// FUNC:	isPhone3
// PARM:	input - ref to form input
//=========================================================
function isPhone3(input) { 
	return isPattern(input,"999");
}

//=========================================================
// FUNC:	isPhone4
// PARM:	input - ref to form input
//=========================================================
function isPhone4(input) {
	return isPattern(input,"9999");
}

//=========================================================
// FUNC:	isPhoneAreaCode
// PARM:	input - ref to form input
//=========================================================
function isPhoneAreaCode(input) {
	return isPattern(input,"999") || isPattern(input,"(999)");
}

//=========================================================
// FUNC:	formatPhone
// PARM"	input - ref to form input
//=========================================================
function formatPhone(input) {
	switch (input.value.length) {
		case 7:  input.value = input.value.substring(0,3) + "-" + input.value.substring(3);
				 break;
		case 8:  input.value = input.value.substring(0,3) + "-" + input.value.substring(4);
				 break;
		case 10: input.value = "(" + input.value.substring(0,3) + ") " + input.value.substring(3,6) + "-" + input.value.substring(6);
				 break;
		case 12: input.value = "(" + input.value.substring(0,3) + ") " + input.value.substring(4,7) + "-" + input.value.substring(8);
				 break;
		default:
   	}
}

//=========================================================
// FUNC:	formatPhoneNumeric
// PARM"	input - ref to form input
//=========================================================
function formatPhoneNumeric(input) {
	input.value = stripToAlphaNumeric(getValue(input));
	return;
}

//__________________________________________________________________________________________________
//||[Username]||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

//=========================================================
// FUNC:	isThereSpaces
// PARM"	input - ref to form input
//=========================================================
function isThereSpaces(input) {
	if(input.value.indexOf(" ") != -1) return true;
	else return false;
}

//=========================================================
// FUNC:	isOnlyLetters
// PARM"	input - ref to form input
//=========================================================
function isOnlyLetters(input) {
	var boo = true;
	var value = getValue(input);
	
	if (window.RegExp) {
			for(i=0; i < value.length; i++) {
			
			var regValid = /\w/

			boo = regValid.test(value.charAt(i));
			if (!boo) return boo;
		}
	}
	return boo;
}

//=========================================================
// FUNC:	isUsername
// PARM"	input - ref to form input
//=========================================================
function isUsername(input) {
	var boo = true;
	boo = isThereSpaces(input);
	boo = isOnlyLetters(input);
	return boo;
}

//_________________________________________________________________________________________________
//||[ ADDRESS ]||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

//=========================================================
// FUNC:	isStateAbbrev
// PARM:	input - ref to form input
//=========================================================
function isStateAbbrev(input) {
	return isPattern(input,"aa");
}

//=========================================================
// FUNC:	isZip4
// PARM:	input - ref to form input
//=========================================================
function isZip4(input) {
	return isPattern(input,"9999");
}

//=========================================================
// FUNC:	isZip
// PARM:	input - ref to form input
//=========================================================
function isZip(input) {
	return isPattern(input,"99999") || 
			isPattern(input,"99999-9999") ||
			isPattern(input,"a9a9a9") ||
			isPattern(input,"a9a 9a9");
}

//=========================================================
// FUNC:	isEmail
// PARM:	input - ref to form input
//=========================================================
function isEmail(input) {
	value = getValue(input);
	
	if (window.RegExp) {
	
		var regValid = /^\w+((-\w+)|(\.\w+))*\@\w+((\.|-)\w+)*\.([a-zA-Z]){2,3}$/

		return regValid.test(value);
		
	}
	else {
	
		var ndxAt = value.indexOf("@");
		var ndxPeriod = value.lastIndexOf(".");
		
		return ((ndxAt > -1) && (ndxPeriod > -1) && (ndxAt < ndxPeriod));
	}
}

//_________________________________________________________________________________________________
//||[ CREDIT CARD ]||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

//=========================================================
// FUNC:	isCreditCard
// PARM:	input - ref to form element
//=========================================================
function isCreditCard(input) {
	var value = getValue(input);
	
	return (
		(parseInt(value,10) == value) && (
			isMASTERCARD(value) ||
			isVISA(value) ||
			isAMEX(value) ||
			isDINERS(value) ||
			isDISCOVER(value)
		)
	);
}

//=========================================================
// FUNC:	isMASTERCARD
// PARM:	cardNumber - 16 digit string of numerals
//=========================================================
function isMASTERCARD(cardNumber) {
	return (
			(cardNumber.length==16) &&
			(parseInt(cardNumber,10) == cardNumber) &&
			(cardNumber.substring(0,2) >= 51) &&
			(cardNumber.substring(0,2) <= 55) &&
			luhn(cardNumber)
	);
}

//=========================================================
// FUNC:	isVISA
// PARM:	cardNumber - 13,16 digit string of numerals
//=========================================================
function isVISA(cardNumber) {
	return (
			((cardNumber.length==13) || (cardNumber.length==16)) && 
			(parseInt(cardNumber,10) == cardNumber) &&
			(cardNumber.charAt(0) == "4") &&
			luhn(cardNumber)
	);
}

//=========================================================
// FUNC:	isAMEX
// PARM:	cardNumber - 15 digit string of numerals
//=========================================================
function isAMEX(cardNumber) {
	return (
			(cardNumber.length==15) &&
			(parseInt(cardNumber,10) == cardNumber) &&
			(
				(cardNumber.substring(0,2) == 34) || 
				(cardNumber.substring(0,2) == 37)
			) &&
			luhn(cardNumber)
	);
}

//=========================================================
// FUNC:	isDINERS
// PARM:	cardNumber - 14 digit string of numerals
// NOTE:	Also supports Carte Blanche
//=========================================================
function isDINERS(cardNumber) {
	return (
			(cardNumber.length==14) &&
			(parseInt(cardNumber,10) == cardNumber) &&
			(
				((cardNumber.substring(0,3) >= 300) && (cardNumber.substring(0,3) <= 305)) ||
				(cardNumber.substring(0,2) == 36) ||
				(cardNumber.substring(0,2) == 38)
			) &&
			luhn(cardNumber)
	);
}

//=========================================================
// FUNC:	isDISCOVER
// PARM:	cardNumber - 16 digit string of numerals
//=========================================================
function isDISCOVER(cardNumber) {
	return (
			(cardNumber.length==16) &&
			(parseInt(cardNumber,10) == cardNumber) &&
			(cardNumber.substring(0,4)=="6011") && 
			luhn(cardNumber)
	);
}

//=========================================================
// FUNC:	luhn
// PARM:	cardNumber - 13-16 digit string of numbers
//
// Performs a luhn integrity test against the given cc#
//=========================================================
function luhn(cardNumber) {
	var digit;
	var total = 0;
	
	if (parseInt(cardNumber,10) != cardNumber) return false;
	
	for (var i=0; i < cardNumber.length; i++) {
		digit = cardNumber.charAt(cardNumber.length-i-1);
		digit *= ((i % 2) + 1);
		digit = digit + "";
		
		for (var j=0; j < digit.length; j++)
			total += parseInt(digit.charAt(j),10);
	}
	
	return ((total % 10) == 0);
}

//=========================================================
// FUNC:	isValidExpiration
// PARM:	month - (1-based int)
//			year  - (int)
//=========================================================
function isValidExpiration(month,year) {
	var now = new Date();
	
	if (year < now.getFullYear()) return false;
	if ((year == now.getFullYear()) && (month < (now.getMonth()+1))) return false;
	
	return true;
}
