
/*########################################################,
|
|   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}$/;


/*========================================================,
|   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;
}



/*========================================================\
|   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;
	}
	var R1 = /[0-1]{1}[0-9]{2}/; 	// first digit must be 2 through 9
	var R2 = /[2-9]{1}11/;			// N11 is reserved
	var R3 = /[2-9]{1}9[0-9]{1}/;	// N9X is reserved
	var R4 = /37[0-9]{1}/;			// 37X is reserved
	var R5 = /96[0-9]{1}/;			// 96X is reserved
	
	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); }
	var regex1 = /[0-9]/g;          // must have at least 1 number
	var regex2 = /[a-zA-Z]/g;       // must have at least 1 letter
	var regex3 = /[^0-9a-zA-Z]/g;   // should only contain numbers and letters
	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)
{
    var CCRules = CreditCardRules(CtypeId); // instance of object
    var CardNumber = field.value;
        CardNumber = CardNumber.replace(/[^0-9]/g,''); // remove non-numbers
    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;
    for (var v=0;v<CCNumberLengthArray.length;v++) // validate card number length
    {
        if (CardNumber.length == CCNumberLengthArray[v])
        {
            ValidLength = true;
            break;
        }
    }
    if (!ValidLength)
    {
        return AlertFocus(field,CannedMessage + " (72)",tab);
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //  Prefix
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    var ValidPrefix = false;
    for (var v=0;v<CCPrefixValsArray.length;v++) // validate card prefix values
    {
        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);
	var currentDayOfMonth   = theDate.getDate(); // get current day of month
	var OneDay              = 1000*60*60*(24.05);
        
    theDate.setDate(1); // set day of month to 1 for month operation
	if (parseFloat(addMonths)>0)
	{
	    for (i=0;i<addMonths;i++) 
	    {
	        MonthDays = DateAddCheckLeapYear(theDate.getFullYear(),MonthDays); // adjusts for leap year
		    theDate  = new Date( theDate.getTime() + (MonthDays[theDate.getMonth()] * OneDay) ) ;
	    }
	}
	theDate.setDate(currentDayOfMonth);  // reset day of month to saved day of month
	
	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;
}
