// BASIC DATA VALIDATION TECHNIQUES
// isWhitespace (s)                    			Check whether string s is empty or whitespace
// isLetter (c)                       			Check whether character c is an English letter 
// isDigit (c)                         			Check whether character c is a digit 
// isLetterOrDigit (c)                			Check whether character c is a letter or digit
// isInteger (s [,eok])                			True if all characters in string s are numbers
// isPositiveInteger (s [,eok])        			True if string s is an integer > 0
// isNonnegativeInteger (s [,eok])     			True if string s is an integer >= 0
// isNegativeInteger (s [,eok])        			True if s is an integer < 0
// isNonpositiveInteger (s [,eok])     			True if s is an integer <= 0
// isAlphabetic (s [,eok])             			True if string s is English letters 
// isAlphanumeric (s [,eok])           			True if string s is English letters and numbers only
// isPhoneNumber (s [,eok])          			True if string s is a valid fax number
// isFaxNumber (s [,eok])						True if string s is valid fax number
// isZIPCode (s [,eok])                			True if string s is a valid U.S. ZIP code.
// isStateCode (s [,eok])              			True if string s is a valid U.S. Postal Code
// isEmail (s [,eok])                  			True if string s is a valid email address
// isYear (s [,eok])                   			True if string s is a valid Year number
// isIntegerInRange (s, a, b [,eok])   			True if string s is an integer between a and b, inclusive
// isMonth (s [,eok])                  			True if string s is a valid month between 1 and 12
// isDay (s [,eok])                    			True if string s is a valid day between 1 and 31
// daysInFebruary (year)               			Returns number of days in February of that year
// isDate (year, month, day)           			True if string arguments form a valid date

// FUNCTIONS TO REFORMAT DATA
// stripWhitespace (s)                 			Removes all whitespace characters from s
// stripInitialWhitespace (s)          			Removes initial (leading) whitespace characters from s
// reformat (TARGETSTRING, STRING, INTEGER...)  Reformats delimter into target string                                  
// reformatZIPCode (ZIPString)         			Reformats as 12345-6789
// reformatPhone (Phone)           				Reformats as (123) 456-789
// reformatFax (Fax)							Reformats as (123) 456-789

// FUNCTIONS TO PROMPT THE USER
// prompt (s)                          			Display prompt string 's' in status bar
// promptEntry (s)                     			Display data entry prompt string 's' in status bar
// warnEmpty (theField, s)             			Notify user that required field theField is empty
// warnInvalid (theField, s)           			Notify user that contents of field theField are invalid

// FUNCTIONS TO CHECK THE CONTENTS OF A FIELD
// checkString (theField, s [,eok])    			Check that theField.value is not empty or all whitespaces
// checkStateCode (theField)           			Check that theField.value is a valid state code
// checkZIPCode (theField [,eok])      			Check that theField.value is a valid zip code
// checkPhone (theField [,eok])					Check that theField.value is a valid phone number
// checkFax (theField, [,eok])					Check that theField.value is a valid fax number
// checkEmail (theField [,eok])        			Check that theField.value is a valid email address

// Digits
var digits = "0123456789";

// Lowercase letters
var lowercaseLetters = "abcdefghijklmnopqrstuvwxyz"

// Uppercase letters
var uppercaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

// Whitespace characters
var whitespace = " \t\n\r";

// Decimal Point
var decimalPointDelimiter = "."

// Non-digit characters allowed in phone number
var phoneNumberDelimiters = "()- ";

// Non-digit characters allowed in fax number
var faxNumberDelimiters = "()- ";

// Valid characters in phone number
var validPhoneChars = digits + phoneNumberDelimiters;

// Valid characters in fax number
var validFaxChars = digits + faxNumberDelimiters;

// Number of digits in phone number
var digitsInPhoneNumber = 10;

// Number of digits in fax number
var digitsInFaxNumber = 10;

// Non-digit characters allowed in zip code
var ZIPCodeDelimiters = "-";

// Preferred delimiter for formatting zip code
var ZIPCodeDelimeter = "-"

// Number of digits in zip code
var digitsInZIPCode1 = 5

// Number of digits in extended zip code
var digitsInZIPCode2 = 9

// Beginning of string
var mPrefix = "Please enter "

// End of string
var mSuffix = " in the appropriate field"

// String
var sFirst = "your first name"
var sLast = "your last name"
var sTitle = "your title"
var sCompany = "your company's name"
var sAddress = "your street address"
var sCity = "your city"
var sStateCode = "your state code"
var sZIPCode = "your zip code"
var sPhone = "your phone number"
var sFax = "four fax number"
var sDateOfBirth = "your date of birth"
var sEmail = "your email address"
var sComments = "some comments"
var sName = "your full name"
var sJob = "a job number"
var sFile = "a file to upload"

// Invalid
var iStateCode = "Please enter a valid two character state abbreviation"
var iZIPCode = "This field must be a 5 or 9 digit zip code"
var iPhone = "Please enter a valid 10 digit phone number"
var iFax = "Please enter a valid 10 digit fax number"
var iEmail = "Please enter a valid email address"

// Prompt
var pEntryPrompt = "Please enter "
var pStateCode = "a 2 character state code"
var pZIPCode = "your zip code"
var pPhone = "your phone number"
var pFax = "your fax number"
var pEmail = "your email address"
var pDay = "a day number between 1 and 31"
var pMonth = "a month number between 1 and 12"
var pYear = "a 2 or 4 digit year number"

// It is OK to leave the field blank, but if not, check it
var defaultEmptyOK = false

// Days in each month
/*var daysInMonth = makeArray(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;*/

// State delimiter
var StateCodeDelimiter = "|";

// Valid state codes
var StateCodes = "AL|AK|AS|AZ|AR|CA|CO|CT|DE|DC|FM|FL|GA|GU|HI|ID|IL|IN|IA|KS|KY|LA|ME|MH|MD|MA|MI|MN|MS|MO|MT|NE|NV|NH|NJ|NM|NY|NC|ND|MP|OH|OK|OR|PW|PA|PR|RI|SC|SD|TN|TX|UT|VT|VI|VA|WA|WV|WI|WY|AE|AA|AE|AE|AP"

// Check for empty string
function isEmpty(s)
{   return ((s == null) || (s.length == 0))
}

// Check for white space or white space characters only
function isWhitespace (s)

{   var i;

    if (isEmpty(s)) return true;

    for (i = 0; i < s.length; i++)
    {   
        var c = s.charAt(i);

        if (whitespace.indexOf(c) == -1) return false;
    }
	
    return true;
}

// 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;
}

// Removes characters not in the string bag 's'
function stripCharsNotInBag (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;
}

// Removes all white spaces
function stripWhitespace (s)

{   return stripCharsInBag (s, whitespace)
}

//Remove initial white spaces
function stripInitialWhitespace (s)

{   var i = 0;

    while ((i < s.length) && charInString (s.charAt(i), whitespace))
       i++;
    
    return s.substring (i, s.length);
}

// Check for letter (A-Z, a-z)
function isLetter (c)
{   return ( ((c >= "a") && (c <= "z")) || ((c >= "A") && (c <= "Z")) )
}

// Check for digit (0-9)
function isDigit (c)
{   return ((c >= "0") && (c <= "9"))
}

// Check for letter or digit
function isLetterOrDigit (c)
{   return (isLetter(c) || isDigit(c))
}



// Check for number
function isInteger (s)

{   var i;

    if (isEmpty(s)) 
       if (isInteger.arguments.length == 1) return defaultEmptyOK;
       else return (isInteger.arguments[1] == true);

    for (i = 0; i < s.length; i++)
    {   
        var c = s.charAt(i);

        if (!isDigit(c)) return false;
    }

    return true;
}

// Check for positive integer
function isPositiveInteger (s)
{   var secondArg = defaultEmptyOK;

    if (isPositiveInteger.arguments.length > 1)
        secondArg = isPositiveInteger.arguments[1];

    return (isSignedInteger(s, secondArg)
         && ( (isEmpty(s) && secondArg)  || (parseInt (s) > 0) ) );
}

// Check for non-negative integer
function isNonnegativeInteger (s)
{   var secondArg = defaultEmptyOK;

    if (isNonnegativeInteger.arguments.length > 1)
        secondArg = isNonnegativeInteger.arguments[1];

    return (isSignedInteger(s, secondArg)
         && ( (isEmpty(s) && secondArg)  || (parseInt (s) >= 0) ) );
}

// Check for negative number
function isNegativeInteger (s)
{   var secondArg = defaultEmptyOK;

    if (isNegativeInteger.arguments.length > 1)
        secondArg = isNegativeInteger.arguments[1];

    return (isSignedInteger(s, secondArg)
         && ( (isEmpty(s) && secondArg)  || (parseInt (s) < 0) ) );
}

// Check for non-positive integer
function isNonpositiveInteger (s)
{   var secondArg = defaultEmptyOK;

    if (isNonpositiveInteger.arguments.length > 1)
        secondArg = isNonpositiveInteger.arguments[1];
		
    return (isSignedInteger(s, secondArg)
         && ( (isEmpty(s) && secondArg)  || (parseInt (s) <= 0) ) );
}

// Check for English alphabet charactger
function isAlphabetic (s)

{   var i;

    if (isEmpty(s)) 
       if (isAlphabetic.arguments.length == 1) return defaultEmptyOK;
       else return (isAlphabetic.arguments[1] == true);

    for (i = 0; i < s.length; i++)
    {   
        var c = s.charAt(i);

        if (!isLetter(c))
        return false;
    }

    return true;
}

// Check for alphanumeric character
function isAlphanumeric (s)

{   var i;

    if (isEmpty(s)) 
       if (isAlphanumeric.arguments.length == 1) return defaultEmptyOK;
       else return (isAlphanumeric.arguments[1] == true);

    for (i = 0; i < s.length; i++)
    {   
        var c = s.charAt(i);

        if (! (isLetter(c) || isDigit(c) ) )
        return false;
    }

    return true;
}

// Reformat a string (phone number, fax number, zip code)
function reformat (s)

{   var arg;
    var sPos = 0;
    var resultString = "";

    for (var i = 1; i < reformat.arguments.length; i++) {
       arg = reformat.arguments[i];
       if (i % 2 == 1) resultString += arg;
       else {
           resultString += s.substring(sPos, sPos + arg);
           sPos += arg;
       }
    }
    return resultString;
}

// Check phone number
function isPhone (s)
{   if (isEmpty(s)) 
       if (isPhone.arguments.length == 1) return defaultEmptyOK;
       else return (isPhone.arguments[1] == true);
    return (isInteger(s) && s.length == digitsInPhoneNumber)
}

// Check fax number
function isFax (s)
{   if (isEmpty(s)) 
       if (isFax.arguments.length == 1) return defaultEmptyOK;
       else return (isFax.arguments[1] == true);
    return (isInteger(s) && s.length == digitsInFaxNumber)
}

// Check zip code
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)))
}

// Check state code
function isStateCode(s)
{   if (isEmpty(s)) 
       if (isStateCode.arguments.length == 1) return defaultEmptyOK;
       else return (isStateCode.arguments[1] == true);
    return ( (StateCodes.indexOf(s) != -1) &&
             (s.indexOf(StateCodeDelimiter) == -1) )
}

// Check email address
function isEmail (s)
{   if (isEmpty(s)) 
       if (isEmail.arguments.length == 1) return defaultEmptyOK;
       else return (isEmail.arguments[1] == true);
   
    if (isWhitespace(s)) return false;
	
    var i = 1;
    var sLength = s.length;

    while ((i < sLength) && (s.charAt(i) != "@"))
    { i++
    }

    if ((i >= sLength) || (s.charAt(i) != "@")) return false;
    else i += 2;

    while ((i < sLength) && (s.charAt(i) != "."))
    { i++
    }

    if ((i >= sLength - 1) || (s.charAt(i) != ".")) return false;
    else return true;
}

// Check for valid year
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 == 2) || (s.length == 4));
}

// Check if string is an integer within the range of a and b
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);
    return ((num >= a) && (num <= b));
}

// Check for valid month
function isMonth (s)
{   if (isEmpty(s)) 
       if (isMonth.arguments.length == 1) return defaultEmptyOK;
       else return (isMonth.arguments[1] == true);
    return isIntegerInRange (s, 1, 12);
}

// Check for valid day
function isDay (s)
{   if (isEmpty(s)) 
       if (isDay.arguments.length == 1) return defaultEmptyOK;
       else return (isDay.arguments[1] == true);   
    return isIntegerInRange (s, 1, 31);
}

// February has 29 days in any year evenly divisible by four
// except for centurial years which are not divisible by 400
// daysInFebruary (INTEGER year)
function daysInFebruary (year)
{
    return (  ((year % 4 == 0) && ( (!(year % 100 == 0)) || (year % 400 == 0) ) ) ? 29 : 28 );
}

// Check for valid date
function isDate (year, month, day)
{
    if (! (isYear(year, false) && isMonth(month, false) && isDay(day, false))) return false;

    var intYear = parseInt(year);
    var intMonth = parseInt(month);
    var intDay = parseInt(day);

    if (intDay > daysInMonth[intMonth]) return false; 

    if ((intMonth == 2) && (intDay > daysInFebruary(intYear))) return false;

    return true;
}

// Display prompt in status bar
function prompt (s)
{   window.status = s
}

// Display data entry prompt in status bar
function promptEntry (s)
{   window.status = pEntryPrompt + s
}

// Notify the user that the required field is empty
function warnEmpty (theField, s)
{   theField.focus()
    alert(mPrefix + s + mSuffix)
    return false
}

// Notify the user that the field contents are invalid
function warnInvalid (theField, s)
{   theField.focus()
    theField.select()
    alert(s)
    return false
}

// Check string
function checkString (theField, s, emptyOK)
{ 
    if (checkString.arguments.length == 2) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    if (isWhitespace(theField.value)) 
       return warnEmpty (theField, s);
    else return true;
}

// Check state code
function checkStateCode (theField, emptyOK)
{   if (checkStateCode.arguments.length == 1) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    else
    {  theField.value = theField.value.toUpperCase();
       if (!isStateCode(theField.value, false)) 
          return warnInvalid (theField, iStateCode);
       else return true;
    }
}

// Reformat zip code
function reformatZIPCode (ZIPString)
{   if (ZIPString.length == 5) return ZIPString;
    else return (reformat (ZIPString, "", 5, "-", 4));
}

// Check zip code
function checkZIPCode (theField, emptyOK)
{   if (checkZIPCode.arguments.length == 1) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    else
    { var normalizedZIP = stripCharsInBag(theField.value, ZIPCodeDelimiters)
      if (!isZIPCode(normalizedZIP, false)) 
         return warnInvalid (theField, iZIPCode);
      else 
      { 
         theField.value = reformatZIPCode(normalizedZIP)
         return true;
      }
    }
}

// Reformat phone number
function reformatPhone (Phone)
{   return (reformat (Phone, "(", 3, ") ", 3, "-", 4))
}

// Check phone number
function checkPhone (theField, emptyOK)
{   if (checkPhone.arguments.length == 1) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    else
    {  var normalizedPhone = stripCharsInBag(theField.value, phoneNumberDelimiters)
       if (!isPhone(normalizedPhone, false)) 
          return warnInvalid (theField, iPhone);
       else 
       {  // if you don't want to reformat as (123) 456-789, comment next line out
          theField.value = reformatPhone(normalizedPhone)
          return true;
       }
    }
}

// Reformat fax number
function reformatFax (Fax)
{   return (reformat (Fax, "(", 3, ") ", 3, "-", 4))
}

// Check fax number
function checkFax (theField, emptyOK)
{   if (checkFax.arguments.length == 1) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    else
    {  var normalizedFax = stripCharsInBag(theField.value, faxNumberDelimiters)
       if (!isFax(normalizedFax, false)) 
          return warnInvalid (theField, iFax);
       else 
       {
          theField.value = reformatFax(normalizedFax)
          return true;
       }
    }
}

// Check email address
function checkEmail (theField, emptyOK)
{   if (checkEmail.arguments.length == 1) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    else if (!isEmail(theField.value, false)) 
       return warnInvalid (theField, iEmail);
    else return true;
}