var reWhitespace = /^\s+$/
var reLetter = /^[a-zA-Z]$/
var reAlphabetic = /^[a-zA-Z\s]+$/
var reAlphanumeric = /^[a-zA-Z0-9\s]+$/
var reName = /^[a-zA-Z\s-]+$/
var reDigit = /^\d/
var reLetterOrDigit = /^([a-zA-Z]|\d)$/
var reInteger = /^\d+$/
var reSignedInteger = /^(-)?\d+$/
var reFloat = /^((\d+(\.\d*)?)|((\d*\.)?\d+))$/
var reSignedFloat = /^(((-)?\d+(\.\d*)?)|((-)?(\d*\.)?\d+))$/
var reEmail = /^.+\@.+\..+$/

var digits = "0123456789";
var currency = "$,";
var whitespace = " \t\n\r";
var phoneNumberDelimiters = "()- ";
var SSNDelimiters = "- ";
var digitsInSocialSecurityNumber = 9;
var digitsInUSPhoneNumber = 10;
var ZIPCodeDelimiters = "-";
var PostalCodeDelimiters = " ";
var digitsInZIPCode1 = 5
var digitsInZIPCode2 = 9
var digitsInCanadaAirMilesNumber = 11;

var iZIPCode = "Zip code must be a 5 or 9 digit value (like 94043 or 94043-6541)."
var iPostalCode = "Postal code must be a 6 characters in length or formatted correctly (like A1B 2C3)."
var iUSPhone = "Phone number must be 10 digits (like 415-555-1212)."
var iWorldPhone = "This field must be a valid international phone number."
var iSSN = "Social security number must be a 9 digit number (like 123-45-6789)."
var iSIN = "Social insurance number must be a 9 digit number (like 123-456-789)."
var iFederalTaxID = "Federal Tax ID must be a 9 digit number (like 12-3456789)."
var iEmail = "Email address must be valid (like name@domain.com)."
var iName = "Field can only contain english letters, a space or a dash"
var iNumber = "Field must be a valid number."
var iPositiveInteger = "Field must be a valid positive integer."
var iDay = "Date of birth day must be between 1 and 31."
var iMonth = "Date of birth month must be between 1 and 12."
var iYear = "Date of birth year must be a 4 digit year (like 1999)."
var iDate = "Date of birth must be a valid date (like 11/15/2000)."
var iCanadaAirMilesNumber = "Air Miles Number must be a 11 digit number (like 4151-555-1212)."

var defaultEmptyOK = false;
var whitespace = " \t\n\r";

// daysInFebruary (INTEGER year)
// 
// Given integer argument year, returns number of days in February of that year.
function daysInFebruary (year) {
	return (((year % 4 == 0) && ((!(year % 100 == 0)) || (year % 400 == 0))) ? 29 : 28 );
}

function makeArray (n) {
	for (var i = 1; i <= n; i++) {
		this[i] = 0;
	} 
	return this;
}
// update for NS6 
var daysInMonth = new Array(12);
daysInMonth[1] = 31;
daysInMonth[2] = 29;
daysInMonth[3] = 31;
daysInMonth[4] = 30;
daysInMonth[5] = 31;
daysInMonth[6] = 30;
daysInMonth[7] = 31;
daysInMonth[8] = 31;
daysInMonth[9] = 30;
daysInMonth[10] = 31;
daysInMonth[11] = 30;
daysInMonth[12] = 31;

function keyValuePair(key, value) {
	this.key = key;
	this.value=value;
}

var charsArray = new Array();
charsArray[0] = new keyValuePair("&quot;", '"');
charsArray[1] = new keyValuePair("&#039;", "'");
charsArray[2] = new keyValuePair("&gt;", ">");
charsArray[3] = new keyValuePair("&lt;", "<");

function convertCharsBack(newValue) {
var result = newValue;
	for (c=0; c<charsArray.length; c++) {
		if (result.indexOf(charsArray[c].key) != -1) {
			result = result.replace(charsArray[c].key, charsArray[c].value);
		} 
	}
	// check recursively to take care of all instances of the above conditions in the string
	for (rec = 0; rec<charsArray.length; rec++) {
		if ((result.indexOf(charsArray[rec].key) != -1)) {
			result = convertCharsBack(result);
		}
	}
	return result;
}

// Removes all characters which appear in string bag from string s.
function stripCharsInBag (s, bag) {
	var i;
    var returnString = "";

    for (i = 0; i < s.length; i++) {   
		var c = s.charAt(i);
        if (bag.indexOf(c) == -1) {
			returnString += c;
		}
	}
    return returnString;
}

// Check whether string s is empty.
function isEmpty (s) {
	return ((s == null) || (s.length == 0));
}

// Returns true if string s is empty or whitespace characters only.
function isWhitespace (s) {
	return (isEmpty(s) || reWhitespace.test(s));
}

// Returns true if character c is an English letter (A .. Z, a..z).
function isLetter (c) {
	return reLetter.test(c);
}

// Returns true if character c is a digit (0 .. 9).
function isDigit (c) {
	return reDigit.test(c);
}

// Returns true if character c is a letter or digit.
function isLetterOrDigit (c) {
	return reLetterOrDigit.test(c);
}

// isInteger (STRING s [, BOOLEAN emptyOK])
// 
// Returns true if all characters in string s are numbers.
//
// Accepts non-signed integers only. Does not accept floating 
// point, exponential notation, etc.
//
// We don't use parseInt because that would accept a string
// with trailing non-numeric characters.
//
// By default, returns defaultEmptyOK if s is empty.
// There is an optional second argument called emptyOK.
// emptyOK is used to override for a single function call
//      the default behavior which is specified globally by
//      defaultEmptyOK.
// If emptyOK is false (or any value other than true), 
//      the function will return false if s is empty.
// If emptyOK is true, the function will return true if s is empty.
//
// EXAMPLE FUNCTION CALL:     RESULT:
// isInteger ("5")            true 
// isInteger ("")             defaultEmptyOK
// isInteger ("-5")           false
// isInteger ("", true)       true
// isInteger ("", false)      false
// isInteger ("5", false)     true

function isInteger (s) {
	if (isEmpty(s)) {
		if (isInteger.arguments.length == 1) {
			return defaultEmptyOK;
		} else {
			return (isInteger.arguments[1] == true);
		}
	}
	return reInteger.test(s);
}

// isSignedInteger (STRING s [, BOOLEAN emptyOK])
// 
// Returns true if all characters are numbers; 
// first character is allowed to be + or - as well.
//
// Does not accept floating point, exponential notation, etc.
//
// We don't use parseInt because that would accept a string
// with trailing non-numeric characters.
//
// EXAMPLE FUNCTION CALL:          RESULT:
// isSignedInteger ("5")           true 
// isSignedInteger ("")            defaultEmptyOK
// isSignedInteger ("-5")          true
// isSignedInteger ("+5")          true
// isSignedInteger ("", false)     false
// isSignedInteger ("", true)      true

function isSignedInteger (s) {
	if (isEmpty(s)) {
		if (isSignedInteger.arguments.length == 1) {
			return defaultEmptyOK;
		} else {
			return (isSignedInteger.arguments[1] == true);
		}
	}
	return reSignedInteger.test(s);
}

// isPositiveInteger (STRING s [, BOOLEAN emptyOK])
// Returns true if string s is an integer > 0.
function isPositiveInteger (s) {
	var secondArg = defaultEmptyOK;

    if (isPositiveInteger.arguments.length > 1) {
		secondArg = isPositiveInteger.arguments[1];
	}
    return (isSignedInteger(s, secondArg)
         && ( (isEmpty(s) && secondArg)  || (parseInt (s, 10) > 0) ) );
}

// isNonnegativeInteger (STRING s [, BOOLEAN emptyOK])
// Returns true if string s is an integer >= 0.
function isNonnegativeInteger (s) {
	var secondArg = defaultEmptyOK;

    if (isNonnegativeInteger.arguments.length > 1) {
		secondArg = isNonnegativeInteger.arguments[1];
	}
	return (isSignedInteger(s, secondArg)
         && ( (isEmpty(s) && secondArg)  || (parseInt (s, 10) >= 0) ) );
}

// isNegativeInteger (STRING s [, BOOLEAN emptyOK])
// Returns true if string s is an integer < 0.
function isNegativeInteger (s) {
	var secondArg = defaultEmptyOK;

    if (isNegativeInteger.arguments.length > 1) {
		secondArg = isNegativeInteger.arguments[1];
	}
	return (isSignedInteger(s, secondArg)
         && ( (isEmpty(s) && secondArg)  || (parseInt (s, 10) < 0) ) );
}

// isNonpositiveInteger (STRING s [, BOOLEAN emptyOK])
// Returns true if string s is an integer <= 0.
function isNonpositiveInteger (s) {
	var secondArg = defaultEmptyOK;

    if (isNonpositiveInteger.arguments.length > 1) {
        secondArg = isNonpositiveInteger.arguments[1];
	}
    return (isSignedInteger(s, secondArg)
         && ( (isEmpty(s) && secondArg)  || (parseInt (s, 10) <= 0) ) );
}

// isFloat (STRING s [, BOOLEAN emptyOK])
// 
// True if string s is an unsigned floating point (real) number. 
//
// Also returns true for unsigned integers. If you wish
// to distinguish between integers and floating point numbers,
// first call isInteger, then call isFloat.
function isFloat (s) {
	if (isEmpty(s)) {
		if (isFloat.arguments.length == 1) {
			return defaultEmptyOK;
		} else {
			return (isFloat.arguments[1] == true);
		}
	}
    return reFloat.test(s);
}

// isSignedFloat (STRING s [, BOOLEAN emptyOK])
// 
// True if string s is a signed or unsigned floating point 
// (real) number. First character is allowed to be + or -.
//
// Also returns true for unsigned integers. If you wish
// to distinguish between integers and floating point numbers,
// first call isSignedInteger, then call isSignedFloat.
function isSignedFloat (s) {
	if (isEmpty(s)) {
		if (isSignedFloat.arguments.length == 1) {
			return defaultEmptyOK;
		} else {
			return (isSignedFloat.arguments[1] == true);
		}
	}
       return reSignedFloat.test(s);
}

// isAlphabetic (STRING s [, BOOLEAN emptyOK])
// Returns true if string s is English letters (A .. Z, a..z) only.
function isAlphabetic (s) {
	if (isEmpty(s)) {
		if (isAlphabetic.arguments.length == 1) {
			return defaultEmptyOK;
		} else {
			return (isAlphabetic.arguments[1] == true);
		}
	}
	return reAlphabetic.test(s);
}

// isAlphanumeric (STRING s [, BOOLEAN emptyOK])
// Returns true if string s is English letters (A .. Z, a..z) and numbers only.
function isAlphanumeric (s) {
	if (isEmpty(s)) {
		if (isAlphanumeric.arguments.length == 1) {
			return defaultEmptyOK;
		} else {
			return (isAlphanumeric.arguments[1] == true);
		}
	}
	return reAlphanumeric.test(s);
}

// isSSN (STRING s [, BOOLEAN emptyOK])
// 
// isSSN returns true if string s is a valid U.S. Social
// Security Number.  Must be 9 digits.
// NOTE: Strip out any delimiters (spaces, hyphens, etc.)
// from string s before calling this function.
function isSSN (s) {
	if (isEmpty(s)) {
		if (isSSN.arguments.length == 1) {
			return defaultEmptyOK;
		} else {
			return (isSSN.arguments[1] == true);
		}
	}
	return (isInteger(s) && s.length == digitsInSocialSecurityNumber);
}

// isSIN (STRING s [, BOOLEAN emptyOK])
// 
// isSIN returns true if string s is a valid U.S. Social
// Security Number.  Must be 9 digits.
// NOTE: Strip out any delimiters (spaces, hyphens, etc.)
// from string s before calling this function.
function isSIN (s) {
	if (isEmpty(s)) {
		if (isSIN.arguments.length == 1) {
			return defaultEmptyOK;
		} else {
			return (isSIN.arguments[1] == true);
		}
	}
	return (isInteger(s) && s.length == digitsInSocialSecurityNumber);
}

// isTaxID (STRING s [, BOOLEAN emptyOK])
// 
// isTaxID returns true if string s is a valid U.S. Tax ID Number.
// Must be 9 digits.
// NOTE: Strip out any delimiters (spaces, hyphens, etc.)
// from string s before calling this function.
function isTaxID (s) {
	if (isEmpty(s)) {
		if (isTaxID.arguments.length == 1) {
			return defaultEmptyOK;
		} else {
			return (isTaxID.arguments[1] == true);
		}
	}
    return (isAlphanumeric(s) && s.length == digitsInSocialSecurityNumber);
}

// isUSPhoneNumber (STRING s [, BOOLEAN emptyOK])
// 
// isUSPhoneNumber returns true if string s is a valid U.S. Phone
// Number.  Must be 10 digits.
// NOTE: Strip out any delimiters (spaces, hyphens, parentheses, etc.)
// from string s before calling this function.
function isUSPhoneNumber (s) {
	if (isEmpty(s)) {
		if (isUSPhoneNumber.arguments.length == 1) {
			return defaultEmptyOK;
		} else {
			return (isUSPhoneNumber.arguments[1] == true);
		}
	}
    return (isInteger(s) && s.length == digitsInUSPhoneNumber);
}

// isInternationalPhoneNumber (STRING s [, BOOLEAN emptyOK])
// 
// isInternationalPhoneNumber returns true if string s is a valid 
// international phone number.  Must be digits only; any length OK.
// May be prefixed by + character.
// NOTE: A phone number of all zeros would not be accepted.
// I don't think that is a valid phone number anyway.
// NOTE: Strip out any delimiters (spaces, hyphens, parentheses, etc.)
// from string s before calling this function.  You may leave in 
// leading + character if you wish.
function isInternationalPhoneNumber (s) {
	if (isEmpty(s)) {
		if (isInternationalPhoneNumber.arguments.length == 1) {
			return defaultEmptyOK;
		} else {
			return (isInternationalPhoneNumber.arguments[1] == true);
		}
	}
    return (isPositiveInteger(s));
}

// isZIPCode (STRING s [, BOOLEAN emptyOK])
// 
// isZIPCode returns true if string s is a valid 
// U.S. ZIP code.  Must be 5 or 9 digits only.
// NOTE: Strip out any delimiters (spaces, hyphens, etc.)
// from string s before calling this function.  
function isZIPCode (s) {
	if (isEmpty(s)) {
		if (isZIPCode.arguments.length == 1) {
			return defaultEmptyOK;
		} else {
			return (isZIPCode.arguments[1] == true);
		}
	}
	return (isInteger(s) && ((s.length == digitsInZIPCode1) || (s.length == digitsInZIPCode2)));
}

// isEmail (STRING s [, BOOLEAN emptyOK])
// 
// Email address must be of form a@b.c -- in other words:
// * there must be at least one character before the @
// * there must be at least one character before and after the .
// * the characters @ and . are both required
function isEmail (s) {
	if (isEmpty(s)) {
		if (isEmail.arguments.length == 1) {
			return defaultEmptyOK;
		} else {
			return (isEmail.arguments[1] == true);
		}
    }
	return reEmail.test(s);
}

// isName (STRING s [, BOOLEAN emptyOK])
// Returns true if string s is English letters, space or a dash (A .. Z, a..z, space, -) only.
function isName (s) {
	if (isEmpty(s)) {
		if (isName.arguments.length == 1) {
			return defaultEmptyOK;
		} else {
			return (isName.arguments[1] == true);
		}
	}
	return reName.test(s);
}

// isIntegerInRange (STRING s, INTEGER a, INTEGER b [, BOOLEAN emptyOK])
// 
// isIntegerInRange returns true if string s is an integer 
// within the range of integer arguments a and b, inclusive.
function isIntegerInRange (s, a, b) {
	if (isEmpty(s)) {
		if (isIntegerInRange.arguments.length == 1) {
			return defaultEmptyOK;
		} else {
			return (isIntegerInRange.arguments[1] == true);
		}
	}
    if (!isInteger(s, false)) {
		return false;
	}
	var num = parseInt (s, 10);
    return ((num >= a) && (num <= b));
}

// isYear (STRING s [, BOOLEAN emptyOK])
// 
// isYear returns true if string s is a valid 
// Year number.  Must be 2 or 4 digits only.
function isYear (s) {
	if (isEmpty(s)) {
		if (isYear.arguments.length == 1) {
			return defaultEmptyOK;
		} else {
			return (isYear.arguments[1] == true);
		}
	}
	if (!isNonnegativeInteger(s)) {
		return false;
	}
	return (s.length == 4);
}

// isMonth (STRING s [, BOOLEAN emptyOK])
// 
// isMonth returns true if string s is a valid 
// month number between 1 and 12.
function isMonth (s) {
	if (isEmpty(s)) {
		if (isMonth.arguments.length == 1) {
			return defaultEmptyOK;
		} else {
			return (isMonth.arguments[1] == true);
		}
	}
    return isIntegerInRange (s, 1, 12);
}

// isDay (STRING s [, BOOLEAN emptyOK])
// 
// isDay returns true if string s is a valid 
// day number between 1 and 31.
function isDay (s) {
	if (isEmpty(s)) {
		if (isDay.arguments.length == 1) {
			return defaultEmptyOK;
		} else {
			return (isDay.arguments[1] == true);   
		}
	}
	return isIntegerInRange (s, 1, 31);
}

// isDate (STRING year, STRING month, STRING day)
//
// isDate returns true if string arguments year, month, and day form a valid date.
function isDate (year, month, day) {
	if (!(isYear(year, false) && isMonth(month, false) && isDay(day, false))) {
		return false;
	}

    var intYear = parseInt(year, 10);
    var intMonth = parseInt(month, 10);
    var intDay = parseInt(day, 10);

    if (intDay > daysInMonth[intMonth]) {
		return false;
	}

    if ((intMonth == 2) && (intDay > daysInFebruary(intYear))) {
		return false;
	}
	return true;
}

// true if numeric value is currency (e.g. 1.23. 152, not 1.256)
function isCurrency(val){
	if (isNaN(Number(val))){
		return false;
	}
	if (val.indexOf('.') != -1){
		var decimalIndex = val.indexOf('.');
		if (val.length - decimalIndex > 3){
			return false;
		}
	}
	return true;
}

// isCanadaAirMilesNumber (STRING s [, BOOLEAN emptyOK])
// 
// isCanadaAirMilesNumber returns true if string s is a valid Canada Air Miles Number.  
// Must be 11 digits.
function isCanadaAirMilesNumber (s)
{   if (isEmpty(s)) 
	if (isCanadaAirMilesNumber.arguments.length == 1){
	       return defaultEmptyOK;
	}
	else {
		return (isCanadaAirMilesNumber.arguments[1] == true);
	}
    return (isInteger(s) && s.length == digitsInCanadaAirMilesNumber)
}