﻿// ********************************************************** //
// JavaScript Validation Suite.                              //
//                                                            //
// Functions:                                                 //
//   regExValidate(x,x,x,x,x)                                 //
//      AlphaText, AlphaWord, ASCIIText, ASCIIWord, 	      //
//	    ComplexInteger, Date, EMail, Required,	              //
//	    SimpleInteger, SimpleText, SimpleWord, StateCode,     //
//      URL, Year, ZipCode                                    //
//   checkPhone(x,x,x)                                        //
//      formatPhone(x)                                        //
//   checkCurrency(x,x,x)                                     //
//      formatCurrency(x)                                     //
//   chooseAlert(x,x,x,x)                                     //
//   finalCheck()                                             //
//                                                            //
// ********************************************************** //

// ********************************************************** //
// regExValidate: validates an input based on a given regEx   //
// flagTiming: 1 - RealTime, 0 onSubmit                       //
// ********************************************************** //
function regExValidate(theField, strFieldName, strValidationType, intRequiredLength, flagTiming) {  
    var boolValidates = true;
    var objRegExp = /./;

    switch (strValidationType) {
        case 'AlphaText':
            // Matches [A-Z], [a-z] - letters only.
            objRegExp = /^[a-zA-Z ]+$/;
            break
        case 'AlphaWord':
            // Matches [A-Z], [a-z] - letters only, no spaces.
            objRegExp = /^[a-zA-Z]+$/;
            break
        case 'ASCIIText':
            // Matches only keys found on a keyboard (no extended ASCII) **AND LINE BREAKS/WHITESPACE**.
            // objRegExp = /^([\w\s`~!@#\$%\^&\*\(\)-_\+={\[}\]\|\\:;"'<,>\.\?\/]|&ldquo;|&#8220;|&rdquo;|&#8221;|&lsquo;|&#8216;|&rsquo;|&#8217;|&ndash;|&#8211;|&mdash;|&#8212;|&acute;|&#180;|&hellip;|&#8230;)*$/;      
            objRegExp = /^([-\w\s`~!@#\$%\^&\*\(\)\-_\+={\[}\]\|\\:;"'<,>\.\?\/]|&ldquo;|&#8220;|&rdquo;|&#8221;|&lsquo;|&#8216;|&rsquo;|&#8217;|&ndash;|&#8211;|&mdash;|&#8212;|&acute;|&#180;|&hellip;|&#8230;)*$/;
            break
        case 'ASCIIWord':
            // Matches only keys found on a keyboard (no extended ASCII), no spaces.
            objRegExp = /^[-\w`~!@#$%^&amp;*\(\)+={}|\[\]\\:&quot;;'&lt;&gt;?,.\/]*$/;
            break
        case 'ComplexInteger':
            // Matches positive or negative integers, with commas. 
            objRegExp = /^[-]?(\d?\d?\d?,?)?(\d{3}\,?)*$/;

            // If the first character is a negative symbol, extend the minimum length
            // by one to account for this extra character
            if (theField.value.indexOf("-") == 0) {
                intRequiredLength++;
            }
            break
        case 'Date':
            // Matches dates in MM/DD/YYYY Format
            objRegExp = /^(0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])[- /.](19|20)\d\d$/;
            break
        case 'Decimal':
						// Matches any positive or negative real number, with optional decimal point and numbers after the decimal.
						objRegExp = /^[-]?\d+(\.\d+)?$/;

            // If the first character is a negative symbol, extend the minimum length
            // by one to account for this extra character
            if (theField.value.indexOf("-") == 0) {
                intRequiredLength++;
            }
            break
        case 'EMail':
            // Matches e-mail addresses only.
            objRegExp = /^\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3}$/;
            break
        case 'SimpleInteger':
            // Matches whole numbers only 
            objRegExp = /^[0-9]+$/;
            break
        case 'SimpleWord':
            // Matches [A-Z], [a-z], [,], ['], [.], [-]. No spaces. Mostly for names. 
            objRegExp = /^([a-zA-Z,'.\-]+)$/;
            break
        case 'SimpleText':
            // Matches [A-Z], [a-z], [,], ['], [.], [-]. Mostly for names. 
            objRegExp = /^([a-zA-Z,'.\- ]+)$/;
            break
        case 'StateCode':
            // Matches upper-case 2-letter State Codes only.
            objRegExp = /^((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))$/;
            break
        case 'URL':
            // Matches an external, qualified URL only (http or https).
            objRegExp = /^((([hH][tT][tT][pP][sS]?)\:\/\/)?([\w\.\-]+(\:[\w\.\&%\$\-]+)*@)?((([^\s\(\)\<\>\\\"\.\[\]\,@;:]+)(\.[^\s\(\)\<\>\\\"\.\[\]\,@;:]+)*(\.[a-zA-Z]{2,4}))|((([01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}([01]?\d{1,2}|2[0-4]\d|25[0-5])))(\b\:(6553[0-5]|655[0-2]\d|65[0-4]\d{2}|6[0-4]\d{3}|[1-5]\d{4}|[1-9]\d{0,3}|0)\b)?((\/[^\/][\w\.\,\?\'\\\/\+&%\$#\=~_\-@]*)*[^\.\,\?\"\'\(\)\[\]!;<>{}\s\x7F-\xFF])?)$/;
            break
        case 'Year':
            // Matches a four-digit Year only. 
            objRegExp = /^(19|20)\d\d$/;
            break
        case 'ZipCode':
            // Matches a five or nine digit Zip Code ('12345' or '12345-4567') only. 
            objRegExp = /(^\d{5}$)|(^\d{5}-\d{4}$)/;
            break
    }

    // Check against the regex expression. If a failure is found, send params to chooseAlert.
    // If flagTiming = 0 (RealTime), chooseAlert will display the alert and return false.
    // If flagTiming = 1 (finalCheck), chooseAlert will return the alert text, so that
    // we can concat them and display all the finalCheck messages at the same time.
    if ((theField.value != "") && (!objRegExp.test(theField.value))) {
        return chooseAlert(strValidationType, theField, strFieldName, flagTiming);
    }
    else if ((theField.value == "") && ((strValidationType == "Required") || (intRequiredLength > 0))) {
        return chooseAlert('Required', theField, strFieldName, flagTiming);
    }
    else if (theField.value.length < intRequiredLength) {
        return chooseAlert('Length', theField, strFieldName, flagTiming);
    }
    else {
        theField.style.background = "#ffffff";
    }
    
    // If there were no failures found, we need to return gracefully. For RealTime checks, that means
    // returning True. For a finalCheck, that means returning an empty string.
    if (flagTiming == 0) {
        return true;
    }
    else {
        return "";
    }

}


// ********************************************************** //
// Check Phone: 1-555-123-4567					              //
// ********************************************************** //
function checkPhone(theField, strFieldName, boolIsRequired, flagTiming) {
    var strRawPhoneNumber = "";
    var i = 0;

    if (theField.value == "" && boolIsRequired == true && flagTiming == 1) {
        return chooseAlert('Required', theField, strFieldName, flagTiming);
    }
    else if (theField.value != "") {
        while (i != theField.value.length) {
        
            if (!isNaN(parseInt(theField.value.charAt(i)))) {
                strRawPhoneNumber = strRawPhoneNumber + theField.value.charAt(i);
            }
            i++;
        }
        if (strRawPhoneNumber.length < 10 || strRawPhoneNumber.length > 11) {
            return chooseAlert('Phone', theField, strFieldName, flagTiming);
        }
        else if (strRawPhoneNumber.length == 7) {
            return chooseAlert('Phone-AreaCode', theField, strFieldName, flagTiming);
        }
        else {
            theField.style.background = "#ffffff";
	        theField.value = formatPhone(strRawPhoneNumber);
	        
            // If there were no failures found, we need to return gracefully. For RealTime checks, that means
            // returning True. For a finalCheck, that means returning an empty string.
            if (flagTiming == 0) {
                return true;
            }
            else {
                return "";
            }
        }
    }
    else {
        // If there were no failures found, we need to return gracefully. For RealTime checks, that means
        // returning True. For a finalCheck, that means returning an empty string.
        if (flagTiming == 0) {
            return true;
        }
        else {
            return "";
        }
    }
    
}

// ********************************************************** //
// Format Phone: 1-555-123-4567					              //
// ********************************************************** //
function formatPhone(strValue) {
    if (strValue.length == 10) {
        return "1-" + strValue.substr(0, 3) + "-" + strValue.substr(3, 3) + "-" + strValue.substr(6, 4);
    }
    else if (strValue.length == 11) {
        return "1-" + strValue.substr(1, 3) + "-" + strValue.substr(4, 3) + "-" + strValue.substr(7, 4);
    }
    
}


// ********************************************************** //
// Check Currency: -$100,000.00					              //
// ********************************************************** //
function checkCurrency(theField, strFieldName, boolIsRequired, flagTiming) {

	var intDecimalCount = 0;	// Total times a decimal point occurs.
	var intNegativeCount = 0;	// Total times a negative sign occurs.
	var i;			            // Loop Counter

    if (theField.value != "") {

        // Make sure there are no illegal characters
	    for(i = 0; i < theField.value.length; i++) {

            switch (theField.value.substr(i, 1)) {
                case '$':
                    break
                case '-':
    			    intNegativeCount = intNegativeCount + 1;
                    break
                case '.':
    			    intDecimalCount = intDecimalCount + 1;
                    break
                case ',':
                    break
                default:
                    // If we fall into the 'else', the character is either a number, or
                    // a different character. 
                    if (parseInt(theField.value.substr(i, 1)) >= 0 || parseInt(theField.value.substr(i, 1)) <= 9) {
                        // do nothing, character is a valid numeral
                    }
                    else {
                        return chooseAlert('Currency', theField, strFieldName, flagTiming);
                    }
                    break
            }
        }

        // Make sure there isn't more than one decimal or negative sign
	    if((intDecimalCount > 1) || (intNegativeCount > 1)) {
            return chooseAlert('Currency', theField, strFieldName, flagTiming);
	    }
	
	    // Make sure the decimal is in the proper position
		/**********************************/
		/*   $#.##, $#.#, $.#, $#., $.##  */
		/**********************************/
	    if(intDecimalCount == 1) {
    		if(((theField.value.length - 1) - theField.value.indexOf(".")) > 2) {
                return chooseAlert('Currency', theField, strFieldName, flagTiming);
		    }
	    }
	    
	    // Make sure the negative sign is in the proper position
		/**********************************/
		/*   -$#.##                       */
		/**********************************/
	    if(intNegativeCount == 1) {
    		if(theField.value.indexOf("-") != 0) {
                return chooseAlert('Currency', theField, strFieldName, flagTiming);
		    }
	    }
	
	    // If we've gotten this far, the string contains only numerals, commas, 
	    // dollar signs, and one, correctly-placed decimal. At this point, the string
	    // is valid enough to be reformatted. Here, we strip out any non-numerical or
	    // non-comma characters ('$' and ',') and then re-format the currency to our
	    // liking.
	    
	    // Strip out other allowed currency characters: '$', ','
        theField.style.background = "#ffffff";
        theField.value = theField.value.toString().replace(/\$|\,/g,'');
	    theField.value = formatCurrency(theField.value);
	}
    else if (theField.value == "" && boolIsRequired == true) {
        return chooseAlert('Required', theField, strFieldName, flagTiming);
    }

    // If there were no failures found, we need to return gracefully. For RealTime checks, that means
    // returning True. For a finalCheck, that means returning an empty string.
    if (flagTiming == 0) {
        return true;
    }
    else {
        return "";
    }
}

// ********************************************************** //
// Format Currency: $100,000.00					              //
// ********************************************************** //
function formatCurrency(strValue) {
	strValue = strValue.toString().replace(/\$|\,/g,'');
	dblValue = parseFloat(strValue);

	blnSign = (dblValue == (dblValue = Math.abs(dblValue)));
	dblValue = Math.floor(dblValue * 100 +0.50000000001);
	intCents = dblValue%100;
	strCents = intCents.toString();
	dblValue = Math.floor(dblValue / 100).toString();
	if (intCents < 10)
		strCents = "0" + strCents;
	for (var i = 0; i < Math.floor((dblValue.length - (1 + i)) / 3); i++)
		dblValue = dblValue.substring(0, dblValue.length - (4 * i + 3)) + ',' +
		dblValue.substring(dblValue.length - ( 4 * i + 3));
	return (((blnSign)?'':'-') + '$' + dblValue + '.' + strCents);
}


// ********************************************************** //
// Function to Display the Alert				              //
// ********************************************************** //
function chooseAlert(strFieldType, theField, strFieldName, flagTiming) {

    if (flagTiming == 0) {
        strAlert = "CAIS Form Processing System Alert:\n\n";
    }
    else {
        strAlert = "";
    }

    switch (strFieldType) {
        case 'AlphaText':
            strAlert += "The " + strFieldName + " field is formatted incorrectly. It can contain only letters (A-Z and a-z) and spaces.";
            break
        case 'AlphaWord':
            strAlert += "The " + strFieldName + " field is formatted incorrectly. It can contain only letters (A-Z and a-z).";
            break
        case 'ASCIIText':
            strAlert += "The " + strFieldName + " field is formatted incorrectly. It can contain only ASCII characters and spaces.";
            break
        case 'ASCIIWord':
            strAlert += "The " + strFieldName + " field is formatted incorrectly. It can contain only ASCII characters.";
            break
        case 'ComplexInteger':
            strAlert += "The " + strFieldName + " field is formatted incorrectly. It must be a postive or negative Integer (only whole numbers, commas are allowed).";
            break
        case 'Currency':
            strAlert += "The " + strFieldName + " field is formatted incorrectly. It must be in proper Currency format, for example: '$100.00' or '100,000.00' or '20'.";
            break
        case 'Date':
            strAlert += "The " + strFieldName + " field is formatted incorrectly. It must be in MM/DD/YYYY format, for example: '01/23/1984' or '10/01/2007'.";
            break
        case 'Decimal':
            strAlert += "The " + strFieldName + " field is formatted incorrectly. It must be a postive or negative Decimal Number, for example, 12.45542 or -872.";
            break
        case 'EMail':
            strAlert += "The " + strFieldName + " field is formatted incorrectly. It must be in proper E-mail format, for example: 'johndoe@udel.edu' or 'bob@hotmail.com'.";
            break
        case 'Length':
            strAlert += "The " + strFieldName + " field does not meet or has exceeded the required length.";
            break
        case 'Phone':
            strAlert += "The " + strFieldName + " field is formatted incorrectly. It must be in proper Phone Number format, for example: '1-302-555-1234' or '(302) 123-4567'.";
            break
        case 'Phone-AreaCode':
            strAlert += "The " + strFieldName + " field is formatted incorrectly. It must include the Area Code, for example: '1-302-555-1234' or '(302) 123-4567'.";
            break
        case 'Required':
            strAlert += "The " + strFieldName + " field is a required field.";
            break
        case 'SimpleInteger':
            strAlert += "The " + strFieldName + " field is formatted incorrectly. It must be an Integer (only whole numbers).";
            break
        case 'SimpleText':
            strAlert += "The " + strFieldName + " field is formatted incorrectly. It can only contain the following characters: [A-Z], [a-z], [,], ['], [.], [-], and spaces.";
            break
        case 'SimpleWord':
            strAlert += "The " + strFieldName + " field is formatted incorrectly. It can only contain the following characters: [A-Z], [a-z], [,], ['], [.], [and -].";
            break
        case 'StateCode':
            strAlert += "The " + strFieldName + " field is formatted incorrectly. It must be a valid, upper-case, two-letter state code, for example: DE, NY, TX, or CA.";
            break
        case 'URL':
            strAlert += "The " + strFieldName + " field is formatted incorrectly. It must be in proper URL format, for example: 'http://www.google.com' or 'https://www.mysite.com/hello.html'.";
            break
        case 'Year':
            strAlert += "The " + strFieldName + " field is formatted incorrectly. It must be in proper Year format, from 1900 to 2099.";
            break
        case 'ZipCode':
            strAlert += "The " + strFieldName + " field is formatted incorrectly. It must be in proper Zip Code format, for example: '12345' or '12345-6789'.";
            break
    }

    // If flagTiming = 0 (RealTime), chooseAlert will display the alert and return false.
    // If flagTiming = 1 (finalCheck), chooseAlert will return the alert text, so that
    // we can concat them and display all the finalCheck messages at the same time.
    if (flagTiming == 0) {
	    strAlert += "\n\nPlease correct this before submitting the form.\n\n ";
        alert(strAlert);
        theField.style.background = "#fff1cf";
        setTimeout(function(){theField.select();}, 100);
        //theField.select();
        //setTimeout(function(){theField.focus();}, 100);
        return false;
    }
    else {
        theField.style.background = "#fff1cf";
        return strAlert;
    }

}


// ********************************************************** //
// Function to finally check the form				          //
// ********************************************************** //
function finalCheck(aryFields,strExternalRedirect) {
    var theField;
    var i = 0;
    var aryParams;
    var boolFoundError = false;
    var strAlert = "CAIS Form Processing System Alert:\n\n";
    var strError = "";

    for(i=0; i<=aryFields.length-1; i++) {

	// The Parameters Expected by aryFields:
        // 0 = theField
        // 1 = strFieldName
        // 2 = strValidationType
        // 3 = intRequiredLength
        // 4 = strFunction
        aryParams = aryFields[i].split(",");

        theField = document.forms[0][aryParams[0]];
        theField.style.background = "#ffffff";

        switch (aryParams[4]) {
            case 'RegEx':
	            strError = regExValidate(theField, aryParams[1], aryParams[2], aryParams[3], 1);
	            break
            case 'Currency':
	            strError = checkCurrency(theField, aryParams[1], aryParams[3], 1);
	            break
            case 'Phone':
	            strError = checkPhone(theField, aryParams[1], aryParams[3], 1);
	            break
	    }
	
	    if (strError != "") {
		    boolFoundError = true;
		    strAlert += strError + '\n';
	    }
    }

    if (boolFoundError) {
	    strAlert += "\nPlease correct this before submitting the form.\n\n ";
	    alert(strAlert);
	    return false;
    }
    else {
        // If we are using an external redirect (i.e. an e-mail form that uses the CEOE form
        // robot, we have to use a traditional <input type="button"> in which case, we have
        // to submit the form manually when the validation script is successfully executed.
        if (strExternalRedirect) {
            theForm.action = strExternalRedirect;
	        theForm.submit();
        }
	    return true;
    }
}