// BROWSER SNIFFER (Sniff out the good and bad browsers)

function Is() {
  var agent = navigator.userAgent.toLowerCase();
  this.major = parseInt(navigator.appVersion);
  this.minor = parseFloat(navigator.appVersion);
  this.ns  = ((agent.indexOf('mozilla')!=-1) && ((agent.indexOf('spoofer')==-1) && (agent.indexOf('compatible') == -1)));
  this.ns2 = (this.ns && (this.major == 2));
  this.ns3 = (this.ns && (this.major == 3));
  this.ns4b = (this.ns && (this.minor < 4.04));
  this.ns4 = (this.ns && (this.major >= 4));
  this.ie   = (agent.indexOf("msie") != -1);
  this.ie3  = (this.ie && (this.major == 2));
  this.ie4  = (this.ie && (this.major >= 4));
  this.op3 = (agent.indexOf("opera") != -1);
  this.win   = (agent.indexOf("win")!=-1);
  this.mac   = (agent.indexOf("mac")!=-1);
  this.unix  = (agent.indexOf("x11")!=-1);
}
var is = new Is();

// VARIABLE DECLARATIONS

var digits = "0123456789";
var lowercaseLetters = "abcdefghijklmnopqrstuvwxyz";
var uppercaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var whitespace = " \t\n\r";
var decimalPointDelimiter = ".";
var SSNDelimiters = "- ";
var validSSNChars = digits + SSNDelimiters;
var digitsInSocialSecurityNumber = 9;
var validZIPCodeChars = digits;
var digitsInZIPCode1 = 5;
var digitsInZIPCode2 = 9;
var phoneNumberDelimiters = "()- ";
var validUSPhoneChars = digits + phoneNumberDelimiters;
var digitsInUSPhoneNumber = 10;

// i is an abbreviation for "invalid"

var iDay =       "This field must contain a day number between 1 and 31.";
var iMonth =     "This field must contain a month number between 1 and 12.";
var iYear =      "This field must contain a 4 digit year number.";
var iDayMonth =  "The day is too large for the month.";
var iRequired =  "This is a required field.";
var iNumeric =   "This field must contain a valid number.";
var iAreaCode =  "The Area Code must be between 200 and 999.";
var iExchange =  "The Phone Exchange must be between 200 and 999.";
var iPNumber =   "The Phone Number must be between 0001 and 9999.";
var iPhone =     "The Phone Number has an error.";
var iEmail =     "The Email address has an error.";
var iInvalid =   "The phrase or sentence contains an unacceptable character.";
var iDayMonth =  "The month does not have that many days.";
var iHyperlink = "The URL has an error.";
var iLength3 =      "The entry must be at least 3 characters.";
var iLength4 =      "The entry must be at least 4 characters.";
var iLength6 =      "The entry must be at least 6 characters.";
var iAlphaNumeric = "The entry may only contain letters and numbers.";

var defaultEmptyOK = true;

function makeArray(n)
{  for (var i = 1; i <= n; i++) {
      this[i] = 0
   } 
   return this
}

var daysInMonth = makeArray(12);
daysInMonth[1] = 31;
daysInMonth[2] = 29;   // must programmatically check this
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 isEmpty(s)
{   return ((s == null) || (s.length == 0))
}

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;
}

function isCrLf (c)
{  return ((c == "\n") || (c == "\r")) 
}

function isLetter (c)
{   return ( ((c >= "a") && (c <= "z")) || ((c >= "A") && (c <= "Z")) )
}

function isDigit (c)
{   return ((c >= "0") && (c <= "9"))
}

function isLetterOrDigit (c)
{   return (isLetter(c) || isDigit(c))
}

function isWordPunctuation (c)
{   return (( c == "'") || ( c == " ") || ( c == "-") || ( c == "."))
}

function isParagraphPunctuation (c)
{   return (((c >= " ") && (c <= "/")) || (c == ":") || (c == ";") || (c == "/") || (c == "?") || (c == "@")) 
}

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;
}

function isSignedInteger (s)
{   if (isEmpty(s)) 
       if (isSignedInteger.arguments.length == 1) return defaultEmptyOK;
       else return (isSignedInteger.arguments[1] == true);
    else {
        var startPos = 0;
        var secondArg = defaultEmptyOK;
        if (isSignedInteger.arguments.length > 1)
            secondArg = isSignedInteger.arguments[1];
        if ( (s.charAt(0) == "-") || (s.charAt(0) == "+") ) startPos = 1;    
        return (isInteger(s.substring(startPos, s.length), secondArg))
    }
}

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) ) );
}

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) ) );
}

function isFloat (s)
{   var i;
    var seenDecimalPoint = false;
    if (isEmpty(s)) 
       if (isFloat.arguments.length == 1) return defaultEmptyOK;
       else return (isFloat.arguments[1] == true);
    if (s == decimalPointDelimiter) return false;
    for (i = 0; i < s.length; i++)
    {   
        var c = s.charAt(i);
        if ((c == decimalPointDelimiter) && !seenDecimalPoint) seenDecimalPoint = true;
        else if (!isDigit(c)) return false;
    }
    return true;
}

function isSignedFloat (s)
{   if (isEmpty(s)) 
       if (isSignedFloat.arguments.length == 1) return defaultEmptyOK;
       else return (isSignedFloat.arguments[1] == true);
    else
    {  var startPos = 0;
       var secondArg = defaultEmptyOK;
       if (isSignedFloat.arguments.length > 1)
          secondArg = isSignedFloat.arguments[1];
       if ( (s.charAt(0) == "-") || (s.charAt(0) == "+") )
          startPos = 1;    
       return (isFloat(s.substring(startPos, s.length), secondArg))
    }
}

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;
}

function isParagraph (s)
{   var i;
    if (isEmpty(s)) 
       if (isParagraph.arguments.length == 1) return defaultEmptyOK;
       else return (isParagraph.arguments[1] == true);
    for (i = 0; i < s.length; i++)
    {   
        var c = s.charAt(i);
        if (!isWordPunctuation(c) && !isLetterOrDigit(c) && !isParagraphPunctuation(c)) 
        return false;
    }
    return true;
}

function isMailMess (s)
{   var i;
    if (isEmpty(s)) 
       if (isMailMess.arguments.length == 1) return defaultEmptyOK;
       else return (isParagraph.arguments[1] == true);
    for (i = 0; i < s.length; i++)
    {   
        var c = s.charAt(i);
        if (!isWordPunctuation(c) && !isLetterOrDigit(c) && !isParagraphPunctuation(c) && !isCrLf(c)) 
        return false;
    }
    return true;
}

function isWords (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) && !isWordPunctuation(c))
        return false;
    }
    return true;
}

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;
}

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)
}

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);
}

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));
}

function isMonth (s)
{   if (isEmpty(s)) 
       if (isMonth.arguments.length == 1) return defaultEmptyOK;
       else return (isMonth.arguments[1] == true);
    return isIntegerInRange (s, 1, 12);
}

function isDay (s)
{   if (isEmpty(s)) 
       if (isDay.arguments.length == 1) return defaultEmptyOK;
       else return (isDay.arguments[1] == true);   
    return isIntegerInRange (s, 1, 31);
}

function daysInFebruary (year)
{   // February has 29 days in any year evenly divisible by four,
    // EXCEPT for centurial years which are not also divisible by 400.
    return (  ((year % 4 == 0) && ( (!(year % 100 == 0)) || (year % 400 == 0) ) ) ? 29 : 28 );
}

function isHyperlink(s)
{
  if (s.indexOf(" ") != -1)
    return false;
  else if (s.indexOf("http://") != -1)
    return false;
  else if (s.indexOf(".") == -1)
    return false;
  else if (s.indexOf(".") == 0)
    return false;
  else if (s.charAt(s.length - 1) == ".")
    return false;

  if (s.indexOf("/") != -1) 
  {
    s = s.substring(0, s.indexOf("/"));
    if (s.charAt(s.length - 1) == ".")
      return false;
  }

  if (s.indexOf(":") != -1) 
  {
    if (s.indexOf(":") == (s.length - 1))
      return false;
    else if (s.charAt(s.indexOf(":") + 1) == ".")
      return false;
    argvalue = s.substring(0, s.indexOf(":"));
    if (s.charAt(s.length - 1) == ".")
      return false;
  }
  return true;

}

/* FUNCTIONS TO NOTIFY USER OF INPUT REQUIREMENTS OR MISTAKES. */

function warn (theField, s)
{
  theField.focus();
  alert(s);
  return false;
};

function warnColor (theField, s)
{
  if (is.ie4) theField.style.backgroundColor = "red";
  theField.focus();
  alert(s);
  if (is.ie4) theField.style.backgroundColor = document.bgColor;
  theField.select();
  return false;
};

/* FUNCTIONS TO REFORMAT FIELD VALUES */

function reformatUSPhone (USPhone)
{   return (reformat (USPhone, "(", 3, ") ", 3, "-", 4))
}

function capitolize (theField)
{   var returnString = "";
    var first = true;
    for (i = 0; i < theField.value.length; i++)
    {   var c = theField.value.charAt(i);
        if (first)
        {   returnString += c.toUpperCase();
            first = false;
        }else
        {   returnString += c;
        };
        if (isWordPunctuation(c)) first = true;
    }
    theField.value = returnString;
    return true;
}

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;
}

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;
}

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;
}

/* FUNCTIONS TO CHECK FIELD VALUES */

function checkRoomNumber (theField, emptyOK)
{ if (checkRoomNumber.arguments.length == 1) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    if (isWhitespace(theField.value)) return warnColor (theField, iRequired);
    if (!isAlphanumeric(theField.value)) return warnColor (theField, iAlphaNumeric);
	if (theField.value.length < 3) return warnColor (theField, iLength3);
    return true;
}

function checkUserID (theField, emptyOK)
{   if (checkUserID.arguments.length == 1) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    if (isWhitespace(theField.value)) return warnColor (theField, iRequired);
    if (!isAlphanumeric(theField.value)) return warnColor (theField, iAlphaNumeric);
	if (theField.value.length < 6) return warnColor (theField, iLength6);
    return true;
}

function checkPassword (theField, emptyOK)
{   if (checkPassword.arguments.length == 1) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    if (isWhitespace(theField.value)) return warnColor (theField, iRequired);
    if (!isAlphanumeric(theField.value)) return warnColor (theField, iAlphaNumeric);
	if (theField.value.length < 4) return warnColor (theField, iLength4);
    return true;
}

function checkUSPhone (theField, emptyOK)
{   if (checkUSPhone.arguments.length == 1) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    else
    {  var normalizedPhone = stripCharsInBag(theField.value, phoneNumberDelimiters)
       if (!isUSPhoneNumber(normalizedPhone, false)) 
          return warnColor (theField,iPhone);
       else 
       {  // if you don't want to reformat as (123) 456-789, comment next line out
          theField.value = reformatUSPhone(normalizedPhone)
          return true;
       }
    }
}

function checkLetter (theField, s, emptyOK)
{  if (checkLetter.arguments.length == 2) emptyOK = defaultEmptyOK;
   if ((emptyOK == true) && (isEmpty(theField.value))) return true;
   if (!isLetter(theField.value)) return warnColor (theField,s);
   return true;
}

function checkIntegerInRange (theField, a, b, s)
{  if (!isIntegerInRange(theField.value, a, b)) return warnColor (theField,s);
   return true;
}

function checkEmpty (theField, s) 
{  if (isEmpty(theField.value)) return warnColor (theField,'');
   return true;
}

function checkWord (theField, s, emptyOK)
{   // Next line is needed on NN3 to avoid "undefined is not a number" error
    // in equality comparison below.
    if (checkWord.arguments.length == 2) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    if (!isAlphabetic(theField.value)) return warnColor (theField, s);
    return true;
}

function checkWords (theField, s, emptyOK)
{   // Next line is needed on NN3 to avoid "undefined is not a number" error
    // in equality comparison below.
    if (checkWords.arguments.length == 2) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    if (isWhitespace(theField.value)) return warnColor (theField, iRequired);
    if (!isWords(theField.value)) return warnColor (theField, s);
    return true;
}

function checkParagraph (theField, s, emptyOK)
{   // Next line is needed on NN3 to avoid "undefined is not a number" error
    // in equality comparison below.
    if (checkParagraph.arguments.length == 2) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    if (isWhitespace(theField.value)) return warnColor (theField, iRequired);
    if (!isParagraph(theField.value)) return warnColor (theField, s);
    return true;
}

function checkDate (dayField, intDay, intMonth, intYear)
{   // catch invalid days, except for February
    // alert(intDay + "," + intMonth + "," + intYear);
    if (checkDate.arguments.length != 4) return false;
    if (intDay > daysInMonth[intMonth]) return warn (dayField,iDayMonth); 
    if ((intMonth == 2) && (intDay > daysInFebruary(intYear))) return warn (dayField,iDayMonth);
    return true;
}

function checkEmail(theField, emptyOK)
{   // Next line is needed on NN3 to avoid "undefined is not a number" error
    // in equality comparison below.
    if (checkEmail.arguments.length == 1) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    if (isWhitespace(theField.value)) return warnColor (theField, iRequired);
    var reg1 = /(@.*@)|(\.\.)|(@\.)|(\.@)|(^\.)/; // not valid
    var reg2 = /^.+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,3}|[0-9]{1,3})(\]?)$/; // valid
    if (reg1.test(theField.value) || !reg2.test(theField.value)) return warnColor (theField, iEmail);
    return true;
}

function checkMailMess (theField, s, emptyOK)
{   // Next line is needed on NN3 to avoid "undefined is not a number" error
    // in equality comparison below.
    if (checkMailMess.arguments.length == 2) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    if (isWhitespace(theField.value)) return warnColor (theField, iRequired);
    if (!isMailMess(theField.value)) return warnColor (theField, s);
    return true;
}

function checkHyperlink(theField, emptyOK)
{   // Next line is needed on NN3 to avoid "undefined is not a number" error
    // in equality comparison below.
    if (checkHyperlink.arguments.length == 1) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    if (isWhitespace(theField.value)) return warnColor (theField, iRequired);
    if (!isHyperlink(theField.value)) return warnColor (theField, iHyperlink);
    return true;
}
