// FormChek.js
//
// SUMMARY
//
// This is a set of JavaScript functions for validating input on 
// an HTML form.  Functions are provided to validate:
//
//      - U.S. and international phone/fax numbers
//      - U.S. ZIP codes (5 or 9 digit postal codes)
//      - U.S. Postal Codes (2 letter abbreviations for names of states)
//      - U.S. Social Security Numbers (abbreviated as SSNs)
//      - email addresses
//	- dates (entry of year, month, and day and validity of combined date)
//	- credit card numbers
//
// Supporting utility functions validate that:
//
//      - characters are Letter, Digit, or LetterOrDigit
//      - strings are a Signed, Positive, Negative, Nonpositive, or
//        Nonnegative integer
//      - strings are a Float or a SignedFloat
//      - strings are Alphabetic, Alphanumeric, or Whitespace
//      - strings contain an integer within a specified range
//
// Functions are also provided to interactively check the
// above kinds of data and prompt the user if they have
// been entered incorrectly.
//
// Other utility functions are provided to:
//
// 	- remove from a string characters which are/are not 
//	  in a "bag" of selected characters	
// 	- reformat a string, adding delimiter characters
//	- strip whitespace/leading whitespace from a string
//      - reformat U.S. phone numbers, ZIP codes, and Social
//        Security numbers
//
//
// Many of the below functions take an optional parameter eok (for "emptyOK")
// which determines whether the empty string will return true or false.
// Default behavior is controlled by global variable defaultEmptyOK.
//
// BASIC DATA VALIDATION FUNCTIONS:
//
// 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.
// isSignedInteger (s [,eok])          True if all characters in string s are numbers; leading + or - allowed.
// 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.
// isFloat (s [,eok])                  True if string s is an unsigned floating point (real) number. (Integers also OK.)
// isSignedFloat (s [,eok])            True if string s is a floating point number; leading + or - allowed. (Integers also OK.)
// isAlphabetic (s [,eok])             True if string s is English letters 
// isAlphanumeric (s [,eok])           True if string s is English letters and numbers only.
// 
// isSSN (s [,eok])                    True if string s is a valid U.S. Social Security Number.
// isUSPhoneNumber (s [,eok])          True if string s is a valid U.S. Phone Number. 
// isInternationalPhoneNumber (s [,eok]) True if string s is a valid international phone 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:
//
// stripCharsInBag (s, bag)            Removes all characters in string bag from string s.
// stripCharsNotInBag (s, bag)         Removes all characters NOT in string bag from string s.
// stripWhitespace (s)                 Removes all whitespace characters from s.
// stripInitialWhitespace (s)          Removes initial (leading) whitespace characters from s.
// reformat (TARGETSTRING, STRING,     Function for inserting formatting characters or
//   INTEGER, STRING, INTEGER ... )       delimiters into TARGETSTRING.                                       
// reformatZIPCode (ZIPString)         If 9 digits, inserts separator hyphen.
// reformatSSN (SSN)                   Reformats as 123-45-6789.
// reformatUSPhone (USPhone)           Reformats as (123) 456-789.


// FUNCTIONS TO PROMPT 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 INTERACTIVELY CHECK FIELD CONTENTS:
//
// checkString (theField, s [,eok])    Check that theField.value is not empty or all whitespace.
// checkStateCode (theField)           Check that theField.value is a valid U.S. state code.
// checkZIPCode (theField [,eok])      Check that theField.value is a valid ZIP code.
// checkUSPhone (theField [,eok])      Check that theField.value is a valid US Phone.
// checkInternationalPhone (theField [,eok])  Check that theField.value is a valid International Phone.
// checkEmail (theField [,eok])        Check that theField.value is a valid Email.
// checkSSN (theField [,eok])          Check that theField.value is a valid SSN.
// checkYear (theField [,eok])         Check that theField.value is a valid Year.
// checkMonth (theField [,eok])        Check that theField.value is a valid Month.
// checkDay (theField [,eok])          Check that theField.value is a valid Day.
// checkDate (yearField, monthField, dayField, labelString, OKtoOmitDay)
//                                     Check that field values form a valid date.
// getRadioButtonValue (radio)         Get checked value from radio button.
// checkCreditCard (radio, theField)   Validate credit card info.


// CREDIT CARD DATA VALIDATION FUNCTIONS
// 
// isCreditCard (st)              True if credit card number passes the Luhn Mod-10 test.
// isVisa (cc)                    True if string cc is a valid VISA number.
// isMasterCard (cc)              True if string cc is a valid MasterCard number.
// isAmericanExpress (cc)         True if string cc is a valid American Express number.
// isDinersClub (cc)              True if string cc is a valid Diner's Club number.
// isCarteBlanche (cc)            True if string cc is a valid Carte Blanche number.
// isDiscover (cc)                True if string cc is a valid Discover card number.
// isEnRoute (cc)                 True if string cc is a valid enRoute card number.
// isJCB (cc)                     True if string cc is a valid JCB card number.
// isAnyCard (cc)                 True if string cc is a valid card number for any of the accepted types.
// isCardMatch (Type, Number)     True if Number is valid for credic card of type Type.
//
// Other stub functions are retained for backward compatibility with LivePayment code.
// See comments below for details.
//
// Performance hint: when you deploy this file on your website, strip out the
// comment lines from the source code as well as any of the functions which
// you don't need.  This will give you a smaller .js file and achieve faster
// downloads.
//
// 18 Feb 97 created Eric Krock
//
// (c) 1997 Netscape Communications Corporation



// VARIABLE DECLARATIONS

var digits = "0123456789";

var lowercaseLetters = "abcdefghijklmnopqrstuvwxyz"

var uppercaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"


// whitespace characters
var whitespace = " \t\n\r";


// decimal point character differs by language and culture
var decimalPointDelimiter = "."


// 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 + "+";


// 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;



// 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 ZIP Codes
var ZIPCodeDelimiters = "-";



// our preferred delimiter for reformatting ZIP Codes
var ZIPCodeDelimeter = "-"


// characters which are allowed in Social Security Numbers
var validZIPCodeChars = digits + ZIPCodeDelimiters



// U.S. ZIP codes have 5 or 9 digits.
// They are formatted as 12345 or 12345-6789.
var digitsInZIPCode1 = 5
var digitsInZIPCode2 = 9


// non-digit characters which are allowed in credit card numbers
var creditCardDelimiters = " "


// CONSTANT STRING DECLARATIONS
// (grouped for ease of translation and localization)

// m is an abbreviation for "missing"

var mPrefix = "You did not enter a value into the "
var mSuffix = " field. This is a required field. Please enter it now."

// s is an abbreviation for "string"

var sCoy = "Company"
var sUSLastName = "Last Name"
var sUSFirstName = "First Name"
var sWorldLastName = "Family Name"
var sWorldFirstName = "Given Name"
var sTitle = "Title"
var sCompanyName = "Company Name"
var sUSAddress = "Street Address"
var sWorldAddress = "Address"
var sCity = "City"
var sStateCode = "State Code"
var sWorldState = "State, Province, or Prefecture"
var sCountry = "Country"
var sZIPCode = "ZIP Code"
var sWorldPostalCode = "Postal Code"
var sPhone = "Phone Number"
var sFax = "Fax Number"
var sDateOfBirth = "Date of Birth"
var sExpirationDate = "Expiration Date"
var sEmail = "Email"
var sSSN = "Social Security Number"
var sCreditCardNumber = "Credit Card Number"
var sOtherInfo = "Other Information"




// i is an abbreviation for "invalid"

var iStateCode = "This field must be a valid two character U.S. state abbreviation (like CA for California). Please reenter it now."
var iZIPCode = "This field must be a 5 or 9 digit U.S. ZIP Code (like 94043). Please reenter it now."
var iUSPhone = "This field must be a 10 digit U.S. phone number (like 415 555 1212). 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."
var iEmail = "This field must be a valid email address (like foo@bar.com). Please reenter it now."
var iCreditCardPrefix = "This is not a valid "
var iCreditCardSuffix = " credit card number. (Click the link on this form to see a list of sample numbers.) Please reenter it now."
var iDay = "This field must be a day number between 1 and 31.  Please reenter it now."
var iMonth = "This field must be a month number between 1 and 12.  Please reenter it now."
var iYear = "This field must be a 2 or 4 digit year number.  Please reenter it now."
var iDatePrefix = "The Day, Month, and Year for "
var iDateSuffix = " do not form a valid date.  Please reenter them now."



// p is an abbreviation for "prompt"

var pEntryPrompt = "Please enter a "
var pStateCode = "2 character code (like CA)."
var pZIPCode = "5 or 9 digit U.S. ZIP Code (like 94043)."
var pUSPhone = "10 digit U.S. phone number (like 415 555 1212)."
var pWorldPhone = "international phone number."
var pSSN = "9 digit U.S. social security number (like 123 45 6789)."
var pEmail = "valid email address (like foo@bar.com)."
var pCreditCard = "valid credit card number."
var pDay = "day number between 1 and 31."
var pMonth = "month number between 1 and 12."
var pYear = "2 or 4 digit year number."


// Global variable defaultEmptyOK defines default return value 
// for many functions when they are passed the empty string. 
// By default, they will return defaultEmptyOK.
//
// defaultEmptyOK is false, which means that by default, 
// these functions will do "strict" validation.  Function
// isInteger, for example, will only return true if it is
// passed a string containing an integer; if it is passed
// the empty string, it will return false.
//
// You can change this default behavior globally (for all 
// functions which use defaultEmptyOK) by changing the value
// of defaultEmptyOK.
//
// Most of these functions have an optional argument emptyOK
// which allows you to override the default behavior for 
// the duration of a function call.
//
// This functionality is useful because it is possible to
// say "if the user puts anything in this field, it must
// be an integer (or a phone number, or a string, etc.), 
// but it's OK to leave the field empty too."
// This is the case for fields which are optional but which
// must have a certain kind of content if filled in.

var defaultEmptyOK = false




// 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
}



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;




// 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"




// Check whether string s is empty.

function isEmpty(s)
{   return ((s == null) || (s.length == 0))
}



// Returns true if string s is empty or 
// whitespace characters only.

function isWhitespace (s)

{   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;
}



// Removes all whitespace characters from s.
// Global variable whitespace (see above)
// defines which characters are considered whitespace.

function stripWhitespace (s)

{   return stripCharsInBag (s, whitespace)
}




// WORKAROUND FUNCTION FOR NAVIGATOR 2.0.2 COMPATIBILITY.
//
// The below function *should* be unnecessary.  In general,
// avoid using it.  Use the standard method indexOf instead.
//
// However, because of an apparent bug in indexOf on 
// Navigator 2.0.2, the below loop does not work as the
// body of stripInitialWhitespace:
//
// while ((i < s.length) && (whitespace.indexOf(s.charAt(i)) != -1))
//   i++;
//
// ... so we provide this workaround function charInString
// instead.
//
// charInString (CHARACTER c, STRING s)
//
// Returns true if single character c (actually a string)
// is contained within string s.

function charInString (c, s)
{   for (i = 0; i < s.length; i++)
    {   if (s.charAt(i) == c) return true;
    }
    return false
}



// Removes initial (leading) whitespace characters from s.
// Global variable whitespace (see above)
// defines which characters are considered whitespace.

function stripInitialWhitespace (s)

{   var i = 0;

    while ((i < s.length) && charInString (s.charAt(i), whitespace))
       i++;
    
    return s.substring (i, s.length);
}







// Returns true if character c is an English letter 
// (A .. Z, a..z).
//
// NOTE: Need i18n version to support European characters.
// This could be tricky due to different character
// sets and orderings for various languages and platforms.

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 character c is a letter or digit.

function isLetterOrDigit (c)
{   return (isLetter(c) || isDigit(c))
}



// isInteger (STRING s [, BOOLEAN emptyOK])
// 
// Returns true if all characters in string s are numbers.
//
// Accepts non-signed integers only. Does not accept floating 
// point, exponential notation, etc.
//
// We don't use parseInt because that would accept a string
// with trailing non-numeric characters.
//
// By default, returns defaultEmptyOK if s is empty.
// There is an optional second argument called emptyOK.
// emptyOK is used to override for a single function call
//      the default behavior which is specified globally by
//      defaultEmptyOK.
// If emptyOK is false (or any value other than true), 
//      the function will return false if s is empty.
// If emptyOK is true, the function will return true if s is empty.
//
// EXAMPLE FUNCTION CALL:     RESULT:
// isInteger ("5")            true 
// isInteger ("")             defaultEmptyOK
// isInteger ("-5")           false
// isInteger ("", true)       true
// isInteger ("", false)      false
// isInteger ("5", false)     true

function isInteger (s)

{   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;
}

// 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))
    }
}




// 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) > 0) ) );
}


// 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) >= 0) ) );
}






// isNegativeInteger (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 isNegativeInteger (s)
{   var secondArg = defaultEmptyOK;

    if (isNegativeInteger.arguments.length > 1)
        secondArg = isNegativeInteger.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 negative, not positive, number

    return (isSignedInteger(s, secondArg)
         && ( (isEmpty(s) && secondArg)  || (parseInt (s) < 0) ) );
}






// isNonpositiveInteger (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 isNonpositiveInteger (s)
{   var secondArg = defaultEmptyOK;

    if (isNonpositiveInteger.arguments.length > 1)
        secondArg = isNonpositiveInteger.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) <= 0) ) );
}





// isFloat (STRING s [, BOOLEAN emptyOK])
// 
// True if string s is an unsigned floating point (real) number. 
//
// Also returns true for unsigned integers. If you wish
// to distinguish between integers and floating point numbers,
// first call isInteger, then call isFloat.
//
// Does not accept exponential notation.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

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;

    // 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 ((c == decimalPointDelimiter) && !seenDecimalPoint) seenDecimalPoint = true;
        else if (!isDigit(c)) return false;
    }

    // All characters are numbers.
    return true;
}







// isSignedFloat (STRING s [, BOOLEAN emptyOK])
// 
// True if string s is a signed or unsigned floating point 
// (real) number. First character is allowed to be + or -.
//
// Also returns true for unsigned integers. If you wish
// to distinguish between integers and floating point numbers,
// first call isSignedInteger, then call isSignedFloat.
//
// Does not accept exponential notation.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

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];

        // skip leading + or -
        if ( (s.charAt(0) == "-") || (s.charAt(0) == "+") )
           startPos = 1;    
        return (isFloat(s.substring(startPos, s.length), secondArg))
    }
}




// isAlphabetic (STRING s [, BOOLEAN emptyOK])
// 
// Returns true if string s is English letters 
// (A .. Z, a..z) only.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.
//
// NOTE: Need i18n version to support European characters.
// This could be tricky due to different character
// sets and orderings for various languages and platforms.

function isAlphabetic (s)

{   var i;

    if (isEmpty(s)) 
       if (isAlphabetic.arguments.length == 1) return defaultEmptyOK;
       else return (isAlphabetic.arguments[1] == true);

    // Search through string's characters one by one
    // until we find a non-alphabetic character.
    // When we do, 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;
}




// isAlphanumeric (STRING s [, BOOLEAN emptyOK])
// 
// Returns true if string s is English letters 
// (A .. Z, a..z) and numbers only.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.
//
// NOTE: Need i18n version to support European characters.
// This could be tricky due to different character
// sets and orderings for various languages and platforms.

function isAlphanumeric (s)

{   var i;

    if (isEmpty(s)) 
       if (isAlphanumeric.arguments.length == 1) return defaultEmptyOK;
       else return (isAlphanumeric.arguments[1] == true);

    // Search through string's characters one by one
    // until we find a non-alphanumeric 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 or letter.
        var c = s.charAt(i);

        if (! (isLetter(c) || isDigit(c) ) )
        return false;
    }

    // All characters are numbers or letters.
    return true;
}




// reformat (TARGETSTRING, STRING, INTEGER, STRING, INTEGER ... )       
//
// Handy function for arbitrarily inserting formatting characters
// or delimiters of various kinds within TARGETSTRING.
//
// reformat takes one named argument, a string s, and any number
// of other arguments.  The other arguments must be integers or
// strings.  These other arguments specify how string s is to be
// reformatted and how and where other strings are to be inserted
// into it.
//
// reformat processes the other arguments in order one by one.
// * If the argument is an integer, reformat appends that number 
//   of sequential characters from s to the resultString.
// * If the argument is a string, reformat 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:
//   reformat("1234567890", "(", 3, ") ", 3, "-", 4)
//
// * To reformat a 9-digit U.S. Social Security number from
//   "123456789" to "123-45-6789" make this function call:
//   reformat("123456789", "", 3, "-", 2, "-", 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 function stripCharsNotInBag to remove the unwanted 
// characters, THEN call function reformat to delimit as desired.
//
// EXAMPLE:
//
// reformat (stripCharsNotInBag ("123 456 7890", digits),
//           "(", 3, ") ", 3, "-", 4)

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;
}




// 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 isSSN (s)
{   if (isEmpty(s)) 
       if (isSSN.arguments.length == 1) return defaultEmptyOK;
       else return (isSSN.arguments[1] == true);
    return (isInteger(s) && s.length == digitsInSocialSecurityNumber)
}




// isUSPhoneNumber (STRING s [, BOOLEAN emptyOK])
// 
// isUSPhoneNumber returns true if string s is a valid U.S. Phone
// Number.  Must be 10 digits.
//
// NOTE: Strip out any delimiters (spaces, hyphens, parentheses, etc.)
// from string s before calling this function.
//
// 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)
}




// 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 (s)
{   if (isEmpty(s)) 
       if (isInternationalPhoneNumber.arguments.length == 1) return defaultEmptyOK;
       else return (isInternationalPhoneNumber.arguments[1] == true);
    return (isPositiveInteger(s))
}




// isZIPCode (STRING s [, BOOLEAN emptyOK])
// 
// isZIPCode returns true if string s is a valid 
// U.S. ZIP code.  Must be 5 or 9 digits only.
//
// NOTE: Strip out any delimiters (spaces, hyphens, etc.)
// from string s before calling this function.  
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

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)))
}





// isStateCode (STRING s [, BOOLEAN emptyOK])
// 
// Return true if s is a valid U.S. Postal Code 
// (abbreviation for state).
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

function isStateCode(s)
{   if (isEmpty(s)) 
       if (isStateCode.arguments.length == 1) return defaultEmptyOK;
       else return (isStateCode.arguments[1] == true);
    return ( (USStateCodes.indexOf(s) != -1) &&
             (s.indexOf(USStateCodeDelimiter) == -1) )
}




// 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
//
// 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);
   
    // 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 @
    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;
}





// 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;
    return ((s.length == 2) || (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);
    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 );
}



// 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 2- or 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);
    var intMonth = parseInt(month);
    var intDay = parseInt(day);

    // catch invalid days, except for February
    if (intDay > daysInMonth[intMonth]) return false; 

    if ((intMonth == 2) && (intDay > daysInFebruary(intYear))) return false;

    return true;
}




/* FUNCTIONS TO NOTIFY USER OF INPUT REQUIREMENTS OR MISTAKES. */


// Display prompt string s in status bar.

function prompt (s)
{   window.status = s
}



// Display data entry prompt string s in status bar.

function promptEntry (s)
{   window.status = pEntryPrompt + s
}




// Notify user that required field theField is empty.
// String s describes expected contents of theField.value.
// Put focus in theField and return false.

function warnEmpty (theField, s)
{   theField.focus()
    alert(mPrefix + s + mSuffix)
    return false
}



// 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(s)
    return false
}




/* FUNCTIONS TO INTERACTIVELY CHECK VARIOUS FIELDS. */

// checkString (TEXTFIELD theField, STRING s, [, BOOLEAN emptyOK==false])
//
// Check that string theField.value is not all whitespace.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

function checkString (theField, s, emptyOK)
{   // Next line is needed on NN3 to avoid "undefined is not a number" error
    // in equality comparison below.
    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;
}



// checkStateCode (TEXTFIELD theField [, BOOLEAN emptyOK==false])
//
// Check that string theField.value is a valid U.S. state code.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

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;
    }
}



// takes ZIPString, a string of 5 or 9 digits;
// if 9 digits, inserts separator hyphen

function reformatZIPCode (ZIPString)
{   if (ZIPString.length == 5) return ZIPString;
    else return (reformat (ZIPString, "", 5, "-", 4));
}




// checkZIPCode (TEXTFIELD theField [, BOOLEAN emptyOK==false])
//
// Check that string theField.value is a valid ZIP code.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

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 
      {  // if you don't want to insert a hyphen, comment next line out
         theField.value = reformatZIPCode(normalizedZIP)
         return true;
      }
    }
}



// takes USPhone, a string of 10 digits
// and reformats as (123) 456-789

function reformatUSPhone (USPhone)
{   return (reformat (USPhone, "(", 3, ") ", 3, "-", 4))
}



// 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 (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 warnInvalid (theField, iUSPhone);
       else 
       {  // if you don't want to reformat as (123) 456-789, comment next line out
          theField.value = reformatUSPhone(normalizedPhone)
          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 (checkInternationalPhone.arguments.length == 1) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    else
    {  if (!isInternationalPhoneNumber(theField.value, false)) 
          return warnInvalid (theField, iWorldPhone);
       else 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 (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;
}



// 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 (checkSSN.arguments.length == 1) 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;
       }
    }
}




// Check that string theField.value is a valid Year.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

function checkYear (theField, emptyOK)
{   if (checkYear.arguments.length == 1) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    if (!isYear(theField.value, false)) 
       return warnInvalid (theField, iYear);
    else return true;
}


// Check that string theField.value is a valid Month.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

function checkMonth (theField, emptyOK)
{   if (checkMonth.arguments.length == 1) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    if (!isMonth(theField.value, false)) 
       return warnInvalid (theField, iMonth);
    else return true;
}


// Check that string theField.value is a valid Day.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

function checkDay (theField, emptyOK)
{   if (checkDay.arguments.length == 1) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    if (!isDay(theField.value, false)) 
       return warnInvalid (theField, iDay);
    else return true;
}



// checkDate (yearField, monthField, dayField, STRING labelString [, OKtoOmitDay==false])
//
// Check that yearField.value, monthField.value, and dayField.value 
// form a valid date.
//
// If they don't, labelString (the name of the date, like "Birth Date")
// is displayed to tell the user which date field is invalid.
//
// If it is OK for the day field to be empty, set optional argument
// OKtoOmitDay to true.  It defaults to false.

function checkDate (yearField, monthField, dayField, labelString, OKtoOmitDay)
{   // Next line is needed on NN3 to avoid "undefined is not a number" error
    // in equality comparison below.
    if (checkDate.arguments.length == 4) OKtoOmitDay = false;
    if (!isYear(yearField.value)) return warnInvalid (yearField, iYear);
    if (!isMonth(monthField.value)) return warnInvalid (monthField, iMonth);
    if ( (OKtoOmitDay == true) && isEmpty(dayField.value) ) return true;
    else if (!isDay(dayField.value)) 
       return warnInvalid (dayField, iDay);
    if (isDate (yearField.value, monthField.value, dayField.value))
       return true;
    alert (iDatePrefix + labelString + iDateSuffix)
    return false
}



// Get checked value from radio button.

function getRadioButtonValue (radio)
{   for (var i = 0; i < radio.length; i++)
    {   if (radio[i].checked) { break }
    }
    return radio[i].value
}




// Validate credit card info.

function checkCreditCard (radio, theField)
{   var cardType = getRadioButtonValue (radio)
    var normalizedCCN = stripCharsInBag(theField.value, creditCardDelimiters)
    if (!isCardMatch(cardType, normalizedCCN)) 
       return warnInvalid (theField, iCreditCardPrefix + cardType + iCreditCardSuffix);
    else 
    {  theField.value = normalizedCCN
       return true
    }
}



/*  ================================================================
    Credit card verification functions
    Originally included as Starter Application 1.0.0 in LivePayment.
    20 Feb 1997 modified by egk:
           changed naming convention to initial lowercase
                  (isMasterCard instead of IsMasterCard, etc.)
           changed isCC to isCreditCard
           retained functions named with older conventions from
                  LivePayment as stub functions for backward 
                  compatibility only
           added "AMERICANEXPRESS" as equivalent of "AMEX" 
                  for naming consistency 
    ================================================================ */


/*  ================================================================
    FUNCTION:  isCreditCard(st)
 
    INPUT:     st - a string representing a credit card number

    RETURNS:  true, if the credit card number passes the Luhn Mod-10
		    test.
	      false, otherwise
    ================================================================ */

function isCreditCard(st) {
  // Encoding only works on cards with less than 19 digits
  if (st.length > 19)
    return (false);

  sum = 0; mul = 1; l = st.length;
  for (i = 0; i < l; i++) {
    digit = st.substring(l-i-1,l-i);
    tproduct = parseInt(digit ,10)*mul;
    if (tproduct >= 10)
      sum += (tproduct % 10) + 1;
    else
      sum += tproduct;
    if (mul == 1)
      mul++;
    else
      mul--;
  }
// Uncomment the following line to help create credit card numbers
// 1. Create a dummy number with a 0 as the last digit
// 2. Examine the sum written out
// 3. Replace the last digit with the difference between the sum and
//    the next multiple of 10.

//  document.writeln("<BR>Sum      = ",sum,"<BR>");
//  alert("Sum      = " + sum);

  if ((sum % 10) == 0)
    return (true);
  else
    return (false);

} // END FUNCTION isCreditCard()



/*  ================================================================
    FUNCTION:  isVisa()
 
    INPUT:     cc - a string representing a credit card number

    RETURNS:  true, if the credit card number is a valid VISA number.
		    
	      false, otherwise

    Sample number: 4111 1111 1111 1111 (16 digits)
    ================================================================ */

function isVisa(cc)
{
  if (((cc.length == 16) || (cc.length == 13)) &&
      (cc.substring(0,1) == 4))
    return isCreditCard(cc);
  return false;
}  // END FUNCTION isVisa()




/*  ================================================================
    FUNCTION:  isMasterCard()
 
    INPUT:     cc - a string representing a credit card number

    RETURNS:  true, if the credit card number is a valid MasterCard
		    number.
		    
	      false, otherwise

    Sample number: 5500 0000 0000 0004 (16 digits)
    ================================================================ */

function isMasterCard(cc)
{
  firstdig = cc.substring(0,1);
  seconddig = cc.substring(1,2);
  if ((cc.length == 16) && (firstdig == 5) &&
      ((seconddig >= 1) && (seconddig <= 5)))
    return isCreditCard(cc);
  return false;

} // END FUNCTION isMasterCard()





/*  ================================================================
    FUNCTION:  isAmericanExpress()
 
    INPUT:     cc - a string representing a credit card number

    RETURNS:  true, if the credit card number is a valid American
		    Express number.
		    
	      false, otherwise

    Sample number: 340000000000009 (15 digits)
    ================================================================ */

function isAmericanExpress(cc)
{
  firstdig = cc.substring(0,1);
  seconddig = cc.substring(1,2);
  if ((cc.length == 15) && (firstdig == 3) &&
      ((seconddig == 4) || (seconddig == 7)))
    return isCreditCard(cc);
  return false;

} // END FUNCTION isAmericanExpress()




/*  ================================================================
    FUNCTION:  isDinersClub()
 
    INPUT:     cc - a string representing a credit card number

    RETURNS:  true, if the credit card number is a valid Diner's
		    Club number.
		    
	      false, otherwise

    Sample number: 30000000000004 (14 digits)
    ================================================================ */

function isDinersClub(cc)
{
  firstdig = cc.substring(0,1);
  seconddig = cc.substring(1,2);
  if ((cc.length == 14) && (firstdig == 3) &&
      ((seconddig == 0) || (seconddig == 6) || (seconddig == 8)))
    return isCreditCard(cc);
  return false;
}



/*  ================================================================
    FUNCTION:  isCarteBlanche()
 
    INPUT:     cc - a string representing a credit card number

    RETURNS:  true, if the credit card number is a valid Carte
		    Blanche number.
		    
	      false, otherwise
    ================================================================ */

function isCarteBlanche(cc)
{
  return isDinersClub(cc);
}




/*  ================================================================
    FUNCTION:  isDiscover()
 
    INPUT:     cc - a string representing a credit card number

    RETURNS:  true, if the credit card number is a valid Discover
		    card number.
		    
	      false, otherwise

    Sample number: 6011000000000004 (16 digits)
    ================================================================ */

function isDiscover(cc)
{
  first4digs = cc.substring(0,4);
  if ((cc.length == 16) && (first4digs == "6011"))
    return isCreditCard(cc);
  return false;

} // END FUNCTION isDiscover()





/*  ================================================================
    FUNCTION:  isEnRoute()
 
    INPUT:     cc - a string representing a credit card number

    RETURNS:  true, if the credit card number is a valid enRoute
		    card number.
		    
	      false, otherwise

    Sample number: 201400000000009 (15 digits)
    ================================================================ */

function isEnRoute(cc)
{
  first4digs = cc.substring(0,4);
  if ((cc.length == 15) &&
      ((first4digs == "2014") ||
       (first4digs == "2149")))
    return isCreditCard(cc);
  return false;
}



/*  ================================================================
    FUNCTION:  isJCB()
 
    INPUT:     cc - a string representing a credit card number

    RETURNS:  true, if the credit card number is a valid JCB
		    card number.
		    
	      false, otherwise
    ================================================================ */

function isJCB(cc)
{
  first4digs = cc.substring(0,4);
  if ((cc.length == 16) &&
      ((first4digs == "3088") ||
       (first4digs == "3096") ||
       (first4digs == "3112") ||
       (first4digs == "3158") ||
       (first4digs == "3337") ||
       (first4digs == "3528")))
    return isCreditCard(cc);
  return false;

} // END FUNCTION isJCB()



/*  ================================================================
    FUNCTION:  isAnyCard()
 
    INPUT:     cc - a string representing a credit card number

    RETURNS:  true, if the credit card number is any valid credit
		    card number for any of the accepted card types.
		    
	      false, otherwise
    ================================================================ */

function isAnyCard(cc)
{
  if (!isCreditCard(cc))
    return false;
  if (!isMasterCard(cc) && !isVisa(cc) && !isAmericanExpress(cc) && !isDinersClub(cc) &&
      !isDiscover(cc) && !isEnRoute(cc) && !isJCB(cc)) {
    return false;
  }
  return true;

} // END FUNCTION isAnyCard()



/*  ================================================================
    FUNCTION:  isCardMatch()
 
    INPUT:    cardType - a string representing the credit card type
	      cardNumber - a string representing a credit card number

    RETURNS:  true, if the credit card number is valid for the particular
	      credit card type given in "cardType".
		    
	      false, otherwise
    ================================================================ */

function isCardMatch (cardType, cardNumber)
{

	cardType = cardType.toUpperCase();
	var doesMatch = true;

	if ((cardType == "VISA") && (!isVisa(cardNumber)))
		doesMatch = false;
	if ((cardType == "MASTERCARD") && (!isMasterCard(cardNumber)))
		doesMatch = false;
	if ( ( (cardType == "AMERICANEXPRESS") || (cardType == "AMEX") )
                && (!isAmericanExpress(cardNumber))) doesMatch = false;
	if ((cardType == "DISCOVER") && (!isDiscover(cardNumber)))
		doesMatch = false;
	if ((cardType == "JCB") && (!isJCB(cardNumber)))
		doesMatch = false;
	if ((cardType == "DINERS") && (!isDinersClub(cardNumber)))
		doesMatch = false;
	if ((cardType == "CARTEBLANCHE") && (!isCarteBlanche(cardNumber)))
		doesMatch = false;
	if ((cardType == "ENROUTE") && (!isEnRoute(cardNumber)))
		doesMatch = false;
	return doesMatch;

}  // END FUNCTION CardMatch()




/*  ================================================================
    The below stub functions are retained for backward compatibility
    with the original LivePayment code so that it should be possible
    in principle to swap in this new module as a replacement for the  
    older module without breaking existing code.  (There are no
    guarantees, of course, but it should work.)

    When writing new code, do not use these stub functions; use the
    functions defined above.
    ================================================================ */

function IsCC (st) {
    return isCreditCard(st);
}

function IsVisa (cc)  {
  return isVisa(cc);
}

function IsVISA (cc)  {
  return isVisa(cc);
}

function IsMasterCard (cc)  {
  return isMasterCard(cc);
}

function IsMastercard (cc)  {
  return isMasterCard(cc);
}

function IsMC (cc)  {
  return isMasterCard(cc);
}

function IsAmericanExpress (cc)  {
  return isAmericanExpress(cc);
}

function IsAmEx (cc)  {
  return isAmericanExpress(cc);
}

function IsDinersClub (cc)  {
  return isDinersClub(cc);
}

function IsDC (cc)  {
  return isDinersClub(cc);
}

function IsDiners (cc)  {
  return isDinersClub(cc);
}

function IsCarteBlanche (cc)  {
  return isCarteBlanche(cc);
}

function IsCB (cc)  {
  return isCarteBlanche(cc);
}

function IsDiscover (cc)  {
  return isDiscover(cc);
}

function IsEnRoute (cc)  {
  return isEnRoute(cc);
}

function IsenRoute (cc)  {
  return isEnRoute(cc);
}

function IsJCB (cc)  {
  return isJCB(cc);
}

function IsAnyCard(cc)  {
  return isAnyCard(cc);
}

function IsCardMatch (cardType, cardNumber)  {
  return isCardMatch (cardType, cardNumber);
}

// Quick find jump routine
function JumpToIt(drpdwn) {
 var val = drpdwn.options[drpdwn.selectedIndex].value
 if (val != "")
  top.frames['Main'].location.href=val
}

// Copy fields from generated quote into hidden variables so they can be read on 
// the quote confirmation page
function SetQuoteFormFields(form) {

 //General quote details
 var table = document.getElementById('AutoNumber8');
 form.elements["QteNbr"].value = table.rows[0].cells[1].innerText;
 form.elements["QteDte"].value = table.rows[1].cells[1].innerText;
 form.elements["QteVldTil"].value = table.rows[2].cells[1].innerText;

 //Customer details
 var table = document.getElementById('AutoNumber7');
 form.elements["CoyNmeEdt"].value = table.rows[0].cells[1].innerText;
 form.elements["SurNmeEdt"].value = table.rows[1].cells[1].innerText;
 form.elements["PstAdd1Edt"].value = table.rows[2].cells[1].innerText;
 form.elements["PstAdd2Edt"].value = table.rows[3].cells[1].innerText;
 form.elements["EmlEdt"].value = table.rows[4].cells[1].innerText;
 form.elements["PhnEdt"].value = table.rows[5].cells[1].innerText;
 form.elements["FaxEdt"].value = table.rows[6].cells[1].innerText;
 form.elements["CmtEdt"].value = table.rows[7].cells[1].innerText;

 // License fields 
 table = document.getElementById('AutoNumber6');
 form.elements["LicItm1"].value = table.rows[1].cells[0].innerText;
 form.elements["LicItm2"].value = table.rows[1].cells[1].innerText;
 form.elements["LicItm3"].value = table.rows[1].cells[2].innerText;
 form.elements["LicItm4"].value = table.rows[1].cells[3].innerText;

 // Support fields 
 form.elements["SupItm1"].value = table.rows[2].cells[0].innerText;
 form.elements["SupItm2"].value = table.rows[2].cells[1].innerText;
 form.elements["SupItm3"].value = table.rows[2].cells[2].innerText;
 form.elements["SupItm4"].value = table.rows[2].cells[3].innerText;

 // Config fields 
 form.elements["CfgItm1"].value = table.rows[3].cells[0].innerText;
 form.elements["CfgItm2"].value = table.rows[3].cells[1].innerText;
 form.elements["CfgItm3"].value = table.rows[3].cells[2].innerText;
 form.elements["CfgItm4"].value = table.rows[3].cells[3].innerText;

 // Installation fields 
 form.elements["InstItm1"].value = table.rows[4].cells[0].innerText;
 form.elements["InstItm2"].value = table.rows[4].cells[1].innerText;
 form.elements["InstItm3"].value = table.rows[4].cells[2].innerText;
 form.elements["InstItm4"].value = table.rows[4].cells[3].innerText;

 // Training fields 
 form.elements["TrnItm1"].value = table.rows[5].cells[0].innerText;
 form.elements["TrnItm2"].value = table.rows[5].cells[1].innerText;
 form.elements["TrnItm3"].value = table.rows[5].cells[2].innerText;
 form.elements["TrnItm4"].value = table.rows[5].cells[3].innerText;

 // Training special fields 
 form.elements["TrnItm11"].value = table.rows[6].cells[0].innerText;
 form.elements["TrnItm21"].value = table.rows[6].cells[1].innerText;
 form.elements["TrnItm31"].value = table.rows[6].cells[2].innerText;
 form.elements["TrnItm41"].value = table.rows[6].cells[3].innerText;

 // Swipe special fields 
 form.elements["SwpItm1"].value = table.rows[7].cells[0].innerText;
 form.elements["SwpItm2"].value = table.rows[7].cells[1].innerText;
 form.elements["SwpItm3"].value = table.rows[7].cells[2].innerText;
 form.elements["SwpItm4"].value = table.rows[7].cells[3].innerText;

 // User manual fields 
 form.elements["UsrMan1"].value = table.rows[8].cells[0].innerText;
 form.elements["UsrMan2"].value = table.rows[8].cells[1].innerText;
 form.elements["UsrMan3"].value = table.rows[8].cells[2].innerText;
 form.elements["UsrMan4"].value = table.rows[8].cells[3].innerText;

 // Quote totals manual fields 
 form.elements["QteSubTTL"].value = table.rows[9].cells[2].innerText;
 form.elements["QteGSTTTL"].value = table.rows[10].cells[2].innerText;
 form.elements["QteTTL"].value = table.rows[11].cells[2].innerText;
 
 return true
}

function SetQuoteDateTime() {
  var table = document.getElementById('AutoNumber8');
  var now = new Date();
  var stadte = new Date(2004,1,1);
  var d  = now.getDate();
  var day = (d < 10) ? '0' + d : d;
  var m = now.getMonth() + 1;
  var month = (m < 10) ? '0' + m : m;
  var yy = now.getYear();
  var year = (yy < 1000) ? yy + 1900 : yy;
  table.rows[0].cells[1].innerText ="Q" +  timeDifference(now, stadte);
  table.rows[1].cells[1].innerText = day + "/" + month + "/" + year;
}

function timeDifference(laterdate,earlierdate) {
  var difference = laterdate.getTime() - earlierdate.getTime();
  var secondsDifference = Math.floor(difference/1000);
  return secondsDifference;
}

// Format number
function FormatNumber(Number,Decimals,Separator) {
  Number += ""          // Force argument to string.
  Decimals += ""        // Force argument to string.
  Separator += ""       // Force argument to string.
  if((Separator == "") || (Separator.length > 1))
    Separator = "."
  if(Number.length == 0)
    Number = "0"
  var OriginalNumber = Number  // Save for number too large.
  var Sign = 1
  var Pad = ""
  var Count = 0
  // If no number passed, force number to 0.
  if(parseFloat(Number)) {
    Number = parseFloat(Number)} 
  else {
    Number = 0 
    }
 // If no decimals passed, default decimals to 2.
 if((parseInt(Decimals,10)) || (parseInt(Decimals,10) == 0)) {
   Decimals = parseInt(Decimals,10)} 
 else {
    Decimals = 2 }
 if(Number < 0)  {
   Sign = -1         // Remember sign of Number.
   Number *= Sign    // Force absolute value of Number.
   }
 if(Decimals < 0)
   Decimals *= -1    // Force absolute value of Decimals.
 // Next, convert number to rounded integer and force to string value.
 // (Number contains 1 extra digit used to force rounding)
 Number = "" + Math.floor(Number * Math.pow(10,Decimals + 1) + 5)
 if((Number.substring(1,2) == '.')||((Number + '')=='NaN'))
   return(OriginalNumber) // Number too large to format as specified.
 // If length of Number is less than number of decimals requested +1,
 // pad with zeros to requested length.
 if(Number.length < Decimals +1)  {
   for(Count = Number.length; Count <= Decimals; Count++)
     Pad += "0"
   }
 Number = Pad + Number // Pad number as needed.
 if(Decimals == 0) {
   // Drop extra digit -- Decimal portion is formatted.
   Number = Number.substring(0, Number.length -1)} 
 else {
   // Or, format number with decimal point and drop extra decimal digit.
   Number = Number.substring(0,Number.length - Decimals -1) + Separator +     Number.substring(Number.length - Decimals -1,  Number.length -1)}
   if((Number == "") || (parseFloat(Number) < 1))
     Number="0"+Number // Force leading 0 for |Number| less than 1.
   if(Sign == -1)
     Number = "-" + Number  // Set sign of number.
 return(Number)
}

// Validate fields on software registration form
function IsRegisterValid(form)
{   
  // How heard field validation
  if ((form.elements["HowHeard"].value == "* Please Select") &&
     (isWhitespace(form.elements["OtherHeard"].value)))
  {
   alert("Please select how you found out about ClockOn.")
   form.elements["HowHeard"].focus()
   return false
   }

  // Surname validation
  if (isWhitespace(form.elements["Surname"].value))
  {
   alert("Please enter your surname.")
   form.elements["Surname"].focus()
   return false
   }

  // Given names validation
  if (isWhitespace(form.elements["GivenNames"].value))
  {
   alert("Please enter your given names.")
   form.elements["GivenNames"].focus()
   return false
   }

  // Email validation
  if (form.elements["HowToSend"][0].checked) 
  {
    if (isWhitespace(form.elements["Email"].value))
  	{
      alert("You have selected Email as your preferred contact method but you have not provided an Email Address.")
      form.elements["Email"].focus()
      return false
	 }
  	else
  	{
      if (isEmail(form.elements["Email"].value, false)==false)
      {
       alert("This field must be a valid email address i.e. admin@clockon.com.au.")
       form.elements["Email"].focus()
       return false
      }
      else 
        return true
        }
   }
   
  // Phone validation
  if (form.elements["HowToSend"][1].checked) 
  {
    if (isWhitespace(form.elements["Phone"].value))
  	{
      alert("You have selected phone as your preferred contact method but you have not provided a phone number.")
      form.elements["Phone"].focus()
      return false
	 }
  	else
  	{
      if (isInternationalPhoneNumber(stripWhitespace (form.elements["Phone"].value), false)==false)
      {
       alert("This field must be a valid phone number.")
       form.elements["Phone"].focus()
       return false
      }
      else return true
        }
   }
   
  // Mail validation
  if (form.elements["HowToSend"][2].checked) 
  {
    if ((isWhitespace(form.elements["Address1"].value)) &&
        (isWhitespace(form.elements["Address2"].value)))       
  	{
      alert("You have selected mail as your preferred contact method but you have not provided a postal address.")
      form.elements["Address1"].focus()
      return false
	 }
    else return true
  }
}

// Quote Valid
function IsQuoteValid(form) {   

  // Version field validation
  if (form.elements["VerLst"].value == "* Please select software version or product") 
  {
  	alert("Please select the version of ClockOn or product you are interested in.");
		form.elements["VerLst"].focus();
		return false;
   	}

  // Employee quantity validation
  if (form.elements["VerLst"].value != "Magnetic Swipe Card Reader(s)"){
	  if (!isPositiveInteger(form.elements["EmpQtyEdt"].value)) {
	  	alert("Please enter the total number of employees in your company (must be greater than 0).");
			form.elements["EmpQtyEdt"].focus();
			return false;
		  }
	 	else 
	 		if (((form.elements["VerLst"].value == "Lite")||(form.elements["VerLst"].value == "Classic")) && 
	 			(form.elements["EmpQtyEdt"].value > 50)) {
	    	alert("This version of ClockOn has a maximum of 50 employees. For more employees you must select Professional or Enterprise.");
		  	form.elements["EmpQtyEdt"].focus();
		  	return false;
		    }
	 		else 
	 			if ((form.elements["VerLst"].value == "TA") && (form.elements["EmpQtyEdt"].value > 50)) {
	    		alert("This version of ClockOn has a maximum of 50 employees. For more employees you must contact ClockOn on +61 2 4342 5896 for a quotation.");
		  		form.elements["EmpQtyEdt"].focus();
		  		return false;
		    	}		    		    
		}
	    
  // Magnetic card quantity validation
  if (form.elements["VerLst"].value != "Magnetic Swipe Card Reader(s)"){
	  if (!isNonnegativeInteger(form.elements["MagCrdQtyEdt"].value)) {
	  	alert("Please enter the total number of magnetic slot card readers you require (enter 0 for none).");
	   	form.elements["MagCrdQtyEdt"].focus();
	   	return false;
  	 	}
  	} 	
	else
	  // Magnetic card quantity validation
	  if ((form.elements["VerLst"].value == "Magnetic Swipe Card Reader(s)")&&(!isPositiveInteger(form.elements["MagCrdQtyEdt"].value))) {
	  	alert("Please enter the total number of magnetic slot card readers you require (must be greater than 0).");
	   	form.elements["MagCrdQtyEdt"].focus();
	   	return false;
	   	}

  // Installation location quantity validation
  if (!isPositiveInteger(form.elements["LocQtyEdt"].value)) {
   	alert("Please enter the total number of locations you wish to install ClockOn at (must be greater than 0).");
   	form.elements["LocQtyEdt"].focus();
   	return false;
   	}

  // On-site installation required
  if ((form.elements["InstRqdChk"].checked) && (!isPositiveInteger(form.elements["InstQtyEdt"].value))) {
    alert("Please enter the total number on-site installations required (must be greater than 0).");
    form.elements["InstQtyEdt"].focus();
    return false;
    }

  // On-site installation required
  if ((form.elements["InstRqdChk"].checked) && (form.elements["InstQtyEdt"].value>form.elements["LocQtyEdt"].value)) {
    alert("The number of on-site installation locations cannot be greater than the total number of locations.");
    form.elements["InstQtyEdt"].focus();
    return false;
    }

  // On-site training required
	if ((form.elements["TrnRqdChk"].checked) && (!isPositiveInteger(form.elements["TrnQtyEdt"].value))) {
	 	alert("Please enter the total number on-site training sessions required (must be greater than 0).");
	  form.elements["TrnQtyEdt"].focus();
	  return false;
   }

  // On-site training required
  if ((form.elements["TrnRqdChk"].checked) && (form.elements["TrnQtyEdt"].value>form.elements["LocQtyEdt"].value)) {
    alert("The number of on-site training locations cannot be greater than the total number of locations.");
    form.elements["TrnQtyEdt"].focus();
    return false;
    }
 	  
 	// Remote surcharge required  
  if ((form.elements["RmtLocChk"].checked) && ((!form.elements["TrnRqdChk"].checked)&&(!form.elements["InstRqdChk"].checked))) {
    alert("You must select at least one on-site Easy-Way service for this charge to apply.");
    form.elements["RmtLocChk"].focus();
    return false;
    }
 	  
	// Printed manual validation
	if (!isNonnegativeInteger(form.elements["PrnManQtyEdt"].value)) {
	 	alert("Please enter the total number of printed manuals you require (enter 0 for none).");
	 	form.elements["PrnManQtyEdt"].focus();
	 	return false;
		}

  // Attempt to calculate quote values, if successful, formulate email and results
  // fields and submit the form successfully
  if ((SpecialsValid(form)==true) && (ContactDetailsValid(form) == true) && (CalculateQuote(form) == true)) {
  	return true;
	  }
  return false;  
}


function SpecialsValid(form) {

	// Discount on software base price
	if (form.elements["SpcCbx"].value == "SpcBsePrc") {
		if ((form.elements["VerLst"].value != "Lite") && (form.elements["VerLst"].value != "Classic") && 
		(form.elements["VerLst"].value != "TA")) {  
		alert("You must select either Lite, Classic or Time and Attendance to obtain this special");
		form.elements["VerLst"].focus();
		return false;
		}
	}

	// Discount on magnetic swipe card readers
	if (((form.elements["SpcCbx"].value == "SpcSwpCrd") && (form.elements["MagCrdQtyEdt"].value <= 0))) {
	alert("You must order at least 1 swipe card reader to obtain this special discount");
	form.elements["MagCrdQtyEdt"].focus();
	return false;
	}

	// Free swipe card with Professional or Enterprise
	if (form.elements["SpcCbx"].value == "SpcFreSwpCrd") {
		if ((form.elements["VerLst"].value != "Professional") && (form.elements["VerLst"].value != "Enterprise")) {  
			alert("You must order either Professional or Enterprise to obtain this special");
			form.elements["VerLst"].focus();
			return false;
			}
	
		if (form.elements["EmpQtyEdt"].value < 20) {
			alert("You must select at least 20 employees to obtain this special");
			form.elements["EmpQtyEdt"].focus();
			return false;
			}			
		}

	// If made it here then specials are valid
	return true;	
}

// Verify the basic user contact fields are valid
function ContactDetailsValid(form) {

	// Company name validation
  if (isWhitespace(form.elements["CoyNmeEdt"].value)) {
   	alert("Please enter your company/trading name. Enter your surname if you are not sure.");
   	form.elements["CoyNmeEdt"].focus();
   	return false;
   	}

  // Surname validation
  if (isWhitespace(form.elements["SurNmeEdt"].value)) {
   	alert("Please enter your surname.");
   	form.elements["SurNmeEdt"].focus();
   	return false;
   	}

  // Given names validation
  if (isWhitespace(form.elements["GvnNmeEdt"].value)) {
   	alert("Please enter your given names.");
   	form.elements["GvnNmeEdt"].focus();
   	return false;
   	}
  
  // Email validation
  if (!isEmail(form.elements["EmlEdt"].value, false)) {
   	alert("Please enter a valid email address i.e. admin@clockon.com.au.");
   	form.elements["EmlEdt"].focus();
   	return false;
   	}

  // Mail validation
  if ((isWhitespace(form.elements["PstAdd1Edt"].value)) && (isWhitespace(form.elements["PstAdd2Edt"].value))) {
    alert("Please enter a valid postal address.");
    form.elements["PstAdd1Edt"].focus();
    return false;
		}
 
  // Phone validation
  if (!isInternationalPhoneNumber(stripWhitespace (form.elements["PhnEdt"].value, false))) {
  	alert("Please enter a valid phone number.");
    form.elements["PhnEdt"].focus();
    return false;
   	}

  // If made it to here then fields are valid 	
  return true; 	
}

// **************************************
// function InitialiseQuoteValues(form)
// **************************************
function InitialiseQuoteValues(form) {
	form.elements["LicItm1"].value = 'License fee not required';
  form.elements["LicItm2"].value = 'N/A';
  form.elements["LicItm3"].value = 'N/A';
  form.elements["LicItm4"].value = '$0.00';

	form.elements["SupItm1"].value = 'Support fee not required';
  form.elements["SupItm2"].value = 'N/A';
  form.elements["SupItm3"].value = 'N/A';
  form.elements["SupItm4"].value = '$0.00';

	form.elements["CfgItm1"].value = 'Data Configuration fee not required';
  form.elements["CfgItm2"].value = 'N/A';
  form.elements["CfgItm3"].value = 'N/A';
  form.elements["CfgItm4"].value = '$0.00';

  form.elements["SwpItm1"].value = 'Magnetic swipe card readers not required';
  form.elements["SwpItm2"].value = 'N/A';
	form.elements["SwpItm3"].value = 'N/A';
	form.elements["SwpItm4"].value = '$0.00';

	form.elements["InstItm1"].value = 'On-site installation fee not required';
  form.elements["InstItm2"].value = 'N/A';
  form.elements["InstItm3"].value = 'N/A';
  form.elements["InstItm4"].value = '$0.00';

	form.elements["TrnItm1"].value = 'On-site training fee not required';
  form.elements["TrnItm2"].value = 'N/A';
  form.elements["TrnItm3"].value = 'N/A';
  form.elements["TrnItm4"].value = '$0.00';

	form.elements["TrnItm11"].value = 'Remote location surcharge not required';
  form.elements["TrnItm21"].value = 'N/A';
  form.elements["TrnItm31"].value = 'N/A';
  form.elements["TrnItm41"].value = '$0.00';    

  form.elements["UsrMan1"].value = 'Printed user manual not required';
  form.elements["UsrMan2"].value = 'N/A';
	form.elements["UsrMan3"].value = 'N/A';
	form.elements["UsrMan4"].value = '$0.00';
	
	return true;
}

// *****************************************
// function CalculateLiteQuote(form)
// *****************************************
function CalculateLiteQuote(form) {

	var TTL = 0.00;
	
	// License
  form.elements["LicItm1"].value = 'ClockOn Lite - License fee (fixed at 50 employees)';
  form.elements["LicItm2"].value = 'N/A';
  form.elements["LicItm3"].value = 'N/A';
  form.elements["LicItm4"].value = '$0.00';

  // Current discounts
	if (form.elements["SpcCbx"].value == "SpcBsePrc") {
		form.elements["SupItm1"].value = 'ClockOn Lite - Annual Support fee renewable (Web Knowledge-base, Email, $50 ON-LINE DISCOUNT)';
    form.elements["SupItm2"].value = '$86.32';
    form.elements["SupItm3"].value = form.elements["LocQtyEdt"].value;
    form.elements["SupItm4"].value = '$' + FormatNumber(form.elements["LocQtyEdt"].value * 86.32,2,'.');

    // Add to quote total
    TTL = form.elements["LocQtyEdt"].value * 86.32;
 		}
	else {
  	form.elements["SupItm1"].value = 'ClockOn Lite - Annual Support fee renewable (Web Knowledge-base, Email)';
		form.elements["SupItm2"].value = '$136.32';
		form.elements["SupItm3"].value = form.elements["LocQtyEdt"].value;
		form.elements["SupItm4"].value = '$' + FormatNumber(form.elements["LocQtyEdt"].value * 136.32,2,'.');

	  // Add to quote total
    TTL = form.elements["LocQtyEdt"].value * 136.32;
 	  }

	return TTL; 	  
}

// ********************************************
// function CalculateClassicQuote(form)
// ********************************************
function CalculateClassicQuote(form){

	var TTL = 0.00;
	
	// License
  form.elements["LicItm1"].value = 'ClockOn Classic - License fee (fixed at 50 employees)';
  form.elements["LicItm2"].value = 'N/A';
  form.elements["LicItm3"].value = 'N/A';
  form.elements["LicItm4"].value = '$0.00'

  // Current Discounts
  if (form.elements["SpcCbx"].value == "SpcBsePrc"){
	  form.elements["SupItm1"].value = 
	  'ClockOn Classic - Annual Support fee renewable (Phone, Web Knowledge-base, Email, $50 ON-LINE DISCOUNT)';
	  form.elements["SupItm2"].value = '$290.91';
	  form.elements["SupItm3"].value = form.elements["LocQtyEdt"].value;
	  form.elements["SupItm4"].value = '$' + FormatNumber(form.elements["LocQtyEdt"].value * 290.91,2,'.');  

	  // Add to quote total
	  TTL = form.elements["LocQtyEdt"].value * 290.91;
  	}
  else{
  	form.elements["SupItm1"].value = 'ClockOn Classic - Annual Support fee renewable (Phone, Web Knowledge-base, Email)';
   	form.elements["SupItm2"].value = '$340.91';
	  form.elements["SupItm3"].value = form.elements["LocQtyEdt"].value;
	  form.elements["SupItm4"].value = '$' + FormatNumber(form.elements["LocQtyEdt"].value * 340.91,2,'.');  

	  // Add to quote total
	  TTL = form.elements["LocQtyEdt"].value * 340.91;
    }

	return TTL;    
}

// ********************************************
// function CalculateTAAQuote(form)
// ********************************************
function CalculateTAAQuote(form){

	var TTL = 0.00;
	
	// License
  form.elements["LicItm1"].value = 'ClockOn Time and Attendance - License fee (fixed at 50 employees)';
  form.elements["LicItm2"].value = 'N/A';
  form.elements["LicItm3"].value = 'N/A';
  form.elements["LicItm4"].value = '$0.00'

  // Current Discounts
  
  // Current Discounts
  if (form.elements["MagCrdQtyEdt"].value > 0) {
	  if(form.elements["SpcCbx"].value == "SpcBsePrc"){
		  form.elements["SupItm1"].value = 
		  'ClockOn Time and Attendance - Annual Support fee renewable (Phone, Web Knowledge-base, Email, $100 ON-LINE DISCOUNT)';
		  form.elements["SupItm2"].value = '$362.72';
		  form.elements["SupItm3"].value = form.elements["LocQtyEdt"].value;
		  form.elements["SupItm4"].value = '$' + FormatNumber(form.elements["LocQtyEdt"].value * 362.727272,2,'.');  
	
		  // Add to quote total
		  TTL = form.elements["LocQtyEdt"].value * 362.727272;
	  	}
  	else {
	  	form.elements["SupItm1"].value = 'ClockOn Time and Attendance - Annual Support fee renewable (Phone, Web Knowledge-base, Email)';
	   	form.elements["SupItm2"].value = '$408.18';
		  form.elements["SupItm3"].value = form.elements["LocQtyEdt"].value;
		  form.elements["SupItm4"].value = '$' + FormatNumber(form.elements["LocQtyEdt"].value * 408.181818,2,'.');  

		  // Add to quote total
		  TTL = form.elements["LocQtyEdt"].value * 408.181818;
	    }	    
	 }
	 else {
	  if(form.elements["SpcCbx"].value == "SpcBsePrc"){
		  form.elements["SupItm1"].value = 
		  'ClockOn Time and Attendance - Annual Support fee renewable (Phone, Web Knowledge-base, Email, $50 ON-LINE DISCOUNT)';
		  form.elements["SupItm2"].value = '$408.18';
		  form.elements["SupItm3"].value = form.elements["LocQtyEdt"].value;
		  form.elements["SupItm4"].value = '$' + FormatNumber(form.elements["LocQtyEdt"].value * 408.181818,2,'.');  
	
		  // Add to quote total
		  TTL = form.elements["LocQtyEdt"].value * 408.181818;
	  	}
  	else {
	  	form.elements["SupItm1"].value = 'ClockOn Time and Attendance - Annual Support fee renewable (Phone, Web Knowledge-base, Email)';
	   	form.elements["SupItm2"].value = '$453.63';
		  form.elements["SupItm3"].value = form.elements["LocQtyEdt"].value;
		  form.elements["SupItm4"].value = '$' + FormatNumber(form.elements["LocQtyEdt"].value * 453.636363,2,'.');  

		  // Add to quote total
		  TTL = form.elements["LocQtyEdt"].value * 453.636363;
	    }	    
	}	 
	return TTL;  
}


// *************************************************
// function CalculateProfessionalQuote(form)
// *************************************************
function CalculateProfessionalQuote(form){

	// Professional & Enterprise temp vars
	var x;
	var UntAmt;
	var LicAmt;
	var SupAmt;
	
	// Initialise local vars
	UntAmt = 0;
	LicAmt = 0;
	SupAmt = 0;

	//Calculate average no employees per site.
 	x = form.elements["EmpQtyEdt"].value / form.elements["LocQtyEdt"].value;
    
  // Calculate unit price per location.
  if (x<7) UntAmt = 250
  else 
    if (x<16) UntAmt = 360
    else
      if (x<26) UntAmt = 400
      else UntAmt = x * 17;
    
  // Setup output line for professional 
  form.elements["LicItm1"].value = 'ClockOn Professional - License fee (' + 
  form.elements["EmpQtyEdt"].value + ' employees at ' + form.elements["LocQtyEdt"].value + ' locations)';
    
  // Vol discount
  LicAmt = UntAmt * form.elements["LocQtyEdt"].value;
  if (form.elements["LocQtyEdt"].value < 3) LicAmt = LicAmt * 1;
  else
    if (form.elements["LocQtyEdt"].value < 6){
    	LicAmt = LicAmt * 0.85;
      form.elements["LicItm1"].value += ' 15% multi-site discount';
      }
    else
      if (form.elements["LocQtyEdt"].value < 10){
      	LicAmt = LicAmt * 0.8;
        form.elements["LicItm1"].value += ' 20% multi-site discount';        
        }
      else
      	if (form.elements["LocQtyEdt"].value < 30){
        	LicAmt = LicAmt * 0.7;
          form.elements["LicItm1"].value += ' 30% multi-site discount';
          }
        else
          if (form.elements["LocQtyEdt"].value <60){
          	LicAmt = LicAmt * 0.6;
            form.elements["LicItm1"].value += ' 40% multi-site discount';
            }
          else {
          	LicAmt = LicAmt * 0.5;
            form.elements["LicItm1"].value += ' 50% multi-site discount';
            }

	// Recalculate new unit amount so unit price * quantity = discounted total amount
  UntAmt = LicAmt / form.elements["LocQtyEdt"].value;
    
  // Reminaing invoice items for professional;
  form.elements["LicItm2"].value = '$' + FormatNumber(UntAmt,2,'.');
  form.elements["LicItm3"].value = form.elements["LocQtyEdt"].value;
  form.elements["LicItm4"].value = '$' + FormatNumber(LicAmt, 2, '.');

  // Support
  if (form.elements["EmpQtyEdt"].value < 11) SupAmt = SupAmt + (10 * 17 + 105);
  else  
    if (form.elements["EmpQtyEdt"].value < 21) SupAmt = SupAmt + (20 * 17);
    else  
    	if (form.elements["EmpQtyEdt"].value < 31) SupAmt = SupAmt + (30 * 17);
      else  
        if (form.elements["EmpQtyEdt"].value < 41) SupAmt = SupAmt + (40 * 17);
        else  
          if (form.elements["EmpQtyEdt"].value < 51) SupAmt = SupAmt + (50 * 17);
          else SupAmt = SupAmt + (form.elements["EmpQtyEdt"].value * 17);

  // Set line items for support.
  form.elements["SupItm1"].value = 'ClockOn Professional - Annual Support fee renewable (Phone, Web Knowledge-base, Email)';
  form.elements["SupItm2"].value = 'Annual Fee';
  form.elements["SupItm3"].value = 'Annual Fee';
  form.elements["SupItm4"].value = '$' + FormatNumber(SupAmt, 2, '.');

	return (LicAmt + SupAmt);  
} 


// *************************************************
// function CalculateEnterpriseQuote(form)
// *************************************************
function CalculateEnterpriseQuote(form){

	// Professional & Enterprise temp vars
	var x;
	var UntAmt;
	var LicAmt;
	var SupAmt;
	
	// Initialise local vars
	UntAmt = 0;
	LicAmt = 0;
	SupAmt = 0;

  //Calculate average no employees per site.
  x = form.elements["EmpQtyEdt"].value / form.elements["LocQtyEdt"].value;
    
  // Calculate unit price per location.
  if (x<7) UntAmt = 500
  else 
    if (x<16) UntAmt = 720
    else
      if (x<26) UntAmt = 800
      else UntAmt = x * 34;
  
  // Setup output line for enterprise 
  form.elements["LicItm1"].value = 'ClockOn Enterprise - License fee (' + 
  form.elements["EmpQtyEdt"].value + ' employees at ' + form.elements["LocQtyEdt"].value + ' locations)';
    
  // Vol discount
  LicAmt = UntAmt * form.elements["LocQtyEdt"].value;
  if (form.elements["LocQtyEdt"].value < 3) LicAmt = LicAmt * 1;
  else
    if (form.elements["LocQtyEdt"].value < 6){
    	LicAmt = LicAmt * 0.85;
      form.elements["LicItm1"].value += ' 15% multi-site discount';
      }
    else
      if (form.elements["LocQtyEdt"].value < 10){
      	LicAmt = LicAmt * 0.8;
        form.elements["LicItm1"].value += ' 20% multi-site discount';        
        }
      else
        if (form.elements["LocQtyEdt"].value < 30){
      	  LicAmt = LicAmt * 0.7;
	        form.elements["LicItm1"].value += ' 30% multi-site discount';
          }
        else
          if (form.elements["LocQtyEdt"].value <60){
          	LicAmt = LicAmt * 0.6;
            form.elements["LicItm1"].value += ' 40% multi-site discount';
            }
          else{
            LicAmt = LicAmt * 0.5;
            form.elements["LicItm1"].value += ' 50% multi-site discount';
            }

  // Recalculate new unit amount so unit price * quantity = discounted total amount
  UntAmt = LicAmt / form.elements["LocQtyEdt"].value;
    
  // Reminaing invoice items for professional;
  form.elements["LicItm2"].value = '$' + FormatNumber(UntAmt,2,'.');
  form.elements["LicItm3"].value = form.elements["LocQtyEdt"].value;
  form.elements["LicItm4"].value = '$' + FormatNumber(LicAmt, 2, '.');

  // Support
  if (form.elements["EmpQtyEdt"].value < 11) SupAmt = SupAmt + (10 * 17 + 105);
  else  
    if (form.elements["EmpQtyEdt"].value < 21) SupAmt = SupAmt + (20 * 17);
    else  
    	if (form.elements["EmpQtyEdt"].value < 31) SupAmt = SupAmt + (30 * 17);
      else  
        if (form.elements["EmpQtyEdt"].value < 41) SupAmt = SupAmt + (40 * 17);
        else  
          if (form.elements["EmpQtyEdt"].value < 51) SupAmt = SupAmt + (50 * 17);
          else SupAmt = SupAmt + (form.elements["EmpQtyEdt"].value * 17);

  // Set line items for support.
  form.elements["SupItm1"].value = 'ClockOn Enterprise - Annual Support fee renewable (Phone, Web Knowledge-base, Email)';
  form.elements["SupItm2"].value = 'Annual Fee';
  form.elements["SupItm3"].value = 'Annual Fee';
  form.elements["SupItm4"].value = '$' + FormatNumber(SupAmt, 2, '.');
 
  return (LicAmt+SupAmt);
}

// *************************************************
// function CalculateEasyWayQuote(form)
// *************************************************
function CalculateEasyWayQuote(form){
  form.elements["CfgItm1"].value = 'Easyway configuration service - ' + form.elements["EmpQtyEdt"].value + ' employees';
  form.elements["CfgItm2"].value = '$10.00';
  form.elements["CfgItm3"].value = form.elements["EmpQtyEdt"].value;
  form.elements["CfgItm4"].value = '$' + form.elements["EmpQtyEdt"].value * 10 + '.00';

  // Add to quote total
  return (form.elements["EmpQtyEdt"].value * 10);    
}

// *************************************************
// function CalculateInstallationQuote(form)
// *************************************************
function CalculateInstallationQuote(form){
  form.elements["InstItm1"].value = 'On-site installation service at ' + form.elements["InstQtyEdt"].value + ' locations';
  form.elements["InstItm2"].value = '$150.00';
  form.elements["InstItm3"].value = form.elements["InstQtyEdt"].value;    
  form.elements["InstItm4"].value = '$' + form.elements["InstQtyEdt"].value * 150 + '.00';

  // Add to quote total
  return (form.elements["InstQtyEdt"].value * 150);        
}

// *************************************************
// function CalculateTrainingQuote(form)
// *************************************************
function CalculateTrainingQuote(form){
  form.elements["TrnItm1"].value = 'On-site training service at ' + form.elements["TrnQtyEdt"].value + ' locations';
  form.elements["TrnItm2"].value = '$300';
  form.elements["TrnItm3"].value = form.elements["TrnQtyEdt"].value;
  form.elements["TrnItm4"].value = '$' + form.elements["TrnQtyEdt"].value * 300 + '.00';    

  // Add to quote total
  return (form.elements["TrnQtyEdt"].value * 300);    
}

// *************************************************
// function CalculateRemoteLocationQuote(form)
// *************************************************
function CalculateRemoteLocationQuote(form){
  form.elements["TrnItm11"].value = 'Remote location surcharge for 1 location only';
  form.elements["TrnItm21"].value = '$300.00';
  form.elements["TrnItm31"].value = '1';
  form.elements["TrnItm41"].value = '$300.00';    

  // Add to quote total
  return 300;    
}

// *************************************************
// function CalculateSwipeCardQuote(form)
// *************************************************
function CalculateSwipeCardQuote(form){

  if (form.elements["MagCrdQtyEdt"].value == 0) return 0;
  
  if (form.elements["SpcCbx"].value == "SpcSwpCrd") {
    form.elements["SwpItm1"].value = 'Magnetic swipe card reader & drivers ($50 ON-LINE DISCOUNT PER UNIT)';
    form.elements["SwpItm2"].value = '$272.72';
    form.elements["SwpItm3"].value = form.elements["MagCrdQtyEdt"].value;
    form.elements["SwpItm4"].value = '$' + FormatNumber(form.elements["MagCrdQtyEdt"].value * 272.727272,2,'.');    
    // Add to quote total
    return (form.elements["MagCrdQtyEdt"].value * 272.727272);    
  	}
	else
	  if (form.elements["SpcCbx"].value == "SpcSwpCrd") {
	    form.elements["SwpItm1"].value = 'Magnetic swipe card reader & drivers ($50 ON-LINE DISCOUNT PER UNIT)';
	    form.elements["SwpItm2"].value = '$272.72';
	    form.elements["SwpItm3"].value = form.elements["MagCrdQtyEdt"].value;
	    form.elements["SwpItm4"].value = '$' + FormatNumber(form.elements["MagCrdQtyEdt"].value * 272.727272,2,'.');    
	    // Add to quote total
	    return (form.elements["MagCrdQtyEdt"].value * 272.727272);    
	  	}
		if (((form.elements["VerLst"].value == "Professional")||(form.elements["VerLst"].value == "Enterprise"))&&
    	(form.elements["MagCrdQtyEdt"].value > 1)) { 
			form.elements["SwpItm1"].value = 'Magnetic swipe card reader & drivers (1 UNIT FREE)';
		  form.elements["SwpItm2"].value = '$318.18';
		  form.elements["SwpItm3"].value = form.elements["MagCrdQtyEdt"].value - 1;
		  form.elements["SwpItm3"].value += '(+1)';			  
		  form.elements["SwpItm4"].value = '$' + FormatNumber((form.elements["MagCrdQtyEdt"].value - 1) * 318.182,2,'.');    
	
	    // Add to quote total
		  return ((form.elements["MagCrdQtyEdt"].value - 1)* 318.182); 
		  }  
	 else {
	 	form.elements["SwpItm1"].value = 'Magnetic swipe card reader & drivers';
	  form.elements["SwpItm2"].value = '$318.18';
	  form.elements["SwpItm3"].value = form.elements["MagCrdQtyEdt"].value;
	  form.elements["SwpItm4"].value = '$' + FormatNumber(form.elements["MagCrdQtyEdt"].value * 318.182,2,'.');    
	
	  // Add to quote total
	  return (form.elements["MagCrdQtyEdt"].value * 318.182);    
	  }
}

// *************************************************
// function CalculateManualQuote(form)
// *************************************************
function CalculateManualQuote(form){
	form.elements["UsrMan1"].value = 'Printed user manual';
 	form.elements["UsrMan2"].value = '$50.00';
 	form.elements["UsrMan3"].value = form.elements["PrnManQtyEdt"].value;
	form.elements["UsrMan4"].value = '$' + (form.elements["PrnManQtyEdt"].value * 50) + '.00';    

  // Add to quote total
  return (form.elements["PrnManQtyEdt"].value * 50);    
}

// **************************************
// function SelectSpecials(form, DefRqd)
// **************************************
function SelectSpecials(form) {

	// Get local object references
	var SpcLst = form.elements["SpcCbx"];
	var VerLst = form.elements["VerLst"];

	// Clear specials list
 	SpcLst.length = 0;

	// If no version is selected
	if (VerLst.selectedIndex == 0) AddToList(SpcLst, "None", "Select a software version or product to view special offers");
	
	// If lite or classic selected
	if ((VerLst.selectedIndex == 1)||(VerLst.selectedIndex == 2)){
		AddToList(SpcLst, "None", "No - If I place an order it will not be on-line");
		AddToList(SpcLst, "SpcSwpCrd", "On-line Special - $50 discount on each magnetic swipe card reader");
		AddToList(SpcLst, "SpcBsePrc", "On-line Special - $50 discount on Lite or Classic");	
		SpcLst.selectedIndex = 2;
	};
	
	// If T&A selected
	if (VerLst.selectedIndex == 5){
		AddToList(SpcLst, "None", "No - If I place an order it will not be on-line");
		if (form.elements["MagCrdQtyEdt"].value == 0) AddToList(SpcLst, "SpcBsePrc", "On-line Special - $50 discount");
		else AddToList(SpcLst, "SpcBsePrc", "On-line Special - $100 discount on software");
		SpcLst.selectedIndex = 1;
	};

	// If professional or enterprise selected
	if ((VerLst.selectedIndex == 3)||(VerLst.selectedIndex == 4)){
		AddToList(SpcLst, "None", "No - If I place an order it will not be on-line");
		AddToList(SpcLst, "SpcSwpCrd", "On-line Special - $50 discount on each magnetic swipe card reader");
		AddToList(SpcLst, "SpcFreSwpCrd", 
		"On-line Special - Free swipe card reader with Professional or Enterprise   (requires at least 20 employees)");
		SpcLst.selectedIndex = 2;
	};

	// If Swipe cards only
	if (VerLst.selectedIndex == 6){
		AddToList(SpcLst, "None", "No specials available for this version");	
		SpcLst.selectedIndex = 0;		
	};
	
	return true;
}

// **************************************
// function AddToList(list,val,txt)
// **************************************
function AddToList(list,val,txt) {
	var Opt = list.options;
	var NewIdx = Opt.length++;
	with(Opt[NewIdx]) { 
		value=val; 
		text=txt; 
		}
	
	return true;	
}

// **************************************
// function CalculateQuote(form)
// **************************************
function CalculateQuote(form){  

	// General Quote Details
 	var QteNbr;
 	var QteDte;
 	var QteVldDte;
  var QteTTL = 0;
  var QteGST = 0;

  // Initialise license and support values
	InitialiseQuoteValues(form);

  // Calculate Lite Quote
  if (form.elements["VerLst"].value == "Lite") QteTTL = QteTTL + CalculateLiteQuote(form);

  // Calculate Classic Quote
  if (form.elements["VerLst"].value == "Classic") QteTTL = QteTTL + CalculateClassicQuote(form);

  // Calculate T&A Quote
  if (form.elements["VerLst"].value == "TA") QteTTL = QteTTL + CalculateTAAQuote(form);
 
 	// Calculate Professional Quote
  if (form.elements["VerLst"].value == "Professional") QteTTL = QteTTL + CalculateProfessionalQuote(form);

 	// Calculate Enterprise Quote
  if (form.elements["VerLst"].value == "Enterprise") QteTTL = QteTTL + CalculateEnterpriseQuote(form);

  // Easyway Data Configuration
  if ((form.elements["EsyWayChk"].checked == true) && (form.elements["VerLst"].value != "Magnetic Swipe Card Reader(s)") &&
  (form.elements["VerLst"].value != "Magnetic Swipe Card Reader(s)"))  
		QteTTL = QteTTL + CalculateEasyWayQuote(form);

	//Installation    
  if ((form.elements["InstRqdChk"].checked == true) && (form.elements["VerLst"].value != "Magnetic Swipe Card Reader(s)"))
		QteTTL = QteTTL + CalculateInstallationQuote(form);
  
  //Training    
  if ((form.elements["TrnRqdChk"].checked == true) && (form.elements["VerLst"].value != "Magnetic Swipe Card Reader(s)"))
  	QteTTL = QteTTL + CalculateTrainingQuote(form);
  	
  // Remote Location Surcharge
  if ((form.elements["RmtLocChk"].checked == true) && (form.elements["VerLst"].value != "Magnetic Swipe Card Reader(s)"))
  	QteTTL = QteTTL + CalculateRemoteLocationQuote(form);
        
  //Swipe Card Units    
	QteTTL = QteTTL + CalculateSwipeCardQuote(form);

  //User Manuals 
  if (form.elements["PrnManQtyEdt"].value > 0) QteTTL = QteTTL + CalculateManualQuote(form);

  // Set grand totals into form variables
  form.elements["QteSubTTL"].value = '$' + FormatNumber(QteTTL, 2, '.');
  form.elements["QteGSTTTL"].value = '$' + FormatNumber(QteTTL * 0.1, 2, '.');
  form.elements["QteTTL"].value = '$' + FormatNumber(QteTTL * 1.1, 2, '.');

  return true;
}

// Tax Valid
function IsTaxValid(form) {

  // Force a version selection
  if (form.elements["VerLst"].selectedIndex == 0) {
  	alert("Please select the version of ClockOn you are using.");
		form.elements["VerLst"].focus();
		return false;
   	}

  // Force selection of how used
  if (form.elements["HowUsdLst"].selectedIndex == 0) {
  	alert("Please select how you are currently using ClockOn.");
		form.elements["HowUsdLst"].focus();
		return false;
   	}

  // Force selection of multiple computers
  if (form.elements["ClkMulLst"].selectedIndex == 0) {
  	alert("Please select if you are using ClockOn on multiple computers.");
		form.elements["ClkMulLst"].focus();
		return false;
   	}

  // Force selection of importance of phone support
  if (form.elements["PhnSupLst"].selectedIndex == 0) {
  	alert("Please rate the importance of phone support.");
		form.elements["PhnSupLst"].focus();
		return false;
   	}

  // Force selection of email support
  if (form.elements["EmlSupLst"].selectedIndex == 0) {
  	alert("Please select if you are aware of our free email support.");
		form.elements["EmlSupLst"].focus();
		return false;
   	}

  // Name validation
  if (isWhitespace(form.elements["NmeEdt"].value)) {
   	alert("Please enter your name.");
   	form.elements["NmeEdt"].focus();
   	return false;
		}

  // Company name validation
  if (isWhitespace(form.elements["CoyNmeEdt"].value)) {
   	alert("Please enter your company/trading name. Enter your name if you are not sure.");
   	form.elements["CoyNmeEdt"].focus();
   	return false;
   	}
   	
  // Industry validation
  if (isWhitespace(form.elements["IndEdt"].value)) {
   	alert("Please enter your industry.");
   	form.elements["IndEdt"].focus();
   	return false;
   	}
   	
  // Address validation
  if ((isWhitespace(form.elements["Add1Edt"].value)) &&
     (isWhitespace(form.elements["Add2Edt"].value))) {
      alert("Please enter your address.")
      form.elements["Add1Edt"].focus()
      return false;
   	  }
   	  
  // Email validation
  if (isWhitespace(form.elements["EmlEdt"].value))
  	{
      alert("Please enter your email address.")
      form.elements["EmlEdt"].focus()
      return false
	 }
  	else
  	
      if (isEmail(form.elements["EmlEdt"].value, false)==false)
      {
       alert("This field must be a valid email address i.e. admin@clockon.com.au.")
       form.elements["EmlEdt"].focus()
       return false
      }
      
  // Phone validation
  if (isWhitespace(form.elements["PhnEdt"].value))
  	{
      alert("Please enter a valid phone number.")
      form.elements["PhnEdt"].focus()
      return false
	 }
  	else
  	
      if (isInternationalPhoneNumber(stripWhitespace (form.elements["PhnEdt"].value), false)==false)
      {
       alert("This field must be a valid phone number.")
       form.elements["PhnEdt"].focus()
       return false
      }

	// Fax validation
  if (isWhitespace(form.elements["FaxEdt"].value)) {
   	alert("Please enter your fax number.");
   	form.elements["FaxEdt"].focus();
   	return false;
   	}
   	  
  // If got here form is valid 	
  return true;
  
}

