function vIs ()
{   // convert all characters to lowercase to simplify testing
    var agt=navigator.userAgent.toLowerCase()

    // *** BROWSER VERSION ***
    this.major = parseInt(navigator.appVersion)
    this.minor = parseFloat(navigator.appVersion)

    this.nav  = ((agt.indexOf('mozilla')!=-1) && ((agt.indexOf('spoofer')==-1)
                && (agt.indexOf('compatible') == -1)))
    this.nav2 = (this.nav && (this.major == 2))
    this.nav3 = (this.nav && (this.major == 3))
    this.nav3up = this.nav && (this.major >= 3)
    this.nav4 = (this.nav && (this.major == 4))
    this.nav4up = this.nav && (this.major >= 4)
    this.navonly      = (this.nav && (agt.indexOf(";nav") != -1))

    this.ie   = (agt.indexOf("msie") != -1)
    this.ie3  = (this.ie && (this.major == 2))
    this.ie4  = (this.ie && (this.major == 4))
    this.ie4up  = this.ie  && (this.major >= 4)

    this.opera = (agt.indexOf("opera") != -1)
     
    // *** JAVASCRIPT VERSION CHECK *** 
    // Useful to workaround Nav3 bug in which Nav3 
    // loads LANGUAGE="JavaScript1.2" .
    if (this.nav2 || this.ie3) this.js = 1.0
    else if (this.nav3 || this.opera) this.js = 1.1
    else if (this.nav4 || this.ie4) this.js = 1.2
    // NOTE: In the future, update this code when newer versions of JS 
    // are released. For now, we try to provide some upward compatibility 
    // so that future versions of Nav and IE will show they are at 
    // *least* JS 1.2 capable. Always check for JS version compatibility 
    // with > or >=.
    else if ((this.nav && (this.minor > 4.05)) || (this.ie && (this.major > 4))) 
         this.js = 1.2
    else this.js = 0.0 // HACK: always check for JS version with > or >=

}

var vis = new vIs();

var agt = navigator.userAgent.toLowerCase();
var ieVer4Up = false;
var isNav  = ((agt.indexOf('mozilla')!=-1) && ((agt.indexOf('spoofer')==-1)
                && (agt.indexOf('compatible') == -1)))
                
// *** BROWSER VERSION ***
ieVer4Up  = (((agt.indexOf("msie") != -1) && (parseInt(navigator.appVersion,10) >= 4))  || ( isNav ));
    
    
// This is the function that shows a prompt 
// The prompt displays the users question
// The header displays a data caption
// The body is the display of the data
function showConfirm(strPrompt,strHeader,strBody)
{
	var msg = "";


	if (vis.js >= 1.0 && ieVer4Up) {
		if (strPrompt) {
			msg += strPrompt + "\n";
		}
		if (strHeader) {
			msg += "______________________________________________________\n\n";
			msg += strHeader + "\n";
		}
		if (strBody) {
			msg += "______________________________________________________\n\n";
			msg += strBody + "\n";
		}
		if (msg)
			return confirm(msg);

	}
	//Default return value if version 1.0 is not supported so this call is ignored!!
	return true;
}



var defaultEmptyOK = false

var sEmail = "Email"

var iEmail = "This field must be a valid email address (like foo@bar.com). Please reenter it now."

var pEmail = "valid email address (like foo@bar.com)."

var iPhone = "This field must be a 10 digit U.S. phone number (like 415 555 1212)\n"
	iPhone += " or\n"
	iPhone += "a valid international phone number (like 011 - country code - city code - telephone number.\n Please reenter it now."
	
var iWorldPhone = "This field must be a valid international phone number. Please reenter it now."

var iSSN = "This field must be a 9 digit U.S. social security number (like 123 45 6789). Please reenter it now."

// Format messages
var s51 = "The format for this field is #####.# (ie. 12345.6).\n Please reenter it now."
var s91 = "The format for this field is #####.# (ie. 12345.1 or 9.5).\nPlease reenter it now."
var s92 = "The format for this field is #####.## (ie. 12345.01 or 9.05).\nPlease reenter it now."
var s5 = "The format for this field is ##### (ie. 12345).\nPlease reenter it now."
var s71 = "The format for this field is #######-A (ie. 1234567-a or 9876564-Z).\nPlease reenter it now."
var s916 = "The format for this field is # to ###### (ie. 0 to 999999).\nPlease reenter it now."

// DATE Messages
var sDateOfBirth = "Date of Birth"
var iDay = "This day must be a number between 1 and 31.  Please reenter it now."
var iMonth = "This month must be a number between 1 and 12.  Please reenter it now."
var iYear = "This year must be a 4 digit year number.  Please reenter it now."
var iDatePrefix = "The Day, Month, and Year"
var iDateSuffix = " do not form a valid date.  Please reenter them now."
var iDate = "This field must be a valid date (like 01/30/1999). Please reenter it now."
var iFuture = "This field cannot be a future date.\nPlease reenter it now."

// whitespace characters
var whitespace = " \t\n\r";
//digits
var digits = "0123456789";

// Notify user that contents of field theField are invalid.
// String s describes expected contents of theField.value.
// Put select theField, pu focus in it, and return false.

function warnInvalid (theField, s)
{   theField.focus()
	theField.select()
	alert(theField.name + ": " + s);
	return false
}
// Check whether string s is empty.

function isEmpty(s)
{   return ((s == null) || (s.length == 0))
}

function isDigit (c)
{   
	return ((c >= "0") && (c <= "9"))
}

// isPositiveInteger (STRING s [, BOOLEAN emptyOK])
// 
// Returns true if string s is an integer > 0.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

function isPositiveInteger (s)
{   var secondArg = defaultEmptyOK;

    if (isPositiveInteger.arguments.length > 1)
        secondArg = isPositiveInteger.arguments[1];

    // The next line is a bit byzantine.  What it means is:
    // a) s must be a signed integer, AND
    // b) one of the following must be true:
    //    i)  s is empty and we are supposed to return true for
    //        empty strings
    //    ii) this is a positive, not negative, number

    return (isSignedInteger(s, secondArg)
         && ( (isEmpty(s) && secondArg)  || (parseInt (s,10) > 0) ) );
}

function isInteger (s)
{   var i;

    if (isEmpty(s)) 
       if (isInteger.arguments.length == 1) return defaultEmptyOK;
       else return (isInteger.arguments[1] == true);

    // Search through string's characters one by one
    // until we find a non-numeric character.
    // When we do, return false; if we don't, return true.

    for (i = 0; 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;
}




// Attempting to make this library run on Navigator 2.0,
// so I'm supplying this array creation routine as per
// JavaScript 1.0 documentation.  If you're using 
// Navigator 3.0 or later, you don't need to do this;
// you can use the Array constructor instead.

function makeArray(n) {
//*** BUG: If I put this line in, I get two error messages:
//(1) Window.length can't be set by assignment
//(2) daysInMonth has no property indexed by 4
//If I leave it out, the code works fine.
//   this.length = n;
   for (var i = 1; i <= n; i++) {
      this[i] = 0
   } 
   return this
}

// isNonnegativeInteger (STRING s [, BOOLEAN emptyOK])
// 
// Returns true if string s is an integer >= 0.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

function isNonnegativeInteger (s)
{   var secondArg = defaultEmptyOK;

    if (isNonnegativeInteger.arguments.length > 1)
        secondArg = isNonnegativeInteger.arguments[1];

    // The next line is a bit byzantine.  What it means is:
    // a) s must be a signed integer, AND
    // b) one of the following must be true:
    //    i)  s is empty and we are supposed to return true for
    //        empty strings
    //    ii) this is a number >= 0

    return (isSignedInteger(s, secondArg)
         && ( (isEmpty(s) && secondArg)  || (parseInt (s,10) >= 0) ) );
}


// 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.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.
//
// 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);

    else {
        var startPos = 0;
        var secondArg = defaultEmptyOK;

        if (isSignedInteger.arguments.length > 1)
            secondArg = isSignedInteger.arguments[1];

        // skip leading + or -
        if ( (s.charAt(0) == "-") || (s.charAt(0) == "+") )
           startPos = 1;    
        return (isInteger(s.substring(startPos, s.length), secondArg))
    }
}


	
// Returns true if string s is empty or 
// whitespace characters only.

function isWhitespace (s)

{   var i;

	// Is s empty?
	if (isEmpty(s)) return true;

	// Search through string's characters one by one
	// until we find a non-whitespace character.
	// When we do, return false; if we don't, return true.

	for (i = 0; i < s.length; i++)
	{   
		// Check that current character isn't whitespace.
		var c = s.charAt(i);

		if (whitespace.indexOf(c) == -1) return false;
	}

	// All characters are whitespace.
	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 whitespace.
        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 whitespace.
        var c = s.charAt(i);
        if (bag.indexOf(c) != -1) returnString += c;
    }

    return returnString;
}


// checkFormat (TEXTFIELD theField, TEXTFIELD theFormat [, BOOLEAN emptyOK==false])
//
// Check that string theField is formated properly.
//
function checkFormat(theField,emptyOK) {
	var pattern = new RegExp;
	if ((emptyOK == true) && (isEmpty(theField.value))) return true;
	if (theField.format==5){
		pattern = /^\d{5}$/;
		if (!pattern.test(theField.value))
			return warnInvalid(theField,s5);	
	}
	if (theField.format==51) {
		pattern = /^\d{1,5}\.\d$/;	
		if (!pattern.test(theField.value))
			return warnInvalid(theField, s51);	
	}
	if (theField.format==71) {
		pattern = /^\d{7}-[a-zA-Z]$/;	
		if (!pattern.test(theField.value))
			return warnInvalid(theField, s71);	
	}
	if (theField.format==91) {
		pattern = /^\d{5}\.\d$/;
		if (!pattern.test(theField.value))
			return warnInvalid(theField,s91);	
	}
	if (theField.format==92) {
		pattern = /^\d{1,5}\.\d\d$/;
		if (!pattern.test(theField.value))
			return warnInvalid(theField,s92);	
	}
	if (theField.format==916) {
		pattern = /^\d{1,6}$/;
		if (!pattern.test(theField.value))
			return warnInvalid(theField,s916);	
	}
	return true;
}


// 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
// * there can only be ONE @ character
// * there cannot be @.
// * there cannot be .. after the @
// * there cannot be .@   (technically this is ok, but our SMTP service can't handle it)
// * there cannot be ;
// * there cannot be ,
// * there cannot be "
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

function isEmail (s)
{   if (isEmpty(s)) 
	if (isEmail.arguments.length == 1) return defaultEmptyOK;
		else return (isEmail.arguments[1] == true);
   
	var i;

	// look for @
	// there must be >= 1 character before @, so we
	// start looking at character position 1 
	// (i.e. second character)
	i = s.indexOf("@", 1);
	if (i == -1) return false;
	
	// now make sure there is not ANOTHER @ in the string!
	if (s.indexOf("@", i+1) != -1) return false;

	// make sure there is not a .. after the "@"
	if (s.indexOf("..", i+1) != -1) return false;

	// look for . (start 2 chars past the "@")
	i = s.indexOf(".", i+2);
	if (i == -1) return false;

	// there must be at least one character after the .
	if (i == (s.length-1)) return false;

	// make sure there is no "@." or ".@" or ; or , or "
	if (s.indexOf("@.", 0) != -1) return false;
	if (s.indexOf(".@", 0) != -1) return false;
	if (s.indexOf(";", 0) != -1) return false;
	if (s.indexOf(",", 0) != -1) return false;
	if (s.indexOf("\"", 0) != -1) return false;

	return true;
}

// checkEmail (TEXTFIELD theField [, BOOLEAN emptyOK==false])
//
// Check that string theField.value is a valid Email.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

function checkEmail (theField, emptyOK)
{ 
	if (vis.js >= 1.0) {
		if (checkEmail.arguments.length == 1 || !emptyOK) emptyOK = defaultEmptyOK;
		if ((emptyOK == true) && (isEmpty(theField.value))) return true;
		else
    		{ 
			var strippedEmail = stripCharsInBag(theField.value, whitespace);
     			if (!isEmail(strippedEmail, false)) 
				return warnInvalid (theField, iEmail);
     			else {
          			theField.value = strippedEmail;
          			return true;
     			}
    		}
	}
	// Default return value if version 1.0 is not supported so this call is ignored!!
	return true;
}

// U.S. phone numbers have 10 digits.
// They are formatted as 123 456 7890 or (123) 456-7890.
var digitsInUSPhoneNumber = 10;

// non-digit characters which are allowed in phone numbers
var phoneNumberDelimiters = "()-. ";


// characters which are allowed in US phone numbers
var validUSPhoneChars = digits + phoneNumberDelimiters;

// characters which are allowed in international phone numbers
// (a leading + is OK)
var validWorldPhoneChars = digits + phoneNumberDelimiters + "+";


// takes USPhone, a string of 10 digits
// and reformats as (123) 456-789

// 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.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

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 reformatUSPhone (USPhone)
{   return (reformat (USPhone, "(", 3, ") ", 3, "-", 4))
}

// 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.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

function isInternationalPhoneNumber (theField)
{
	var cCodes = new Array();
	cCodes[213] = 1;	// algeria
	cCodes[684] = 1;	// american somoa
	cCodes[297] = 1;	// aruba
	cCodes[61] = 1;		// australia
	cCodes[43] = 1;		// austria
	cCodes[973] = 1;	// bahrain
	cCodes[880] = 1;	// bangladesh
	cCodes[32] = 1;		// belgium
	cCodes[501] = 1;	// belize
	cCodes[591] = 1;	// bolivia
	cCodes[55] = 1;		// brazil
	cCodes[237] = 1;	// cameroon
	cCodes[56] = 1;		// chile
	cCodes[86] = 1;		// china
	cCodes[57] = 1;		// columbia
	cCodes[506] = 1;	// costa rica
	cCodes[357] = 1;	// cyprus
	cCodes[420] = 1;	// czech
	cCodes[45] = 1;		// denmark
	cCodes[593] = 1;	// ecuador
	cCodes[20] = 1;		// egypt
	cCodes[503] = 1;	// el salvador
	cCodes[251] = 1;	// ethiopia
	cCodes[679] = 1;	// fiji
	cCodes[358] = 1;	// finland
	cCodes[33] = 1;		// france
	cCodes[596] = 1;	// french antilles
	cCodes[689] = 1;	// french polynesia
	cCodes[241] = 1;	// gabon
	cCodes[49] = 1;		// germany
	cCodes[233] = 1;	// ghana
	cCodes[30] = 1;		// greece
	cCodes[590] = 1;	// guadeloupe
	cCodes[671] = 1;	// guam
	cCodes[53] = 1;		// guantanamo bar
	cCodes[502] = 1;	// guatemala
	cCodes[592] = 1;	// guyana
	cCodes[509] = 1;	// haiti
	cCodes[504] = 1;	// honduras
	cCodes[852] = 1;	// hong kong
	cCodes[36] = 1;		// hungary
	cCodes[354] = 1;	// iceland
	cCodes[91] = 1;		// india
	cCodes[62] = 1;		// indonesia
	cCodes[98] = 1;		// iran
	cCodes[964] = 1;	// iraq
	cCodes[353] = 1;	// ireland
	cCodes[972] = 1;	// isreal
	cCodes[39] = 1;		// italy
	cCodes[225] = 1;	// ivory coast
	cCodes[81] = 1;		// japan
	cCodes[962] = 1;	// jordan
	cCodes[254] = 1;	// kenya
	cCodes[82] = 1;		// korea
	cCodes[965] = 1;	// kuwait
	cCodes[231] = 1;	// liberia
	cCodes[218] = 1;	// libya
	cCodes[41] = 1;		// liechtenstein
	cCodes[352] = 1;	// luxemborg
	cCodes[265] = 1;	// malawi
	cCodes[60] = 1;		// malaysia
	cCodes[223] = 1;	// mali
	cCodes[356] = 1;	// malta
	cCodes[52] = 1;		// mexico
	cCodes[377] = 1;	// monaco
	cCodes[212] = 1;	// morocco
	cCodes[264] = 1;	// nambia
	cCodes[977] = 1;	// nepal
	cCodes[31] = 1;		// netherlands
	cCodes[599] = 1;	// neth antilles
	cCodes[687] = 1;	// new caledonia
	cCodes[64] = 1;		// new zealand
	cCodes[505] = 1;	// nicaragua
	cCodes[234] = 1;	// nigeria
	cCodes[47] = 1;		// norway
	cCodes[968] = 1;	// oman
	cCodes[92] = 1;		// pakistan
	cCodes[507] = 1;	// panama
	cCodes[675] = 1;	// papua new guinea
	cCodes[595] = 1;	// paraguay
	cCodes[51] = 1;		// peru
	cCodes[63] = 1;		// philipines
	cCodes[48] = 1;		// poland
	cCodes[351] = 1;	// portugal
	cCodes[974] = 1;	// qatar
	cCodes[40] = 1;		// romania
	cCodes[7] = 1;		// russia
	cCodes[670] = 1;	// saipan
	cCodes[378] = 1;	// san marino
	cCodes[966] = 1;	// saudia arabia
	cCodes[221] = 1;	// senegal
	cCodes[65] = 1;		// singapore
	cCodes[421] = 1;	// slovakia
	cCodes[27] = 1;		// south africa
	cCodes[34] = 1;		// spain
	cCodes[94] = 1;		// sri lanka
	cCodes[597] = 1;	// suriname
	cCodes[46] = 1;		// sweden
	cCodes[41] = 1;		// switzerland
	cCodes[886] = 1;	// taiwan
	cCodes[255] = 1;	// tanzania
	cCodes[66] = 1;		// thailand
	cCodes[216] = 1;	// tunisia
	cCodes[90] = 1;		// turkey
	cCodes[971] = 1;	// united arab emirates
	cCodes[44] = 1;		// U.K.
	cCodes[598] = 1;	// uraguay
	cCodes[39] = 1;		// vatican
	cCodes[58] = 1;		// venezuela
	cCodes[84] = 1;		// vietnam
	cCodes[967] = 1;	// yemen
	cCodes[243] = 1;	// zaire	
	cCodes[263] = 1;	// zimbabwe
	
	var normalizedPhone = stripCharsInBag(theField.value, phoneNumberDelimiters);
	 

	if (isEmpty(normalizedPhone)) 
		if (isInternationalPhoneNumber.arguments.length == 1)
			return defaultEmptyOK;
		else
			return (isInternationalPhoneNumber.arguments[1] == true);
	
		
	if (!isPositiveInteger(normalizedPhone)) return false;
	

	
	
	if (normalizedPhone.substring(0,3) == '011')
	{
		if (cCodes[parseInt(normalizedPhone.substring(3,5),10)] == 1)
		{
				
			if (normalizedPhone.length < 6)
				return false;
				
			theField.value = '011' + '-' + normalizedPhone.substring(3,3) + '-' + normalizedPhone.substring(5);
		}
		else if (cCodes[parseInt(normalizedPhone.substring(3,6),10)] == 1)
		{
			
				if (normalizedPhone.length < 7)
					return false;
				theField.value = '011' + '-' + normalizedPhone.substring(3,6) + '-' + normalizedPhone.substring(6);
		}
		else
			return false;
	}
	else
		return false;

	return true;
}


// checkUSPhone (TEXTFIELD theField [, BOOLEAN emptyOK==false])
//
// Check that string theField.value is a valid US Phone.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

function checkUSPhone (theField, emptyOK)
{
	if (vis.js >= 1.0) {

		if ((checkUSPhone.arguments.length == 1) || !emptyOK) emptyOK = defaultEmptyOK;
    		if ((emptyOK == true) && (isEmpty(theField.value)))
    			return true;
    		else
    		{  var normalizedPhone = stripCharsInBag(theField.value, phoneNumberDelimiters)
       			if (!isUSPhoneNumber(normalizedPhone, false)) 
          			return checkInternationalPhone(theField, emptyOK);
       			else 
       			{  // if you don't want to reformat as (123) 456-789, comment next line out
          			theField.value = reformatUSPhone(normalizedPhone)
          			return true;
       			}
    		}

	}
	//Default return value if version 1.0 is not supported so this call is ignored!!
	return true;

}

// checkInternationalPhone (TEXTFIELD theField [, BOOLEAN emptyOK==false])
//
// Check that string theField.value is a valid International Phone.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

function checkInternationalPhone(theField, emptyOK)
{
	if (vis.js >= 1.0) {
		if (checkInternationalPhone.arguments.length == 1 || !emptyOK) emptyOK = defaultEmptyOK;
			if ((emptyOK == true) && (isEmpty(theField.value)))
				return true;
			else
			{
				if (!isInternationalPhoneNumber(theField, false)) 
					return warnInvalid (theField, iPhone);
				else
					return true;
			}
	}
	//Default return value if version 1.0 is not supported so this call is ignored!!
	return true;

}

////

// non-digit characters which are allowed in 
// Social Security Numbers
var SSNDelimiters = "- ";


// characters which are allowed in Social Security Numbers
var validSSNChars = digits + SSNDelimiters;


// U.S. Social Security Numbers have 9 digits.
// They are formatted as 123-45-6789.
var digitsInSocialSecurityNumber = 9;


// 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.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

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;
}



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)
}

// takes SSN, a string of 9 digits
// and reformats as 123-45-6789

function reformatSSN (SSN)
{   return (reformat (SSN, "", 3, "-", 2, "-", 4))
}


// Check that string theField.value is a valid SSN.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

function checkSSN (theField, emptyOK)
{
	if (vis.js >= 1.0) {

		if ((checkSSN.arguments.length == 1) || !emptyOK) emptyOK = defaultEmptyOK;

    	if ((emptyOK == true) && (isEmpty(theField.value)))
			return true;
    	else
    	{  var normalizedSSN = stripCharsInBag(theField.value, SSNDelimiters)
       		if (!isSSN(normalizedSSN, false)) 
          		return warnInvalid (theField, iSSN);
       		else 
       		{  // if you don't want to reformats as 123-456-7890, comment next line out
          		theField.value = reformatSSN(normalizedSSN)
          		return true;
       		}
    	}
	}
	//Default return value if version 1.0 is not supported so this call is ignored!!
	return true;

}
//****************  Start of day checking routines


// daysInFebruary (INTEGER year)
// 
// Given integer argument year,
// returns number of days in February of that year.

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 );
}


// isYear (STRING s [, BOOLEAN emptyOK])
// 
// isYear returns true if string s is a valid 
// Year number.  Must be 2 or 4 digits only.
// 
// For Year 2000 compliance, you are advised
// to use 4-digit year numbers everywhere.
//
// And yes, this function is not Year 10000 compliant, but 
// because I am giving you 8003 years of advance notice,
// I don't feel very guilty about this ...
//
// For B.C. compliance, write your own function. ;->
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

function isYear (s)
{   if (isEmpty(s)) 
       if (isYear.arguments.length == 1) return defaultEmptyOK;
       else
        return (isYear.arguments[1] == true);
        
        
    if (!isNonnegativeInteger(s))
		 return false;
 
    //check for four digit year
    return ((s.length == 4));

}



// 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.
// 
// For explanation of optional argument emptyOK,
// see comments of function isInteger.


function isIntegerInRange (s, a, b)
{   if (isEmpty(s)) 
       if (isIntegerInRange.arguments.length == 1) return defaultEmptyOK;
       else return (isIntegerInRange.arguments[1] == true);

    // Catch non-integer strings to avoid creating a NaN below,
    // which isn't available on JavaScript 1.0 for Windows.
    if (!isInteger(s, false)) return false;

    // Now, explicitly change the type to integer via parseInt
    // so that the comparison code below will work both on 
    // JavaScript 1.2 (which typechecks in equality comparisons)
    // and JavaScript 1.1 and before (which doesn't).
    var num = parseInt (s,10);
    return ((num >= a) && (num <= b));
}



// isMonth (STRING s [, BOOLEAN emptyOK])
// 
// isMonth returns true if string s is a valid 
// month number between 1 and 12.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

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.
// 
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

function isDay (s)
{   if (isEmpty(s)) 
       if (isDay.arguments.length == 1) return defaultEmptyOK;
       else return (isDay.arguments[1] == true);   
    return isIntegerInRange (s, 1, 31);
}



// daysInFebruary (INTEGER year)
// 
// Given integer argument year,
// returns number of days in February of that year.

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 isDateString (s)
{ 

	if (isEmpty(s)) 
	if (isDateString.arguments.length == 1) return defaultEmptyOK;
		else
			return (isDateString.arguments[1] == true);
   
	// is s whitespace?
	if (isWhitespace(s)) return false;
    
	// there must be >= 1 character before @, so we
	// start looking at character position 1 
	// (i.e. second character)
	var i = 1;
	var sLength = s.length;

	// look for first /
	while ((i < sLength) && (s.charAt(i) != "/"))
	{
		 i++
	}

	if ((i >= sLength) || (s.charAt(i) != "/"))
		 return false;
	else
	{
		 i += 2;
	}

	// look for next /
	while ((i < sLength) && (s.charAt(i) != "/"))
	{ i++
	}
	// there must be 4 characters after the last /
	if ( (i == (sLength - 5)) && (s.charAt(i) == "/")   )
	
		return s.split('/');
	else
		return false;
}

// 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)
{   
	// catch invalid years (not 4-digit) and invalid months and days.
    if (! (isYear(year, false) && isMonth(month, false) && isDay(day, false))) return false;

    // Explicitly change type to integer to make code work in both
    // JavaScript 1.1 and JavaScript 1.2.
    var intYear = parseInt(year,10);
    var intMonth = parseInt(month,10);
    var intDay = parseInt(day,10);
    
	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;


    // catch invalid days, except for February
    if (intDay > daysInMonth[intMonth]) return false; 
    if ((intMonth == 2) && (intDay > daysInFebruary(intYear)))
		 return false;
    return true;
}

// checkFuture datefield
// Make sure the input date.value is not in the future
function checkFuture (theDate,emptyOK) {
	// Dont allow entry > than the current date
	if ((emptyOK == true) && (isEmpty(theDate.value))) return true;
	var today = new Date();	//Get the current date
	var test = new Date(Date.parse(theDate.value));
	if (today.getTime() < test.getTime())
		return warnInvalid (theDate, iFuture);
	return true;
}

// checkPast date,datetest,datelabel
// Make sure the input date.value is not prior to datetest
// If it is, build message indicating label of the datetest field
// ie. if you dont want to allow a date prior to a date of birth
function checkPast (theDate,theTest,theLabel,emptyOK) {
	// Dont allow entry < than the test date
	if ((emptyOK == true) && (isEmpty(theDate.value))) return true;
	var test = new Date(Date.parse(theDate.value));
	var past = new Date(Date.parse(theTest));
	if (test.getTime() < past.getTime())
		return warnInvalid (theDate, "This field cannot be prior to " + theLabel + ".\nPlease reenter it now.");
	return true;
}

// checkMinimumAge date,minimumage
// Make sure the input birthdate is over ## years of age
function checkMinimumAge (theDate,minimumage,emptyOK) {
	// Dont allow entry < than the current date - ## years
	if ((emptyOK == true) && (isEmpty(theDate.value))) return true;
	var today = new Date();
	var test = new Date(Date.parse(theDate.value));
	test.setFullYear(test.getFullYear( )+minimumage);
	if (today.getTime() < test.getTime())
		return warnInvalid (theDate, "The minimum age to subscribe to this service is " + minimumage + " years old.\n");
	return true;
}


function checkDate (theField, emptyOK)
{
	var a = makeArray(3); 
	

    // in equality comparison below.
    if (vis.js >= 1.0) {
    
		// Next line is needed on NN3 to avoid "undefined is not a number" error
		if (checkDate.arguments.length == 1 || !emptyOK) emptyOK = defaultEmptyOK;


		if ((emptyOK == true) && (isEmpty(theField.value)))
			 return true;
		 if (!(a = isDateString(theField.value))) 
			return warnInvalid (theField, iDate);
			

		// is this a proper date string format has been verified
		
		if (!isYear(a[2])) return warnInvalid (theField, iYear);
		
		// strip
		if (a[2].length > 2) a[2] = a[2].substring(a[2].length - 4);
		
		if (!isMonth(a[0])) return warnInvalid (theField, iMonth);
		
		// pad
		if (a[0].length == 1) a[0] = '0' + a[0]
		// strip
		if (a[0].length > 2) a[0] = a[0].substring(a[0].length - 2);
		
		if (!isDay(a[1])) return warnInvalid (theField, iDay);
		
		// pad
		if (a[1].length == 1) a[1] = '0' + a[1]
		// strip
		if (a[1].length > 2) a[1] = a[1].substring(a[1].length - 2);
		
		theField.value =   a[0] + "/" + a[1] + "/" + a[2]
		if (!isDate(a[2], a[0], a[1]))
			return warnInvalid (theField, iDatePrefix + iDateSuffix);

		if (theField.future==false) 
			if (!checkFuture(theField,theField.optional))
				return false;

		if (theField.past) 
			if (!checkPast(theField,theField.past,theField.pastlabel,theField.optional))
				return false;
		
		if (theField.minimumage) 
			if (!checkMinimumAge(theField,theField.minimumage,theField.optional))
				return false;

		// if made it this far then, its ok 
		return true;
	}
	
	//Default return value if version 1.0 is not supported so this call is ignored!!
	return true;

}

//This example is from JavaScript: The Definitive Guide, 3rd Edition.  
//That book and this example were Written by David Flanagan.            
//They are Copyright (c) 1996, 1997, 1998 O'Reilly & Associates.        
//This example is provided WITHOUT WARRANTY either expressed or implied.
//You may study, use, modify, and distribute it for any purpose,        
//as long as this notice is retained.                                   


// A utility function that returns true if a string contains only 
// whitespace characters.
function isblank(s)
{
    for(var i = 0; i < s.length; i++) {
        var c = s.charAt(i);
        if ((c != ' ') && (c != '\n') && (c != '\t')) return false;
    }
    return true;
}

// This is the function that performs form verification. It will be invoked
// from the onSubmit() event handler. The handler should return whatever
// value this function returns.
function verify(f)
{
	var msg;
	var empty_fields = "";
	var errors = "";
	
	if (vis.js >= 1.1 && ieVer4Up ) {
		// Loop through the elements of the form, looking for all 
		// text and textarea elements that don't have an "optional" property
		// defined. Then, check for fields that are empty and make a list of them.
		// Also, if any of these elements have a "min" or a "max" property defined,
		// then verify that they are numbers and that they are in the right range.
		// If an element hase an "excludefirst" property defined as true,
		// verify that the first value is not selected (for a list box).
		// Put together error messages for fields that are wrong.
		for(var i = 0; i < f.elements.length; i++) {
			var e = f.elements[i];
			if (((e.type == "text") || (e.type == "textarea")) && !e.optional) {
				// first check if the field is empty
				if ((e.value == null) || (e.value == "") || isblank(e.value)) {
					empty_fields += "\n          " + e.name;
					continue;
				}

				// Now check for fields that are supposed to be numeric.
				if (e.numeric || (e.min != null) || (e.max != null)) { 
					var v = parseFloat(e.value);
					if (isNaN(v) || 
						((e.min != null) && (v < e.min)) || 
						((e.max != null) && (v > e.max))) {
						errors += "- The field " + e.name + " must be a number";
						if (e.min != null) 
							errors += " that is greater than " + e.min;
						if (e.max != null && e.min != null) 
							errors += " and less than " + e.max;
						else if (e.max != null)
							errors += " that is less than " + e.max;
						errors += ".\n";
					}
				}
				if (e.positiveinteger == true) {
					if (e.value != 0 && isPositiveInteger(e.value) == false) {
						errors += "- The field " + e.name + " must be zero or a positive whole number";
						errors += ".\n";
					}
				}
			}
			if (e.excludefirst != null)
				if (e.excludefirst)
					if (e.selectedIndex == 0)
						errors += "- Please select a value for the field " + e.name + ".\n";
		}

	
		// Now, if there were any errors, display the messages, and
		// return false to prevent the form from being submitted. 
		// Otherwise check email.
		if (empty_fields || errors)
		{
			msg  = "______________________________________________________\n\n"
			msg += "The form was not submitted because of the following error(s).\n";
			msg += "Please correct these error(s) and re-submit.\n";
			msg += "______________________________________________________\n\n"

			if (empty_fields) {
				msg += "- The following required field(s) are empty:" 
						+ empty_fields + "\n";
				if (errors) msg += "\n";
			}
			msg += errors;
			alert(msg);
			return false;
		}
		else
		{
			//Since all mandatory fields have been filled in at this point
			//We verify email addresses
			
			for(var i = 0; i < f.elements.length; i++) {
				var e = f.elements[i];
				if (e.email) {
					if (!checkEmail(e,e.optional))
						return false;
				}
				if (e.ssn) {
					if (!checkSSN(e,e.optional))
						return false;
				}
				if (e.phone) {
					if (!checkUSPhone(e,e.optional))
						return false;
				}
				if (e.date) {
					if (!checkDate(e,e.optional))
						return false;
				}
				if (e.format) {
					if (!checkFormat(e,e.optional))
						return false;
				}
			}
			// all fields ok; form is verified!
			return true;
		}
	} //if in version javascript 1.1

	// if not javascript 1.1, need to default to true
	return true;
}

// This function can be called from the onSubmit event of a form.  It calls the verify()
// function first to make sure that all required fields are present (and may also check
// e-mail addresses; see above.  If verify() returns true, it then calls showConfirm()
// to give the user an "are you sure" style prompt.
function VerifyAndConfirm(TheForm, strPrompt, strHeader, strBody)
{
	if (vis.js >= 1.1) {
		if (!verify(TheForm)) {
			return false;
		}
	}
	return showConfirm(strPrompt, strHeader, strBody);
}

// Verify that at least one checkbox from a set is checked
// (checkbox names must obey a pattern: a common prefix string)
function AtLeastOne(f, prefix, msg) {
	var i, e, l;
	
	l = prefix.length;
	for (i = 0; i < f.elements.length; i++) {
		e = f.elements[i];
		if ((e.type == 'checkbox') && (e.name.length >= l) && (e.name.substring(0,l) == prefix)) {
			if (e.checked) {
				return true;
			}
		}
	}
	alert(msg);
}


