// Form Validation Functions  v1.1.8
// documentation: http://www.dithered.com/javascript/form_validation/index.html
// license: http://creativecommons.org/licenses/by/1.0/
// code by Chris Nott (chris[at]dithered[dot]com)
//
// Modifications made by Sharp HealthCare Web Development Team
// 	- Jesse Marinez - added a regular expression match for text input
//	- Jesse Marinez - removed some built-in patterns for non-US phone and postal code matches
//	- Jesse Marinez - added a password pattern for Sharp's password requirements
//	- Jesse Marinez - added a few more comments and made some formatting changes
//  - 2006 Aug 8 - Jesse Marinez - changed pattern to patterntype to resolve conflict with webforms 2.0
//  - 2007 Apr 24 - Jesse Marinez - added an autotab function (from http://www.rgagnon.com/jsdetails/js-0113.html)
//  - 2010 Sep 08 - Johnson Ssuai - added year validation conditions.
// Sharp HealthCare To Do:
//	- find a way to send ignoreWhitespace flags to functions that accept it
//  - 2011 June 14 - David Luu - Added checkbox condition in getFormerrors

function autoTab(currentField, toField)
{
	// autotab code from Real's how-to (http://www.rgagnon.com/jsdetails/js-0113.html)
	if (currentField.getAttribute && currentField.value.length==currentField.getAttribute("maxlength")) 
		{ toField.focus() }
}

function getFormErrors(form)
{
	var errors = new Array();

	// loop thru all form elements
	for (var elementIndex = 0; elementIndex < form.elements.length; elementIndex++)
	{
		var element = form.elements[elementIndex];
		
		// if using pattern, set to patterntype
		if (element.pattern) {element.patterntype = element.pattern}
		if (element.patternError) {element.patterntypeError = element.patternError}
		
		// check box
		if (element.type == "checkbox")
		{
			// required element
			if (element.required && !element.checked)
				{ errors[errors.length] = element.requiredError; }
		}
		
		// text and textarea types
		if (element.type == "text" || element.type == "textarea" || element.type == "hidden")
		{
			element.value = trimWhitespace(element.value)
			
			// required element
			if (element.required  && element.value == '') 
				{ errors[errors.length] = element.requiredError; }
			
			// maximum length
			else if (element.maxlength && isValidLength(element.value, 0, element.maxlength) == false)
				{ errors[errors.length] = element.maxlengthError; }
			
			// minimum length
			else if (element.minlength && isValidLength(element.value, element.minlength, Number.MAX_VALUE) == false)
				{ errors[errors.length] = element.minlengthError; }
			
			// match a custom regex - JM
			else if (element.regex)
			{
				var regexpattern = new RegExp(element.regex);
				if(!regexpattern.test(element.value)){
					errors[errors.length] = element.regexError;
				}
			}
			
			// pattern
			// can match one of the builtin patterns:
			//		visa, mastercard, american express, discover, credit card,
			//		email, zip code, phone number, alphabetic, numeric, alphanumeric, date
			else if (element.patterntype)
			{
				if
				(
					(element.patterntype.toLowerCase() == 'email' && isValidEmail(element.value) == false) ||
					(element.patterntype.toLowerCase() == 'zip code' && isValidZipcode(element.value) == false) ||
					(element.patterntype.toLowerCase() == 'alphanumeric' && isAlphanumeric(element.value, true) == false) ||
					(element.patterntype.toLowerCase() == 'numeric' && isNumeric(element.value, true) == false) ||
					(element.patterntype.toLowerCase() == 'alphabetic' && isAlphabetic(element.value, true) == false) ||
					(element.patterntype.toLowerCase() == 'date' && 
						((element.day && element.year && 
							isValidDate(element.value, form[element.day].value, form[element.year].value) == false) ||
						 (!element.day && !element.year && isValidDate(element.value) == false))) ||
					
					(element.patterntype.toLowerCase() == 'phone number' && 
						((element.phoneprefix && element.phonesuffix &&
						  isValidPhoneNumber(element.value, form[element.phoneprefix].value, form[element.phonesuffix].value) == false)
						 || (!element.phoneprefix && !element.phonesuffix && isValidPhoneNumber(element.value) == false))) ||
						 
					((element.patterntype.toLowerCase() == 'visa' || element.patterntype.toLowerCase() == 'mastercard' ||
					  element.patterntype.toLowerCase() == 'american express' || element.patterntype.toLowerCase() == 'discover' ||
					  element.patterntype.toLowerCase() == 'credit card') && isValidCreditCard(element.value, element.patterntype) == false ) )
				{
					errors[errors.length] = element.patterntypeError;
				}
			}
		}
		
		// password 
		else if (element.type == "password") 
		{
			// required element
			if (element.required  && element.value == '')
            	{ errors[errors.length] = element.requiredError; }
			
			// maximum length (for Sharp passwords use pattern below instead which checks length)
			else if (element.maxlength && isValidLength(element.value, 0, element.maxlength) == false)
				{ errors[errors.length] = element.maxLengthError; }
			
			// minimum length (for Sharp passwords use pattern below instead which checks length)
			else if (element.minlength && isValidLength(element.value, element.minlength, Number.MAX_VALUE) == false)
				{ errors[errors.length] = element.minLengthError; }
			
			// if password needs verifying, value of element.verify holds the 2nd password field name
			else if (element.verify && element.value != form[element.verify].value)
			{
				errors[errors.length] = element.verifyError;
				element.value = "";
				form[element.verify].value = "";
			}
			
			// pattern type
			else if (element.patterntype)
			{
				if (element.patterntype.toLowerCase() == 'sharp password' && isSharpPassword(element.value) == false)
				{
					errors[errors.length] = element.patterntypeError;
					element.value = "";
					form[element.verify].value = "";
				}
			}
		}
		
			
		// file upload
		if (element.type == "file")
		{
			// required element
			if (element.required  && element.value == '')
				{ errors[errors.length] = element.requiredError; }
			// match a custom regex - CM 01/02/2009
			else if (element.regex && !element.regex.test(element.value))
				{ errors[errors.length] = element.regexError; }
		}
		
		// select
		else if (element.type == "select-one" || element.type == "select-multiple" || element.type == "select")
		{
			// required element
			if (element.required && element.selectedIndex == -1)
				{ errors[errors.length] = element.requiredError; }
			
			// disallow empty value selection
			else if (element.disallowEmptyValue && element.options[element.selectedIndex].value == '')
				{ errors[errors.length] = element.disallowEmptyValueError; }
		}
		
		// radio buttons
		else if (element.type == "radio")
		{
			var radiogroup = form.elements[element.name];
			
			// required element
			if (radiogroup.length && radiogroup[0] && radiogroup[0].required)
			{
				var checkedRadioButton = -1;
				for (var radioIndex = 0; radioIndex < radiogroup.length; radioIndex++)
				{
					if (radiogroup[radioIndex].checked == true)
					{
						checkedRadioButton = radioIndex;
						break;
					}
				}
				
				// show error if required and flag group as having been tested
				if (checkedRadioButton == -1 && !radiogroup.tested)
				{
					errors[errors.length] = radiogroup[0].requiredError;
					radiogroup.tested = true;
				}
				
				// last radio button in group? reset tested flag
				if (element == radiogroup[radiogroup.length - 1])
					{ radiogroup.tested = false; }
			}

			// commented out as it caused a weird problem and seems to work without it - Jesse Marinez
			//radiogroup = null;
		}
	}
	
	return errors;
}

// Check that the number of characters in a string is between a max and a min
function isValidLength(string, min, max)
{
	if (string.length < min || string.length > max) return false;
	else return true;
}

// Check that a credit card number is valid based using the LUHN formula (mod10 is 0)
function isValidCreditCard(number)
{
	// turn number into a string - JM
	number = '' + number;
	
	if (number.length > 16 || number.length < 13 ) return false;
	else if (getMod10(number) != 0) return false;
	else if (arguments[1]) 	// a card type was also passed
	{
		var type = arguments[1];
		var first2digits = number.substring(0, 2);
		var first4digits = number.substring(0, 4);
		
		if      (type.toLowerCase() == 'visa' && number.substring(0, 1) == 4 && (number.length == 16 || number.length == 13 )) return true;
		else if (type.toLowerCase() == 'mastercard' && number.length == 16 &&
				(first2digits == '51' || first2digits == '52' || first2digits == '53' || first2digits == '54' || first2digits == '55')) return true;
		else if (type.toLowerCase() == 'american express' && number.length == 15 && (first2digits == '34' || first2digits == '37')) return true;
		else if (type.toLowerCase() == 'discover' && number.length == 16 && first4digits == '6011') return true;
		// uncomment these as needed - JM
		// else if (type.toLowerCase() == 'diners club' && number.length == 14 && (first2digits == '30' || first2digits == '36' || first2digits == '38')) return true;
		// else if (type.toLowerCase() == 'enroute' && number.length == 15 && (first4digits == '2014' || first4digits == '2149')) return true;
		// else if (type.toLowerCase() == 'jcb' && number.length == 16 &&
		//		   (first4digits == '3088' || first4digits == '3096' || first4digits == '3112' || first4digits == '3158' || first4digits == '3337' || first4digits == '3528')) return true;
		
		else return true;	// if not one of the enumerated types above, assume true since it passed mod10 - JM
	}
	
	else return true;
}

// Check that an email address is valid based on RFC 821 (?)
function isValidEmail(address)
{
	var emailRegex = /^[-'_~\+\w]+(\.[-'_~\+\w]+)*@(([-_\w]+\.)+[a-zA-Z]{2,7})$/;
	if (address != '' && !emailRegex.test(address)) return false;
	
	// allow empty strings to return true - screen these with either a 'required' test or a 'length' test
	else return true;
}

// Check that a US zip code is valid
function isValidZipcode(zipcode)
{
	zipcode = removeSpaces(zipcode);
	if (!(zipcode.length == 5 || zipcode.length == 9 || zipcode.length == 10)) return false;
	if ((zipcode.length == 5 || zipcode.length == 9) && !isNumeric(zipcode)) return false;
	if (zipcode.length == 10 && zipcode.search && zipcode.search(/^\d{5}-\d{4}$/) == -1) return false;
	return true;
}

// Check that a date is valid
function isValidDate(argMonth, argDay, argYear)
{
	// placeholders for our values
	var month;
	var day;
	var year;
	
	if (arguments.length == 1)	
	{
		var strDate = arguments[0];
		// allow empty strings to return true - screen these with either a 'required' test or a 'length' test
		if (strDate.length == 0)
			{ return true; }

		// if only one argument, we are expecting a string formatted as mm/dd/yyyy
		var dateregex = /^\d{1,2}\/\d{1,2}\/\d{4}$/;
		if ( !dateregex.test(strDate) )
			{ return false;	}
		
		// split the string into an array
		var date;
		arrDate = strDate.split("/");
		// assign the array parts to our placeholders
		month = arrDate[0];
		day = arrDate[1];
		year = arrDate[2];
	}
	else if (arguments.length == 3)
	{
		month = arguments[0];	// when month, day and year are in separate fields, passed as 3 arguments
		day = arguments[1];
		year = arguments[2];

		// allow empty strings to return true - screen these with either a 'required' test or a 'length' test
		if (month.length == 0 && day.length == 0 && year.length == 0)
			{ return true; }
	}
	
	// validate the individual parts
	if (month < 1 || month > 12) {
		return false;
	}
	if (day < 1 || day > 31) {
		return false;
	}
	if ((month==4 || month==6 || month==9 || month==11) && day==31) {
		return false;
	}
	if (month == 2) { // check for february 29th
		var isleap = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
		}
	if (month==2 && day>29) {
		return false;
	   }
   	if (month==2 && day>28 && !isleap) {
		return false;
		}
	if (year.length !==4) {   
		return false;
	   }   
	if (month == "none" || day == "none")	{
		return false;
		}
	if (year > 2155 || year < 1901)	{
		return false;
		}		
	return true;
}

// Check that a phone number is valid
function isValidPhoneNumber(areaCode, prefixNumber, suffixNumber)
{
	if (arguments.length == 1)
	{
		var phoneNumber = arguments[0];
		// allow empty strings to return true - screen these with either a 'required' test or a 'length' test
		if (phoneNumber.length == 0)
			{ return true; }

		phoneNumber = phoneNumber.replace(/\D+/g, '');
		var length = phoneNumber.length;
		if (phoneNumber.length >= 7)
		{
			var areaCode = phoneNumber.substring(0, length-7);				// grab all digits before last 7, if any
			var prefixNumber = phoneNumber.substring(length-7, length-4);	// grab first 3 digits of last 7
			var suffixNumber = phoneNumber.substring(length-4);				// grab last 4 digits
		}
		else return false;
	}
	else if (arguments.length == 3)
	{
		var areaCode = arguments[0];		// when area code, prefix and suffix are in separate fields, passed as 3 arguments
		var prefixNumber = arguments[1];
		var suffixNumber = arguments[2];

		// allow empty strings to return true - screen these with either a 'required' test or a 'length' test
		if (areaCode.length == 0 && prefixNumber.length == 0 && suffixNumber.length == 0)
			{ return true; }
	}
	else return true;
	
	if (areaCode.length != 3 || !isNumeric(areaCode) || prefixNumber.length != 3 || !isNumeric(prefixNumber) || suffixNumber.length != 4 || !isNumeric(suffixNumber))
		{ return false; }
	
	return true;
}

// Check that a string contains only letters, numbers and underscores
function isAlphanumeric(string, ignoreWhiteSpace)
{
	if (string.search)
	{
		if ((ignoreWhiteSpace && string.search(/[^\w\s]/) != -1) || (!ignoreWhiteSpace && string.search(/\W/) != -1))
			{ return false; }
	}
	return true;
}

// Check that a string contains only letters
function isAlphabetic(string, ignoreWhiteSpace)
{
	if (string.search)
	{
		if ((ignoreWhiteSpace && string.search(/[^a-zA-Z\s]/) != -1) || (!ignoreWhiteSpace && string.search(/[^a-zA-Z]/) != -1)) return false;
	}
	return true;
}

// Check that a string contains only numbers
function isNumeric(string, ignoreWhiteSpace)
{
	if (string.search)
	{
		if ((ignoreWhiteSpace && string.search(/[^\d\s]/) != -1) || (!ignoreWhiteSpace && string.search(/\D/) != -1)) return false;
	}
	return true;
}

// Remove characters that might cause security problems from a string 
function removeBadCharacters(string)
{
	if (string.replace)
	{
		string.replace(/[<>\"\'\`%;\)\(&\+]/, '');
	}
	return string;
}

// Remove all spaces from a string
function removeSpaces(string)
{
	var newString = '';
	for (var i = 0; i < string.length; i++)
	{
		if (string.charAt(i) != ' ') newString += string.charAt(i);
	}
	return newString;
}

// Remove leading and trailing whitespace from a string
function trimWhitespace(string)
{
	var newString  = '';
	var substring  = '';
	beginningFound = false;
	
	// copy characters over to a new string
	// retain whitespace characters if they are between other characters
	for (var i = 0; i < string.length; i++)
	{
		// copy non-whitespace characters
		if (string.charAt(i) != ' ' && string.charCodeAt(i) != 9)
		{
			// if the temporary string contains some whitespace characters, copy them first
			if (substring != '')
			{
				newString += substring;
				substring = '';
			}
			newString += string.charAt(i);
			if (beginningFound == false) beginningFound = true;
		}
		
		// hold whitespace characters in a temporary string if they follow a non-whitespace character
		else if (beginningFound == true) substring += string.charAt(i);
	}
	return newString;
}

// Returns a checksum digit for a number using mod 10
function getMod10(number)
{
	// convert number to a string and check that it contains only digits
	// return -1 for illegal input
	number = '' + number;
	number = removeSpaces(number);
	if (!isNumeric(number)) return -1;
	
	// calculate checksum using mod10
	var checksum = 0;
	for (var i = number.length - 1; i >= 0; i--)
	{
		var isOdd = ((number.length - i) % 2 != 0) ? true : false;
		digit = number.charAt(i);
		
		if (isOdd) checksum += parseInt(digit);
		else
		{
			var evenDigit = parseInt(digit) * 2;
			if (evenDigit >= 10) checksum += 1 + (evenDigit - 10);
			else checksum += evenDigit;
		}
	}
	return (checksum % 10);
}

// Check that a string matches Sharp.com's password requirements
function isSharpPassword( string )
{
	if ( isValidLength(string, 6, 14) == false || !/[a-zA-Z]/.test(string) || !/[0-9]/.test(string) ) return false;
	else return true;
}

function displayFormErrors( errors, lang )
{
	if ( lang.toLowerCase() == "es" )
	{
		var errorStart;
		// for spanish special chars in alerts, use octal codes (\xxx)
		errorStart = "No se envi\363 el formulario debido " + ((errors.length > 1) ? "a los" : "al") + " siguiente problema" + ((errors.length > 1) ? "s" : "") + ":\n\n";
		var errorEnd = "\nPor favor corrija " + ((errors.length > 1) ? "los errores" : "el error") + " y reenv\355e el formulario.";
	}
	else
	{
		var errorStart = "The form was not submitted due to the following problem" + ((errors.length > 1) ? "s" : "") + ":\n\n";
		var errorEnd = "\nPlease fix " + ((errors.length > 1) ? "these" : "this") + " problem" + ((errors.length > 1) ? "s" : "") + " and resubmit the form.";
	}
	
	var errorMessage = errorStart;
	for (var errorIndex = 0; errorIndex < errors.length; errorIndex++)
		{ errorMessage += "* " + errors[errorIndex] + "\n"; }
	errorMessage += errorEnd;
	alert(errorMessage);
}


