/* *************************************************************

** FORMVAL.JS - JS Form Validation Library

** =======================================

** This library contains code to validate HTML form-field data.

** The bulk of this file was derived from Eric Krock's FormChek.js at

** developer.netscape.com/docs/examples/javascript/formval/overview.jspl

** (Many thanks, Eric!) Enjoy ... and please maintain this header.

**

** To load this library in an HTML doc, put the following

** line in the doc's HEAD (before any other SCRIPT tags):

**

** <SCRIPT SRC="formval.js" LANGUAGE="JavaScript"></SCRIPT>

**

** Author      Ver  Date     Comments

** ======      ===  ====     ========

** Eric Krock  1.0  2/18/97  original JS 1.0-only version

** Rick Scott  2.0  2/1/00   streamlined file by removing:

**                           SS check, credit-card checks,

**                           defaultEmptyOK, and much more;

**                           added stripLeadingBlanks(),

**                           stripTrailingBlanks(), 

**                           stripLeadingTrailingBlanks(),

**                           getCheckedRadioButton(), 

**                           getCheckedCheckboxes(), 

**                           getCheckedSelectOptions()

**

** (c) 1997 Netscape Communications Corporation

** Copyright 2000, Rick Scott, all rights reserved.

************************************************************* */



/* *************************************************************

** USAGE

** =====

** Functions to Validata Form-Element Data:

**   isEmpty(s) - true if string s is empty

**   isBlank(s) - true if string s is empty or blank (all whitespace chars)

**   isLetter(c) - true if char c is an English letter 

**   isDigit(c) - true if char c is a digit 

**   isInteger(s) - true if string s is a signed/unsigned number

**   isIntegerInRange(s, a, b) - true if string s (integer) is a <= s <= b

**   isFloat(s) - true if string s is a signed/unsigned floating-point number

**   isAlphanumeric(s) - true if s is English letters and numbers only

**   isUSPhoneNumber(s) - true if string s is a valid U.S. phone number

**   isZIPCode(s) - true if string s is a valid U.S. ZIP code

**   isStateCode(s) - true if string s is a valid U.S. (2-letter) state code

**   isEmail(s) - true if string s is a valid email address

**   getCheckedRadioButton(radioSet) - gets index checked radio button in set

**   getCheckedCheckboxes(checkboxSet) - gets index(es) of checked checkboxes in set

**   getCheckedSelectOptions(select) - gets index(es) of checked select options

**

** Functions to Reformat input=text Form-Field Data:

**   stripCharsInBag(s, bag) - removes all chars in string bag from string s

**   stripCharsNotInBag(s, bag) - removes all chars NOT in string bag from string s

**   stripBlanks(s) - removes all blank chars from s

**   stripLeadingBlanks(s) - removes leading blank chars from s

**   stripTrailingBlanks(s) - removes trailing blank chars from s

**   stripLeadingTrailingBlanks(s) - removes lead+trail blank chars from s

**   reformatString(targetStr, str1, int1 [, str2, int2 [, ..., ...]]) - 

**     inserts formatting chars or delimiters in targetStr (see below)

************************************************************* */





/* ********************************************************** */

/* Global Variables and Constants *************************** */

/* ********************************************************** */



var digits = "0123456789";

var lowercaseLetters = "abcdefghijklmnopqrstuvwxyz"

var uppercaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

var blanks = " \t\n\r";  // aka whitespace chars



// decimal point character differs by language and culture

var decimalPointDelimiter = "."



// non-digit characters allowed in phone numbers

var phoneNumberDelimiters = "()- ";



// characters allowed in US phone numbers

var validUSPhoneChars = digits + phoneNumberDelimiters;



// U.S. phone numbers have 10 digits, formatted as ### ### #### or (###)###-####

var digitsInUSPhoneNumber = 10;



// non-digit characters which are allowed in ZIP Codes

var ZIPCodeDelimiters = "-";



// our preferred delimiter for reformatting ZIP Codes

var ZIPCodeDelimeter = "-"



// U.S. ZIP codes have 5 or 9 digits, formatted as ##### or #####-####

var digitsInZIPCode1 = 5

var digitsInZIPCode2 = 9



// Valid U.S. Postal Codes for states, territories, armed forces, etc.

// See http://www.usps.gov/ncsc/lookups/abbr_state.txt

var USStateCodeDelimiter = "|";

var USStateCodes = "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|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"





/* ********************************************************** */

/* Functions ************************************************ */

/* ********************************************************** */





// Returns true if string s is empty



function isEmpty(s)

  {

  return ((s == null) || (s.length == 0));

  }





// Returns true if string s is empty or all blank chars



function isBlank(s)

  {

  var i;



  // Is s empty?

  if (isEmpty(s))

    return true;



  // Search through string's chars one by one until we find first

  // non-blank char, then return false; if we don't, return true

  for (i=0; i<s.length; i++)

    {   

    // Check that current character isn't blank

    var c = s.charAt(i);

    if (blanks.indexOf(c) == -1) 

      return false;

    }

  // All characters are blank

  return true;

  }





// Removes all characters which appear in string bag from string s



function stripCharsInBag (s, bag)

  {

  var i;

  var returnString = "";



  // Search through string's characters one by one;

  // if character is not in bag, append to returnString

  for (i = 0; i < s.length; i++)

    {   

    // Check that current character isn't blank

    var c = s.charAt(i);

    if (bag.indexOf(c) == -1) 

      returnString += c;

    }

  return returnString;

  }





// Removes all characters which do NOT appear in string bag from string s



function stripCharsNotInBag (s, bag)

  {

  var i;

  var returnString = "";



  // Search through string's characters one by one;

  // if character is in bag, append to returnString

  for (i = 0; i < s.length; i++)

    {   

    // Check that current character isn't blank

    var c = s.charAt(i);

    if (bag.indexOf(c) != -1) 

      returnString += c;

    }

  return returnString;

  }





// Removes all blank chars (as defined by blanks) from s



function stripBlanks(s)

  {

  return stripCharsInBag(s, blanks)

  }





// Removes leading blank chars (as defined by blanks) from s



function stripLeadingBlanks(s)

  { 

  var i = 0;

  while ((i < s.length) && (blanks.indexOf(s.charAt(i)) != -1))

     i++;

  return s.substring(i, s.length);

  }





// Removes trailing blank chars (as defined by blanks) from s



function stripTrailingBlanks(s)

  { 

  var i = s.length - 1;

  while ((i >= 0) && (blanks.indexOf(s.charAt(i)) != -1))

     i--;

  return s.substring(0, i+1);

  }





// Removes leading+trailing blank chars (as defined by blanks) from s



function stripLeadingTrailingBlanks(s)

  { 

  s = stripLeadingBlanks(s);

  s = stripTrailingBlanks(s);

  return s;

  }





// Returns true if character c is an English letter (A .. Z, a..z)



function isLetter(c)

  {

  return (((c >= "a") && (c <= "z")) || ((c >= "A") && (c <= "Z")));

  }





// Returns true if character c is a digit (0 .. 9)



function isDigit(c)

  {

  return ((c >= "0") && (c <= "9"));

  }





// Returns true if all chars in string s are numbers;

// first character is allowed to be + or -; does not 

// accept floating point, exponential notation, etc.



function isInteger(s)

  {

  if (isBlank(s))

    return false;



  // skip leading + or -

  if ((s.charAt(0) == "-") || (s.charAt(0) == "+"))

    var i = 1;

  else

    var i = 0;



  // Search through string's chars one by one until we find a 

  // non-numeric char, then return false; if we don't, return true

  for (i; i<s.length; i++)

    {   

    // Check that current character is number

    var c = s.charAt(i);

    if (!isDigit(c)) 

      return false;

    }

  // All characters are numbers

  return true;

  }





// True if string s is an unsigned floating point (real) number; 

// first character is allowed to be + or -; no exponential notation.



function isFloat(s)

  { 

  var seenDecimalPoint = false;



  if (isBlank(s)) 

    return false;

  if (s == decimalPointDelimiter) 

    return false;



  // skip leading + or -

  if ((s.charAt(0) == "-") || (s.charAt(0) == "+"))

    var i = 1;

  else

    var i = 0;



  // Search through string's chars one by one until we find a 

  // non-numeric char, then return false; if we don't, return true



  for (i; i<s.length; i++)

    {   

    // Check that current character is number

    var c = s.charAt(i);



    if ((c == decimalPointDelimiter) && !seenDecimalPoint) 

      seenDecimalPoint = true;

    else if (!isDigit(c)) 

      return false;

    }

  // All characters are numbers

  return seenDecimalPoint;

  }





// Returns true if string s is English letters (A .. Z, a..z) only



function isAlphabetic(s)

  {

  var i;



  if (isBlank(s)) 

     return false;



  // Search through string's chars one by one until we find a 

  // non-alphabetic char, then return false; if we don't, return true



  for (i = 0; i < s.length; i++)

  {   

  // Check that current character is letter

  var c = s.charAt(i);



  if (!isLetter(c))

    return false;

  }



  // All characters are letters

  return true;

  }





// Returns true if string s is English letters (A .. Z, a..z) and numbers only



function isAlphanumeric(s)

  {

  var i;



  if (isBlank(s)) 

     return false;



  // Search through string's chars one by one until we find a 

  // non-alphanumeric char, then return false; if we don't, return true



  for (i = 0; i < s.length; i++)

    {   

    // Check that current character is number or letter

    var c = s.charAt(i);



    if (! (isLetter(c) || isDigit(c) ) )

    return false;

    }



  // All characters are numbers or letters

  return true;

  }





// reformatString(targetStr [, str1, int1, str2, int2, ... strN, intN])       

//

// Handy function for arbitrarily inserting formatting characters

// or delimiters of various kinds within targetString.

//

// reformatString() takes one required string argument, targetStr, 

// and 0-N optional string/integer-pair arguments. These optional 

// arguments specify how targetStr is to be reformatted and how/where 

// other strings are to be inserted in it.

//

// reformatString() processes the optional args in order one by one.

// * If the argument is an integer, reformatString() appends that number 

//   of sequential characters from targetStr to the resultString.

// * If the argument is a string, reformatString() appends the string

//   to the resultString.

//

// NOTE: The first argument after targetString must be a string.

// (It can be empty.)  The second argument must be an integer.

// Thereafter, integers and strings must alternate.  This is to

// provide backward compatibility to Navigator 2.0.2 JavaScript

// by avoiding use of the typeof operator.

//

// It is the caller's responsibility to make sure that we do not

// try to copy more characters from s than s.length.

//

// EXAMPLES:

//

// * To reformat a 10-digit U.S. phone number from "1234567890"

//   to "(123)456-7890" make this function call:

//   reformatString("1234567890", "(", 3, ")", 3, "-", 4)

//

// HINT:

//

// If you have a string which is already delimited in one way

// (example: a phone number delimited with spaces as "123 456 7890")

// and you want to delimit it in another way using function reformat,

// call stripCharsNotInBag() or stripBlanks() to remove unwanted 

// characters, THEN call function reformatString to delimit as desired.

//

// EXAMPLE:

//

// reformatString(stripCharsNotInBag ("123 456 7890", digits),

//           "(", 3, ") ", 3, "-", 4)



function reformatString(targetString)

  { 

  var arg;

  var sPos = 0;

  var resultString = "";



  for (var i=1; i<reformatString.arguments.length; i++)

    {

    arg = reformatString.arguments[i];

    if (i%2 == 1) 

      {

      resultString += arg;

      }

    else

      {

      resultString += targetString.substring(sPos, sPos + arg);

      sPos += arg;

      }

    }

  return resultString;

  }





// Returns true if s is valid U.S. phone number (with area code): 10 digits



function isUSPhoneNumber(s)

  { 

  if (isBlank(s)) 

    return false;

  s = stripCharsNotInBag(s, digits);

  return (isInteger(s) && (s.length == digitsInUSPhoneNumber));

  }





// Returns true if string s is a valid U.S. ZIP code: ##### or #####-####



function isZIPCode(s)

  { 

  if (isBlank(s)) 

    return false;



  s = stripLeadingTrailingBlanks(s);



  if ((isInteger(s)) && (s.indexOf("-") == -1) &&  // #####

      (s.length == digitsInZIPCode1))

    return true;



  if (s.indexOf("-") != 5)  // - in wrong place

    return false;



  s = stripCharsNotInBag(s, digits);



  if (s.length == digitsInZIPCode2)

    return true;   // #####-####

  else

    return false;  // not #####-####

  }





// Returns true if s is valid 2-letter U.S. state abbreviation



function isStateCode(s)

  { 

  if (isBlank(s)) 

    return false;

  return ((USStateCodes.indexOf(s) != -1) &&

          (s.indexOf(USStateCodeDelimiter) == -1))

  }





// Returns true if string is a valid email address: @ and . required,

// at least one char before @, at least one char before and after .



function isEmail(s)

  { 

  if (isBlank(s)) 

    return false;

  

  // there must be >= 1 character before @, so we start

  // start looking at character position 1 (i.e. second character)

  var i = 1;

  var sLength = s.length;



  // look for @

  while ((i < sLength) && (s.charAt(i) != "@"))

    i++



  if ((i >= sLength) || (s.charAt(i) != "@")) 

    return false;

  else 

    i += 2;



  // look for .

  while ((i < sLength) && (s.charAt(i) != "."))

    i++



  // there must be at least one character after the .

  if ((i >= sLength - 1) || (s.charAt(i) != ".")) 

    return false;

  else 

    return true;

  }





// Returns true if string s is an integer such that a <= s <= b



function isIntegerInRange (s, a, b)

  { 

  if (isBlank(s)) 

    return false;

  if (!isInteger(s)) 

    return false;

  var num = parseInt(s);

  return ((num >= a) && (num <= b));

  }





// Returns index of checked radio button in radio set,

// or -1 if no radio buttons are checked



function getCheckedRadioButton(radioSet)

  { 

  for (var i=0; i<radioSet.length; i++)

    if (radioSet[i].checked)

      return i;

  return -1;

  }





// Returns array containing index(es) of checked checkbox(es) 

// in checkbox set, or -1 if no checkboxes are checked



function getCheckedCheckboxes(checkboxSet)

  {

  var arr = new Array();

  for (var i=0,j=0; i<checkboxSet.length; i++)

    if (checkboxSet[i].checked)

      arr[j++] = i;

  if (arr.length > 0)

    return arr;

  else

    return -1;

  }





// Returns array containing index(es) of checked option(s) 

// in select box, or -1 if no options are selected



function getCheckedSelectOptions(select)

  {

  var arr = new Array();

  for (var i=0,j=0; i<select.length; i++)

    if (select.options[i].selected)

      arr[j++] = i;

  if (arr.length > 0)

    return arr;

  else

    return -1;

  }



