
/*########################################################,
|
|   Form Validation Library v.1.0   � 2006, 2007, 2008
|   Gabriels Technology Solutions   Jim Conte
|
|----------------------------------------------------------
|
|   You may contact me if you have questions at
|   jim@gabriels.net or jimconte.com
|
`##########################################################
|
|   Note on Validation functions....
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|   Many functions do not check if the field has value.
|   This is so the user may optionally leave the field
|   blank. If users do enter a value, it must match the 
|   pattern. These functions will return TRUE if the
|   field is left blank. The presence of data must be
|   validated separately in most cases.
|
`########################################################*/


/*========================================================,
| ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
|   Regular Expressions                 
| ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
`========================================================*/

var RegExNumberReplace = /[^0-9\.\-]/g;
var RegExNumberFormat = /^((\-)?([0-9]+)?[\.]?([0-9]+))$/;
var RegExZip = /^[0-9]{5}(\-[0-9]{4})?$/;
var RegExUrl = /(ftp|http|https):\/\/[\w0-9\-]+(\.[\w0-9\-]+){1,}([\w#!:.?+=&%@!\-\/]+)?/;
//var RegExEmail        = /^([a-zA-Z0-9]+([_\.\-]{1})?[a-zA-Z0-9]+)+\@([a-zA-Z0-9]+([_\.\-]{1})?[a-zA-Z0-9]+)+\.[a-zA-Z0-9]{2,4}$/;
// Updated 1/24/2007 - Jim
//var RegExEmail        = /^([a-zA-Z0-9]+(([_\.\-]{1})?[a-zA-Z0-9]+)*)+\@([a-zA-Z0-9]+(([_\.\-]{1})?[a-zA-Z0-9]+)*)+\.[a-zA-Z0-9]{2,4}$/;
// Updated 3/6/2007 - Jim
//var RegExEmail = /^(([a-z0-9]+\.)|([a-z0-9]+([_\-]{0,1}[a-z0-9])+\.))*([a-z0-9]+[_\-a-z0-9]*[a-z0-9]+)\@(([a-z0-9]+\.)|([a-z0-9]+([_\-]{0,1}[a-z0-9])+\.))*([a-z0-9]+[_\-a-z0-9]*[a-z0-9]+)\.[a-z]{2,4}$/i;
var RegExDate = /^\d\d?\/\d\d?\/\d{4}$/;
var RegExEmail = /^[A-Za-z0-9](([_\.\-]?[a-zA-Z0-9]+)*)@([A-Za-z0-9]+)(([\.\-]?[a-zA-Z0-9]+)*)\.([A-Za-z]{2,})$/;

/*========================================================,
|   trim | requires [String]
|---------------------------------------------------------:
|   returns [String] without leading and trailing spaces
`========================================================*/

function trim(str) {
    return str.replace(/^\s*|\s*$/g, '');
}

/*========================================================,
|   selectionValueIs | requires [Select object]
|---------------------------------------------------------:
|   returns true if 
|   [String theValue] == [Select object] String value
`========================================================*/

function selectionValueIs(theSelectBox, theValue) {
    if (trim(theSelectBox.options[theSelectBox.options.selectedIndex].value) == trim(theValue.toString())) {
        return true;
    } else {
        return false;
    }
}

/*========================================================,
|   IsNumeric | requires [String]
|---------------------------------------------------------:
|   returns true if [String] can be interpreted 
|   as a number ( [-][000][.][000] )
`========================================================*/

function IsNumeric(theValue) {
    //    theValue = theValue.replace(RegExNumberReplace,'');
    //	if (!RegExNumberFormat.test(theValue)) 
    //	{
    //        return false;
    //    }
    //    return true;
    ///// Updated - Madhavi
    theValue = theValue.replace(/\d*(\.\d*)?/, '');
    if (theValue != '') {
        return false;
    }
    return true;
}

/*========================================================,
|   fieldHasValue | requires [Input object]
|---------------------------------------------------------:
|   returns true if [Input object] != NULL value
`========================================================*/

function fieldHasValue(theField) {
    if (trim(theField.value).length > 0) {
        return true;
    } else {
        return false;
    }
}

/*========================================================,
|   formatURL | requires [Input object]
|---------------------------------------------------------:
|   returns string URL with "http://" prefix
`========================================================*/

function formatURL(urlField) {
    var URL = trim(urlField.value);
    if (URL.length > 0) {
        URL = URL.replace("http://", "");
        URL = "http://" + URL;
    }
    return URL;
}


/*========================================================\
|   formatLeadingZero | requires [Number]
|---------------------------------------------------------:
|   returns [Number] string, adds leading zero if missing
`========================================================*/

function formatLeadingZero(val) {
    var display = '';
    if (val < 10) {
        display = "0" + val;
    } else {
        display = "" + val;
    }
    return display;
}


/*========================================================\
|   validateTextFieldNotNull
|   requires [Input Object] [String:message] [String:Tabid]
|---------------------------------------------------------:
|   true = [Input Object].value != NULL
|   false = See AlertFocus(field,message,tab)
`========================================================*/

function validateTextFieldNotNull(field, message, tab) {
    if (!fieldHasValue(field)) {
        return AlertFocus(field, message, tab);
    }
    return true;
}

/*========================================================\
|   validateTextFieldMinLength | requires    
|   [Input Object] [Number] [String:message] [String:Tabid]
|---------------------------------------------------------:
|   true = [Input Object].value.length >= [Number]
|   false = See AlertFocus(field,message,tab)
`========================================================*/

function validateTextFieldMinLength(field, minLength, message, tab) {
    if (trim(field.value).length >= minLength) {
        return true;
    } else {
        return AlertFocus(field, message, tab);
    }
}

/*========================================================\
|   validateSelectboxNotNull    
|   requires [Select Object] [String:message] [String:Tabid]
|---------------------------------------------------------:
|   true = [Select Object] value == ''
|   false = See AlertFocus(field,message,tab)
`========================================================*/

function validateSelectboxNotNull(field, message, tab) {
    if (field.options[field.options.selectedIndex].value == '') {
        return AlertFocus(field, message, tab);
    } else {
        return true;
    }
}

/*========================================================\
|   validateSelectIndexNotZero    
|   requires [Select Object] [String:message] [String:Tabid]
|---------------------------------------------------------:
|   true = [Select Object] selectedIndex > 0
|   false = See AlertFocus(field,message,tab)
`========================================================*/

function validateSelectIndexNotZero(field, message, tab) {
    if (field.options.selectedIndex == 0) {
        return AlertFocus(field, message, tab);
    } else {
        return true;
    }
}

/*========================================================\
|   validateGenericRegEx    
|   requires [String:Regular Expression] [Input Object] 
|            [String:message]            [String:Tabid]
|---------------------------------------------------------:
|   pass through generic Regex handler
`========================================================*/

function validateGenericRegEx(RegEx, field, message, tab) {
    if (fieldHasValue(field)) {
        var T = trim(field.value);
        if (!RegEx.test(T)) {
            return AlertFocus(field, message, tab);
        }
        else {
            return true;
        }
    }
    return true;
}

/*========================================================\
|   validateUSPhone  
|   ***********************************************
|   (Depricated - Use "validatePhoneUS" instead - Jim 3/7/2007)
|   ***********************************************
|   requires [Input Object] [String:Tabid]
|   note: this function changes the value of the field
|---------------------------------------------------------:
|   true = [Input Object].value is a valid phone number
|   true = field is blank
|   false = see AlertFocus(field,message,tab);
`========================================================*/

function validateUSPhone(field, message, tab) {
    if (fieldHasValue(field)) {
        var tempPhone = field.value;
        tempPhone = tempPhone.replace(/[^0-9]*/g, '');

        if (tempPhone.length == 10 || tempPhone.length == 11) {
            // field.value = FormatUSPhone(tempPhone);
            return true;
        }
        else if (tempPhone.length > 0) {
            return AlertFocus(field, message, tab);
        }
    }
    return true;
}
/*========================================================\
|   helper function formats U.S. phone number
`========================================================*/
//function FormatUSPhone(PhoneValue) {
//    if (PhoneValue.length == 10) {
//        var NewPhone = "(" + PhoneValue.substring(0, 3) + ") " + PhoneValue.substring(3, 6) + "-" + PhoneValue.substring(6);
//    }
//    if (PhoneValue.length == 11) {
//        var NewPhone = PhoneValue.substring(0, 1) + " (" + PhoneValue.substring(1, 4) + ") " + PhoneValue.substring(4, 7) + "-" + PhoneValue.substring(7);
//    }
//    return NewPhone;
//}

function FormatUSPhone(PhoneValue) {
    var NewPhone = PhoneValue.replace(/ /g,'');
    if (IsNumeric(NewPhone)) {
        if (NewPhone.length == 10) {
            NewPhone = NewPhone.substring(0, 3) + "-" + NewPhone.substring(3, 6) + "-" + NewPhone.substring(6);
        }
    }
    
    return NewPhone;
}

/*========================================================\
|   Phone Number Validation 
`========================================================*/

var RegExPhoneUS = /[\(\[\{ ]*([0-9]{3})[\)\]\}.\- ]*([a-z0-9]{3})[.\- ]*([a-z0-9]{4}) *((e(x(t)?)?)(ension)?|x)?[.\-:\/\\ ]*([0-9]*)/i;
// RegExPhoneUS validates phone number with extension, and returns values if used in matching
// (111) AAA-BBBB ex.444..
// match[1] = 111   area code   (numeric)
// match[2] = AAA               (alpha-numeric)
// match[3] = BBBB              (alpha-numeric)
// match[9] = 444.. extension   (numeric)
//-------------------------------- Jim 3/7/2007

var PhoneFormatMessage = "Please enter a phone number using the following format \n\
\n\
(000) XXX-XXXX ext.0000\n\
\n\
0 = must be a number\n\
X = any number or letter\n\
\n\
Area code is required, extension is optional";

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*\
|   NANPAvalidation
|``````````````````````````````````````````````````````````
|   Requires [String areacode]
|   validates US area code
|   returns true/false based on nanpa NXX standard
\*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

function NANPAvalidation(areacode) {
    var A = areacode;
    if (A.length != 3) {
        return false;
    }
    // first digit must be 2 through 9
    var R1 = /[0-1]{1}[0-9]{2}/;
    // N11 is reserved
    var R2 = /[2-9]{1}11/;
    // N9X is reserved
    var R3 = /[2-9]{1}9[0-9]{1}/;
    // 37X is reserved
    var R4 = /37[0-9]{1}/;
    // 96X is reserved
    var R5 = /96[0-9]{1}/;

    if (R1.test(A) || R2.test(A) || R3.test(A) || R4.test(A) || R5.test(A)) {
        return false;
    }
    return true;
    /*
    Validation based on the NXX standard
    N = 2-9
    X = 0-9
    North American Numbering Plan Administration
    http://www.nanpa.com
    */
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*\
|   stringAllTheSame
|``````````````````````````````````````````````````````````
|   Requires [Form Object] field
|   returns true if all values of field are the same
|   used to prevent users from entering bad data
\*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

function stringAllTheSame(field) {
    var test = trim(field.value).split('');
    if (test.length < 2) {
        return true;
    }

    for (var x = 1; x < test.length; x++) {
        if (test[x] != test[x - 1]) {
            return false;
        }
    }
    return true;
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*\
|   formatPhoneUS     
|``````````````````````````````````````````````````````````
|   Pattern: (000) XXX-XXXX ext.0000
|   0 = number
|   X = number or letter
|``````````````````````````````````````````````````````````
|   Requires [Form Object] field
|   formats phone if phone is valid US phone number
|   supports extension if provided
\*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

function formatPhoneUS(field) {
    if (stringAllTheSame(field)) {
        return;
    }
    if (RegExPhoneUS.test(field.value)) {
        var matches = field.value.match(RegExPhoneUS);
        if (matches[9].length > 0) {
            field.value = "(" + matches[1] + ") " + matches[2] + "-" + matches[3] + " ext." + matches[9];
        }
        else {
            field.value = "(" + matches[1] + ") " + matches[2] + "-" + matches[3];
        }
    }
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*\
|   validatePhoneUS 
|``````````````````````````````````````````````````````````
|   Requires [Form Object] field
|            [Boolean] optional
|   calls formatPhoneUS
|``````````````````````````````````````````````````````````
|   alerts user and returns true / false on validation
|   if optional is provided, this means the field is optional...
|   shows alternative message, returns "True" if it's empty
\*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

function validatePhoneUS(field, optional) {
    if (optional != undefined) {
        if (optional == true) {
            if (trim(field.value) == "") {
                return true;
            }
            PhoneFormatMessage = "This phone number is not required, \n\
	                        but if you choose to add it,\n" + PhoneFormatMessage;
        }
    }

    var returnPhone = field.value;

    formatPhoneUS(field);

    if (!RegExPhoneUS.test(field.value) || stringAllTheSame(field)) {
        window.alert(PhoneFormatMessage);
        field.focus();
        return false;
    }
    else {
        var matches = field.value.match(RegExPhoneUS);
        if (!NANPAvalidation(matches[1])) {
            window.alert("(" + matches[1] + ") is not a valid US area code");
            return false
        }
    }
    return true;
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*\
|   validateOnlyPhoneUS 
|``````````````````````````````````````````````````````````
|   Requires [Form Object] field
|   calls formatPhoneUS
|``````````````````````````````````````````````````````````
|   alerts user and returns true / false on validation
\*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

function validateOnlyPhoneUS(field) {
    var returnPhone = field.value;

    formatPhoneUS(field);

    if (!RegExPhoneUS.test(field.value) || stringAllTheSame(field)) {
        return false;
    }
    else {
        var matches = field.value.match(RegExPhoneUS);
        if (!NANPAvalidation(matches[1])) {
            return false
        }
    }
    return true;
}

/*========================================================\
|   validateZip    
|   requires [Input Object] [String:message] [String:Tabid]
|---------------------------------------------------------:
|   true = [Input Object].value matches pattern
|   false = See validateGenericRegEx
`========================================================*/

function validateZip(field, message, tab) {
    return validateGenericRegEx(RegExZip, field, message, tab);
}

/*========================================================\
|   validURLField    
|   requires [Input Object] [String:message] [String:Tabid]
|---------------------------------------------------------:
|   true = [Input Object].value matches pattern
|   false = See AlertFocus(field,message,tab)
`========================================================*/

function validURLField(field, message, tab) {
    return validateGenericRegEx(RegExUrl, field, message, tab);
}

/*========================================================\
|   validEmailField    
|   requires [Input Object] [String:message] [String:Tabid]
|---------------------------------------------------------:
|   true = [Input Object].value is valid email
|   false = See AlertFocus(field,message,tab)
`========================================================*/

function validEmailField(field, message, tab) {
    return validateGenericRegEx(RegExEmail, field, message, tab);
}

/*========================================================\
|   validEmailString 
|   requires [String:strEmail] [String:message] [String:Tabid]
|---------------------------------------------------------:
|   true = String:strEmail is valid email, return true
|   false = alert(message), return false
`========================================================*/

function validEmailString(strEmail, message) {
    if (!RegExEmail.test(strEmail)) {
        window.alert("validEmailString: " + message);
        return false;
    }
    else {
        return true;
    }
}

// Validation for email address.
function validateEmail(emailField, emErr, message) {
    if (fieldHasValue(emailField) && validEmailField(emailField)) {
        return true;
    }
    else {
        emErr.style.display = 'block';
        if (gE("eml")) {
            gE("eml").style.color = '#A41D21';
        }

        if (!fieldHasValue(emailField)) {
            emErr.innerHTML = "Please enter your e-mail address.";
        }
        else {
            if (message != "") {
                emErr.innerHTML = message; //"*Not a valid e-mail format.";
            }
        }
        firstErrField = emailField;
        AlertFocus(firstErrField);
        return false;
    }
}


/*========================================================\
|   validEmailFieldMultiple   
|   requires [Input Object] [String:message] 
|            [String:separator] [String:Tabid]
|---------------------------------------------------------:
|   true = [Input Object].value is valid email or list of 
|           emails separated by "separator"
|   false = See AlertFocus(field,message,tab)
`========================================================*/

function validEmailFieldMultiple(field, separator, message, tab) {
    field.value = trim(field.value);
    if (!fieldHasValue(field)) {
        return true;
    }
    if (field.value.indexOf(separator) > -1) {
        var AllEmails = field.value.split(separator);
        for (var x = 0; x < AllEmails.length; x++) {
            if (!validEmailString(trim(AllEmails[x]), message)) {
                return false;
            }
        }
        return true;
    }
    else {
        return validateGenericRegEx(RegExEmail, field, message, tab);
    }
}

/*========================================================\
|   validatePassword    
|   enforces the rules of a valid user password 
`========================================================*/

function validatePassword(field, message, tab) {

    message = "For security, your password must:\n\n- Contain only numbers and letters\n- Contain at least one number and one letter\n- Be at least 6 characters in length";
    var fieldValue = trim(field.value);
    if (!fieldHasValue(field)) { fieldValue = null; return AlertFocus(field, message + "\n(e:1)", tab); }
    if (fieldValue.length < 6) { fieldValue = null; return AlertFocus(field, message + "\n(e:2)", tab); }
    // must have at least 1 number
    var regex1 = /[0-9]/g;
    // must have at least 1 letter
    var regex2 = /[a-zA-Z]/g;
    // should only contain numbers and letters
    var regex3 = /[^0-9a-zA-Z]/g;
    if (!regex1.test(fieldValue)) { fieldValue = null; return AlertFocus(field, message + "\n(e:3)", tab); }
    if (!regex2.test(fieldValue)) { fieldValue = null; return AlertFocus(field, message + "\n(e:4)", tab); }
    if (regex3.test(fieldValue)) { fieldValue = null; return AlertFocus(field, message + "\n(e:5)", tab); }
    return true;
}

/*========================================================\
|   convertFloat | requires [String]
|---------------------------------------------------------:
|   returns value converted to number or Zero
|   "-100.01" = -100.01   "-100.00" = -100
|   "100"     = 100       "Five"    = 0
`========================================================*/

function convertFloat(theString) {
    theString = theString.replace(RegExNumberReplace, '');
    if (!isNaN(parseFloat(theString))) {
        return parseFloat(theString);
    }
    else {
        return 0;
    }
}


/*========================================================\
|   validateNumericFieldNOTZero    
|   requires [Input Object] [String:message] [String:Tabid]
|---------------------------------------------------------:
|   true = [Input Object].value != 0
|   false = See AlertFocus(field,message,tab)
`========================================================*/

function validateNumericFieldNOTZero(field, message, tab) {

    if (!fieldHasValue(field)) {
        return AlertFocus(field, message, tab);
    }
    if (convertFloat(field.value) == 0) {
        return AlertFocus(field, message, tab);
    }
    return true;
}

/*========================================================\
|   validateNumericField    
|   requires [Input Object] [String:message] [String:Tabid]
|---------------------------------------------------------:
|   true = [Input Object].value is a number and != 0 
|   false = See AlertFocus(field,message,tab)
`========================================================*/

function validateNumericField(field, message, tab) {
    if (fieldHasValue(field)) {
        if (IsNumeric(field.value)) {
            if (convertFloat(field.value) != 0) {
                field.value = convertFloat(field.value);
                return true;
            }
            else {
                return AlertFocus(field, message, tab);
            }
        }
        else {
            return AlertFocus(field, message, tab);
        }
    }
    return true;
}


/*========================================================\
|   CreditCardRules [Object]    
|---------------------------------------------------------:
|   1=Visa  2=MasterCard    3=Amex      4=Discover
|---------------------------------------------------------:
|   stores rules data for use in validation
`========================================================*/
function CreditCardRules(id) {
    CC = new Array();
    CC.push(new Array());
    //----------------------------------------------------------------------------------
    //               Name   Prefix Length    Prefix Values     Valid Card Number Length(s)
    //__________________________________________________________________________________
    CC.push(new Array("Visa", 1, [4], [13, 16]));
    CC.push(new Array("MasterCard", 2, [51, 52, 53, 54, 55], [16]));
    CC.push(new Array("American Express", 2, [34, 37], [15]));
    CC.push(new Array("Discover", 4, [6011], [16]));

    return CC[id];
}

/*========================================================\
|   validateCreditCard    
|   requires    [Input Object] credit card number field
|               [value int] Month
|               [value int] Year
|               [value int] credit card Type
|   [String:Tabid - optional]
|---------------------------------------------------------:
|   1=Visa  2=MasterCard    3=Amex      4=Discover
|---------------------------------------------------------:
|   true = valid credit card number 
|   false = See AlertFocus(field,message,tab)
`========================================================*/
function validateCreditCard(field, Cmonth, Cyear, CtypeId, tab) {
    // instance of object
    var CCRules = CreditCardRules(CtypeId);
    var CardNumber = field.value;
    // remove non-numbers
    CardNumber = CardNumber.replace(/[^0-9]/g, '');
    var CCNumberLengthArray = CCRules[3];
    var CCPrefixValsArray = CCRules[2];
    var CCPrefixLen = CCRules[1];
    var CCName = CCRules[0];
    var CannedMessage = "Please enter a valid " + CCName + " card number";

    if (isNaN(CardNumber)) {
        return AlertFocus(field, CannedMessage + " (67)", tab);
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //  Date
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    var TodayDate = new Date();
    var CardDate = new Date(Cmonth + "/1/" + Cyear);
    if (dateCompare(TodayDate, CardDate) <= 0) {
        return AlertFocus(field, "This " + CCName + " card has expired", tab);
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //  Length
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    var ValidLength = false;
    // validate card number length
    for (var v = 0; v < CCNumberLengthArray.length; v++) {
        if (CardNumber.length == CCNumberLengthArray[v]) {
            ValidLength = true;
            break;
        }
    }
    if (!ValidLength) {
        return AlertFocus(field, CannedMessage + " (72)", tab);
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //  Prefix
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    var ValidPrefix = false;
    // validate card prefix values
    for (var v = 0; v < CCPrefixValsArray.length; v++) {
        if (parseInt(CardNumber.substring(0, CCPrefixLen), 10) == CCPrefixValsArray[v]) {
            ValidPrefix = true;
            break;
        }
    }
    if (!ValidPrefix) {
        return AlertFocus(field, CannedMessage + " (84)", tab);
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //  LUHN
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    if (!ValidateCreditCardLUHN(CardNumber)) {
        return AlertFocus(field, CannedMessage + " (99)", tab);
    }

    return true;
}

/*========================================================\
|   ValidateCreditCardLUHN    
|   requires    [value] cardNumber
|-------------------------------------------------------
|   LUHN Formula for validation of credit card numbers.
|   Assumes number is numeric and all special characters 
|   have been removed. 
|   -- You have to start from the right digit, 
|      and work backwards. 
|   -- Every second digit multiplied by 2
|   -- if the value is > 9, subtract 9 
|      ( or, add each digit together - same result )
|   -- add all of these numbers together
|   -- If the sum is divisible by 10, it is a valid number
|---------------------------------------------------------:
|   true = valid credit card number 
`========================================================*/
function ValidateCreditCardLUHN(cardNumber) {
    var ar = new Array(cardNumber.length);
    var i = 0, sum = 0;
    for (i = 0; i < cardNumber.length; i++) {
        ar[i] = parseInt(cardNumber.charAt(i), 10);
    }
    for (i = ar.length - 2; i >= 0; i -= 2) {
        ar[i] *= 2;
        if (ar[i] > 9) {
            ar[i] -= 9;
        }
    }
    for (i = 0; i < ar.length; i++) {
        sum += ar[i];
    }
    if ((sum % 10) == 0) {
        return true;
    }
    else {
        return false;
    }
}


/*========================================================\
|   validateDateField    
|   requires [Input Object] [String:Tabid]
|---------------------------------------------------------:
|   true = [Input Object].value matches format and rules 
|   false = See AlertFocus(field,message,tab)
`========================================================*/

function validateDateField(field, tab) {
    if (fieldHasValue(field)) {
        if (RegExDate.test(field.value)) {
            var dateParts = field.value.split('/');
            var theMonth = convertFloat(dateParts[0]);
            var theDay = convertFloat(dateParts[1]);
            var theYear = convertFloat(dateParts[2]);
            //-- clean parts and put back together
            var newDateString = theMonth + "/" + theDay + "/" + theYear;
            //-- This logic makes sure impossible dates like Feb. 31 do not slip through.
            //-- formatProperDate evaluates the date, so it would be a different value
            //-- if not a regular date
            if (formatProperDate(trim(field.value)) == formatProperDate(trim(newDateString))) {
                return true;
            }
            else {
                return AlertFocus(field, "Please enter a date in the format MM/DD/YYYY.", tab);
            }
        }
        else {
            return AlertFocus(field, "Please enter a date in the format MM/DD/YYYY.", tab);
        }
    }
    return true;
}

/*========================================================\
|   validateDateString  
|   requires [String:Date]
|---------------------------------------------------------:
|   true = [String:Date] matches format and rules 
|   false = returns false
`========================================================*/

function validateDateString(DateString) {
    if (RegExDate.test(DateString)) {
        //--- take date appart
        var dateParts = trim(DateString).split('/');
        var theMonth = parseFloat(dateParts[0]);
        var theDay = parseFloat(dateParts[1]);
        var theYear = parseFloat(dateParts[2]);
        //-- clean parts and put back together
        var newDateString = theMonth + "/" + theDay + "/" + theYear;
        //-- This logic makes sure impossible dates like Feb. 31 do not slip through.
        //-- formatProperDate evaluates the date, so it would be a different value
        //-- if not a regular date
        if (formatProperDate(trim(DateString)) == formatProperDate(trim(newDateString))) {
            return true;
        }
        else {
            return false;
        }
    }
    else {
        return false;
    }
}

/*========================================================\
|   formatProperDate    
|   requires [String]
|---------------------------------------------------------:
|   returns [String] properly formatted mm/dd/yyyy
|   NOTE: does NOT validate, WILL return value
`========================================================*/

function formatProperDate(dateString) {
    var tempDate = new Date(dateString);
    var theMonth = tempDate.getMonth() + 1;
    var theDay = tempDate.getDate();
    var theYear = tempDate.getYear();
    if (theYear < 1000) {
        theYear += 1900;
    }
    var newDateVal = theMonth + "/" + theDay + "/" + theYear;
    return newDateVal;
}


/*========================================================\
|   dateCompare    
|   requires [String:date1] [String:date2]
|---------------------------------------------------------:
|   returns number of days after date2 - date1
|   assumes dates are in correct format
`========================================================*/

function dateCompare(olderDate, newerDate) {
    var tempolderDate = new Date(olderDate);
    var tempnewerDate = new Date(newerDate);
    var olderDateValue = tempolderDate.getTime();
    var newerDateValue = tempnewerDate.getTime();
    var DateDifference = newerDateValue - olderDateValue;
    var OneDay = 1000 * 60 * 60 * 24;
    return Math.ceil(DateDifference / OneDay);
}

/*========================================================\
|   DateAdd    
|   requires [String:date1] [Integer days, months, years]
|---------------------------------------------------------:
|   returns date with additional timespan
`========================================================*/
function DateAdd(theDate, addDays, addMonths, addYears) {
    var MonthDays = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
    var theDate = new Date(theDate);
    // get current day of month
    var currentDayOfMonth = theDate.getDate();
    var OneDay = 1000 * 60 * 60 * (24.05);

    // set day of month to 1 for month operation
    theDate.setDate(1);
    if (parseFloat(addMonths) > 0) {
        for (i = 0; i < addMonths; i++) {
            // adjusts for leap year
            MonthDays = DateAddCheckLeapYear(theDate.getFullYear(), MonthDays);
            theDate = new Date(theDate.getTime() + (MonthDays[theDate.getMonth()] * OneDay));
        }
    }
    // reset day of month to saved day of month
    theDate.setDate(currentDayOfMonth);

    if (parseFloat(addDays) > 0) {
        theDate = new Date(theDate.getTime() + (parseFloat(addDays) * OneDay));
    }

    if (parseFloat(addYears) > 0) {
        theDate.setYear(theDate.getFullYear() + addYears);
    }

    return formatProperDate(theDate);
}

/*========================================================\
|   DateAddCheckLeapYear    | requires Array
|   helper function for DateAdd
|---------------------------------------------------------:
|   returns array with adjustments for leap year
`========================================================*/
function DateAddCheckLeapYear(theYear, MonthDays) {
    if (theYear % 4 == 0) {
        if (theYear % 100 == 0 && theYear % 400 == 0) MonthDays[1] = 29;
        if (theYear % 100 != 0) MonthDays[1] = 29;
    }
    else {
        MonthDays[1] = 28;
    }
    return MonthDays;
}


/*========================================================\
|   selectOptionByValue    
|   requires [Object:Select] [String:value]
|---------------------------------------------------------:
|   selects the option where 
|   [Object:Select] option value = [String:value]
`========================================================*/

function selectOptionByValue(objSelect, strValue) {
    if (objSelect.options.length) {
        var x;
        for (x = 0; x < objSelect.options.length; x++) {
            if (trim(objSelect.options[x].value) == trim(strValue)) {
                objSelect.options.selectedIndex = x;
            }
        }
    }
}

/*========================================================\
|   getJobWebID 
|---------------------------------------------------------:
|   returns a unique string for use as a web ID
|   based on Universal Time and to the millisecond
`========================================================*/

/* ============ get JobWebID ================= */

function getJobWebID() {
    var Today = new Date();
    var TYear = Today.getUTCFullYear();
    if (TYear < 1000) {
        TYear += 1900;
    }
    TYear -= 2000;
    if (TYear < 10) {
        TYear = "0" + TYear;
    }
    var TMonth = Today.getUTCMonth() + 1
    var TDay = Today.getUTCDate();
    var THour = Today.getUTCHours();
    var TMinute = Today.getUTCMinutes();
    var TSecond = Today.getUTCSeconds();
    var TMseconds = Today.getUTCMilliseconds();
    if (TMseconds > 60) {
        TMseconds = parseInt((TMseconds / 17), 10);
    }
    var JobWebID = "G" + TYear + '' + IDEncode(TMonth) + '' + IDEncode(TDay) + '' + IDEncode(THour) + '' + IDEncode(TMinute) + '' + IDEncode(TSecond) + '' + IDEncode(TMseconds);
    return JobWebID;

    function IDEncode(theValue) {
        theValue = parseFloat(theValue);
        var theCodes = "0123456789ABCDEFGHIJKLMNpqrstuvwxyzabcdefghijklmnPQRSTUVWXYZ";
        var decode = theCodes.split("");
        if (theValue < 60 && theValue > 0) {
            return decode[theValue];
        }
        return theValue;
    }
}




/*========================================================\
|   arrayFromBinary  
|   requires [Number:bin], [Number:it]
|---------------------------------------------------------:
|   Iterates through binary progression
|   creating array of values, returns array
`========================================================*/
function arrayFromBinary(binaryNum, iterations) {
    var tempArray = new Array();
    if (isNaN(binaryNum) || parseFloat(binaryNum) < 1) {
        return tempArray;
    }
    var x;
    var localBinary = 1;
    for (x = 1; x <= iterations; x++) {
        if (((binaryNum & localBinary) / localBinary) == 1) {
            tempArray.push(localBinary);
        }
        localBinary *= 2;
    }
    return tempArray;
}




/*========================================================\
|   mathAddElementsInArray  |  requires [Array]
|---------------------------------------------------------:
|   returns number after Adding all elements in array
|   always returns number or zero
`========================================================*/
function mathAddUpArray(myArray) {
    var solution = 0;
    if (myArray.length) {
        var x;
        for (x = 0; x < myArray.length; x++) {
            if (!isNaN(trim(myArray[x]))) {
                solution += parseFloat(myArray[x]);
            }
        }
    }
    else {
        if (!isNaN(myArray)) {
            solution = parseFloat(myArray);
        }
    }
    return solution;
}

/*========================================================\
|   AlertFocus    
|   requires [Input Object] [String:message] [String:Tabid]
|---------------------------------------------------------:
|   1.opens alert with [String:message]
|   2.TRY switches to Tab Panel [String:Tabid]
|   3.brings focus to [Input Object]
|   4.returns false
`========================================================*/

function AlertFocus(field, message, tab) {
    if (message != undefined) {
        window.alert("AlertFocus: " + message);
    }
    if (tab != undefined) {
        TabPanel(tab);
    }
    try {
        field.focus();
    }
    catch (e) {
        // do nothing
        return false;
    }
    return false;
}

function ValidatePhone(phoneNumber) {
    var error = "";
    var stripped = phoneNumber.replace(/[\(\)\.\-\s]/g, '');

    if (phoneNumber == "") {
        return false;
    } else if (isNaN(parseInt(stripped))) {
        return false;
    } else if (!(stripped.length == 10)) {
        return false;
    }
    return true;
}

function ValidateZip(id) {
    var zip = $(id).val();
    if (!RegExZip.test(zip)) {
        return false
    }
    return true;
}





























