///////////////////////////////////////////////////////////////////////////
// data_qualify.js
//
// data qualification funtionality - performs qualification based on hidden
//        values in the form.
//
// EXAMPLE: if there is a field called name, and you wish to make sure that it is
//        required, you would put this value in the form:
//
// <INPUT TYPE="hidden" NAME="name_r" VALUE="[pretty field name string]">
//
// TESTS:
//
// - required text field: _r
// - required pulldown: _r
// - required radio button: _r
// - required numeric field: _n
// - required php array pulldown (where name contains "[]"): _s
// - e-mail format: _e
// - url format: _u
// - numeric format: _i
// - float format: _f
// - date format: _d
// - phone format: _p
// - social security # format: _o
// - credit card # format: _c
// - password (minimum check for security): _a
// - textarea word count: _t
///////////////////////////////////////////////////////////////////////////

var BROWSER_IE = (document.all) ? true : false;
var BROWSER_NET = (document.layers) ? true : false;
var BROWSER_NET_SIX =
	((document.getElementById) && (! BROWSER_IE)) ? true : false;
var BROWSER_VER_4_PLUS =
	(BROWSER_NET_SIX || BROWSER_NET || BROWSER_IE) ? true : false;

// the default date format (lower case here for display purposes)
var DATE_FORMAT_DEFAULT = "mm/dd/yyyy";

var PHONE_FORMAT_DEFAULT = "xxx-xxx-xxxx";

// the word count default
var WORD_COUNT_DEFAULT = 25;

// the array of upper and lower case letters
var UPPER_CASE = new Array("A","B","C","D","E","F","G","H","I","J",
        "K","L","M","N","O","P","Q","R","S","T", "U","V","W","X","Y","Z");
var LOWER_CASE = new Array("a","b","c","d","e","f","g","h","i","j",
        "k","l","m","n","o","p","q","r","s","t", "u","v","w","x","y","z");

// regexp strings
var RE_INT = /^\d*$|^[\-]\d*$/;
var RE_DIGIT = /\d/;
var RE_FLOAT = /^[\d]*\.\d*$|^[\-]\d*$/;
var RE_STR = /[a-zA-Z]/;
var RE_ALL = /[a-zA-Z0-9]/;
var RE_CHARS = (/[\?,'"|&\|\*\^%\$#@!\{\}\[\]]/);
var RE_URL = (/^(http|https):\/\/(\S+\.)+([0-9]{1,3}|[a-z]{2,4})((\/)?|(\/+\S*))(\/)?$/);
var RE_DATE = /^([0-1][0-9])\/(([0-2][0-9])|([3][0-1]))\/(([1][9]|[2][0-9])[0-9][0-9])$/;
var RE_MAIL = /^([\da-zA-Z])\S+(\@)+[\da-zA-Z+]\S+(\.[a-zA-Z]{2,4}$)/;
var RE_PHONE = /^\d{3}\-\d{3}\-\d{4}$/;
var RE_SSN = /^\d{3}\-\d{2}\-\d{4}$/;
var RE_PASSWORD = /\w{5,12}/;

// with the form in layers, the actual form location varies by browser
var formLocation;

// field tracking arrays
var reqFields = new Array();
var reqNums = new Array();
var fieldNames = new Array();

// the required text field arrays (_r)
var textNames = new Array();
var textPretty = new Array();

// the required pulldown arrays (_r)
var radioNames = new Array();
var radioPretty = new Array();

// the position of the radio in the form
var radioPos = new Array();

// the required radio button arrays (_r)
var pullNames = new Array();
var pullPretty = new Array();

// the required numeric field arrays (_n)
var reqnumNames = new Array();
var reqnumPretty = new Array();

// the required php array pulldown arrays (_s)
var pullPHPMultiNames = new Array();
var pullPHPMultiPretty = new Array();

// the e-mail format arrays (_e)
var emailNames = new Array();
var emailPretty = new Array();

// the url format arrays (_u)
var urlNames = new Array();
var urlPretty = new Array();

// the numeric format arrays (_i)
var numberNames = new Array();
var numberPretty = new Array();

// the float format arrays (_f)
var floatNames = new Array();
var floatPretty = new Array();

// the date format arrays (_d)
var dateNames = new Array();
var datePretty = new Array();

// the textarea word count arrays (_t)
var textareaNames = new Array();
var textareaPretty = new Array();

// the ssn arrays (_o)
var ssnNames = new Array();
var ssnPretty = new Array();

// the cc arrays (_c)
var ccNames = new Array();
var ccPretty = new Array();

// the password arrays (_a)
var passwordNames = new Array();
var passwordPretty = new Array();

// the phone arrays (_p)
var phoneNames = new Array();
var phonePretty = new Array();

// is it a leap year?
function isLeapYear (y) {
    return (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0));
}

// is it the 29th of feb on a leap year?
function isLeapYearDate (m, d, y) {
    return ((m == 2) && (d == 29) && (isLeapYear(y)));
}

// where's waldo?
function establishFormLocation () {

   //     var rhett = "";
   //     
   //     if (BROWSER_NET) {
   //         rhett = "document.[whatever_layer].document.bodyForm";
   //     } else if (BROWSER_NET_SIX) {
   //         rhett = "document.[whatever_layer].bodyForm";
   //     } else {
   //             // the default - this will catch browsers like net3 that don't
   //         // respond to that wacky dhtml stuff...
   //         rhett = "document.bodyForm";    
   //     }
   // 
   //         
   // return rhett;

   // simple model for now
   return "document.bodyForm";    

}

// onload this will sort out all the required fields indicated    
function getReqFields () {

    // establish form location now
    this.formLocation = this.establishFormLocation();

    // create the form object string for use below
    var formObj = eval(this.formLocation);
    
    // get all the required field names
    for (var i=0; i < formObj.elements.length; i++) {
        var x = formObj.elements[i].name;
        var rawstr = x.substring(0,(x.length-2));

        if (x.substring((x.length-2),(x.length)) == "_r") {
            // required string
            reqFields[reqFields.length] = rawstr;
        } else if (x.substring((x.length-2),(x.length)) == "_s") {
            // required numeric format
            this.popReqArray(pullPHPMultiNames, pullPHPMultiPretty, (rawstr + "[]"), "_s");
        } else if (x.substring((x.length-2),(x.length)) == "_n") {
            // required numeric format
            this.popReqArray(reqnumNames, reqnumPretty, rawstr, "_n");
        } else if (x.substring((x.length-2),(x.length)) == "_e") {
            // e-mail format
            this.popReqArray(emailNames, emailPretty, rawstr, "_e");
        } else if (x.substring((x.length-2),(x.length)) == "_u") {
            // basic url format
            this.popReqArray(urlNames, urlPretty, rawstr, "_u");
        } else if (x.substring((x.length-2),(x.length)) == "_i") {
            // numeric format
            this.popReqArray(numberNames, numberPretty, rawstr, "_i");
        } else if (x.substring((x.length-2),(x.length)) == "_f") {
            // float format
            this.popReqArray(floatNames, floatPretty, rawstr, "_f");
        } else if (x.substring((x.length-2),(x.length)) == "_d") {
            // date (mm/dd/yyyy) format
            this.popReqArray(dateNames, datePretty, rawstr, "_d");
        } else if (x.substring((x.length-2),(x.length)) == "_t") {
            // textarea word count
            this.popReqArray(textareaNames, textareaPretty, rawstr, "_t");
        } else if (x.substring((x.length-2),(x.length)) == "_p") {
            // phone format
            this.popReqArray(phoneNames, phonePretty, rawstr, "_p");
        } else if (x.substring((x.length-2),(x.length)) == "_o") {
            // ssn format
            this.popReqArray(ssnNames, ssnPretty, rawstr, "_o");
        } else if (x.substring((x.length-2),(x.length)) == "_c") {
            // cc format
            this.popReqArray(ccNames, ccPretty, rawstr, "_c");
        } else if (x.substring((x.length-2),(x.length)) == "_a") {
            // password format
            this.popReqArray(passwordNames, passwordPretty, rawstr, "_a");
        }

        // add name to general field name array
        // we need to search this later and it is faster
        // than the "this.formLocation.elements" array
        fieldNames[fieldNames.length] = x;
    }    

    // record the array element number so we
    // can access these elements later
    for (z=0; z < reqFields.length; z++) {
        for (var i=0; i < fieldNames.length; i++) {
            if (fieldNames[i] == reqFields[z]) {
                reqNums[reqNums.length] = i;
                break;
            }
        }
    }

    // create different field type arrays
    for (q=0; q < reqNums.length; q++) {

        // add to the proper array
        if ((formObj.elements[reqNums[q]].type == "text") ||
                (formObj.elements[reqNums[q]].type == "file")) {
            // required field is a text field
            this.popReqArray(textNames, textPretty, reqFields[q], "_r");
        } else if (formObj.elements[reqNums[q]].type == "password") {
            // required field is a password field
            this.popReqArray(textNames, textPretty, reqFields[q], "_r");
        } else if (formObj.elements[reqNums[q]].type == "textarea") {
            // required field is a textarea field
            this.popReqArray(textNames, textPretty, reqFields[q], "_r");
        } else if (formObj.elements[reqNums[q]].type == "radio") {
            // required field is a radio button
            this.popReqArray(radioNames, radioPretty, reqFields[q], "_r");
            // radioPos[radioPos.length] = reqNums[q];
        } else if (formObj.elements[reqNums[q]].type.indexOf("select") != -1) {
            // required field is a pulldown
            this.popReqArray(pullNames, pullPretty, reqFields[q], "_r");
        }        
    }
}

// getReqFields uses this function to populate arrays
function popReqArray (dataArray, prettyArray, fieldName, reqType) {
    // this function pushes data into proper arrays
    //        it does not use the push() function so that
    //        this script can be used by Net3, IE3

    // push into proper array
    dataArray[dataArray.length] = fieldName;
    // get the pretty alert name
    var y;

    // god i hate the internet
    if (reqType == "_s") {
        y = eval(this.formLocation + "['" + fieldName.substring(0, (fieldName.length - 2)) + reqType + "'].value");
    } else {
        y = eval(this.formLocation + "['" + fieldName + reqType + "'].value");
    }

    // push pretty name into proper array
    prettyArray[prettyArray.length] = y;
}

// processes a bad data field by alerting the user
//        with the passed message, focusing on the bad field,
//        re-enabling the buttons and returning false
function processBadElement (el, msg) {
    // let them know...
    alert(msg);
    // re-enable buttons
    enableFormButtons();
    // focus on the field
    el.focus();
    if (el.type == "text") {
        el.select();
    }
    // return bad
    return false;
}

// data qualification
function checkForm (button) {

    // disable form buttons
    this.disableFormButtons(button);

    // check required fields
    for (var i=0; i < textNames.length; i++) {
        var formstr = eval(this.formLocation + "['" + textNames[i] + "']");
        if ((formstr.value.length == 0) || (! RE_ALL.test(formstr.value))) {
            // bad format
            var msg = "You did not enter a " + textPretty[i]
                        + " value.\n\nThis field is needed for the form to be submitted.\n";
            // let them know
            return this.processBadElement(formstr, msg);
        }
    }

    // check numeric required fields
    for (var i=0; i < reqnumNames.length; i++) {
        var formstr = eval(this.formLocation + "['" + reqnumNames[i] + "']");
        // regexp test
        if ((formstr.value.length == 0) ||
                    (RE_STR.test(formstr.value)) ||
                    (RE_CHARS.test(formstr.value))) {
            // bad format
            var msg = "Your " + reqnumPretty[i]
                    + " is not\nin the required numeric-only format.\n";
            // let them know
            return this.processBadElement(formstr, msg);
        }
    }

    // check the php multi pulldowns
    for (var i=0; i < pullPHPMultiNames.length; i++) {
        var formstr = eval(this.formLocation + "['" + pullPHPMultiNames[i] + "']");

        if ((formstr.selectedIndex == -1) ||
                (formstr[formstr.selectedIndex].value == "")) {
            // bad format
            var msg = "You did not specify a " + pullPHPMultiPretty[i]
                    + ".\n\nThis field is needed for the form to be submitted.\n";
            // let them know
            return this.processBadElement(formstr, msg);
        }
    }

    // check the pulldowns
    for (var i=0; i < pullNames.length; i++) {
        var formstr = eval(this.formLocation + "['" + pullNames[i] + "']");

        if ((formstr.selectedIndex == -1) ||
                (formstr[formstr.selectedIndex].value == "")) {
            // bad format
            var msg = "You did not specify a " + pullPretty[i]
                    + ".\n\nThis field is needed for the form to be submitted.\n";
            // let them know
            return this.processBadElement(formstr, msg);
        }
    }

    // check the required radio buttons
    for (var i=0; i < radioNames.length; i++) {
        var formstr = eval(this.formLocation + "['" + radioNames[i] + "']");

        var nothingChecked = true;
        for (var x=0; x < formstr.length; x++) {
            if (formstr[x].checked) {
                nothingChecked = false;
                break;
            }
        }

        if (nothingChecked) {
            // bad format
            var msg = "You did not specify a " + radioPretty[i]
                        + ".\n\nThis field is needed for the form to be submitted.\n";
            // let them know (manually alert here due to radio type - 
            // ie sometimes won't properly identify radios via a type
            // check, and so we can't properly test in processBadElement
            // to provide the right kind of element focus...ugh
            alert(msg);
            formstr[0].focus();
            // re-enable in case of error
            this.enableFormButtons();
            // return bad
            return false;
        }
    }

    // test all e-mail addresses
    for (var i=0; i < emailNames.length; i++) {
        var formstr = eval(this.formLocation + "['" + emailNames[i] + "']");
        // the bad characters
        var dot = formstr.value.indexOf(".");
        var atsign = formstr.value.indexOf("@");
        if ((! RE_MAIL.test(formstr.value)) && (formstr.value.length != 0) && (formstr.value != "NA")) {
            // bad format
            var msg = "The " + emailPretty[i] + " address that you\nprovided ("
                    + formstr.value + ") is\nnot in the proper format.\n";
            // let them know
            return this.processBadElement(formstr, msg);
        }
    }

    // test all the urls
    for (var i=0; i < urlNames.length; i++) {
        var formstr = eval(this.formLocation + "['" + urlNames[i] + "']");        
        if ((formstr.value != "") && ((! RE_URL.test(formstr.value)) || (RE_CHARS.test(formstr.value)))) {
            // bad format
            var msg = "The " + urlPretty[i] + " URL that you\nprovided ("
                    + formstr.value + ") is\nnot in the proper format.\n";
            // let them know
            return this.processBadElement(formstr, msg);
        }
    }

    // test the number-only fields
    for (var i=0; i < numberNames.length; i++) {
        var formstr = eval(this.formLocation + "['" + numberNames[i] + "']");
        if (! RE_INT.test(formstr.value)) {
            // bad format
            var msg = "Your " + numberPretty[i] + " is not in the\nproper "
                    + "numeric format.\n\nPlease use only non-decimal numbers.\n";
            // let them know
            return this.processBadElement(formstr, msg);
        }
    }

    // test the float-only fields
    for (var i=0; i < floatNames.length; i++) {
        var formstr = eval(this.formLocation + "['" + floatNames[i] + "']");
        if (! RE_FLOAT.test(formstr.value)) {
            // bad format
            var msg = "Your " + floatPretty[i] + " is not in the\nproper "
                    + "decimal format.\n\nPlease use only decimal numbers.\n";
            // let them know
            return this.processBadElement(formstr, msg);
        }
    }

    // test specific fields for date format (mm/dd/yyyy)
    for (var i=0; i < dateNames.length; i++) {
        var formstr = eval(this.formLocation + "['" + dateNames[i] + "']");

        if (formstr.value == "") {
            continue;
        }

        // check for specifics
        var m = (formstr.value.substring(0, 1) == "0") ? parseInt(formstr.value.substring(1, 2)) : parseInt(formstr.value.substring(0, 2));
        var d = (formstr.value.substring(3, 4) == "0") ? parseInt(formstr.value.substring(4, 5)) : parseInt(formstr.value.substring(3, 5));
        var y = parseInt(formstr.value.substring(6, 10));
        var maxDays = new Array(31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

        if ((! RE_DATE.test(formstr.value)) ||
                    (m == 0) || (m > 12) || (d == 0) ||
                    ((d > maxDays[m - 1]) && (! isLeapYearDate(m, d, y)))) {
            // bad format
            var msg = "Your " + datePretty[i]
                    + " is in the wrong\nformat or is not a realistic date.\n\n"
                    + "Please use " + DATE_FORMAT_DEFAULT + " format.\n";
            // let them know
            return this.processBadElement(formstr, msg);
        }
    }

    // test textarea fields for 75 words or less
    for (var i=0; i < textareaNames.length; i++) {
        var formstr = eval(this.formLocation + "['" + textareaNames[i] + "']");
        var wc = 0;
        for (t = 0; t < formstr.value.length-1; t++) {
            if (formstr.value.substring(t, t+1) == " ") {
                wc++;
            }
        }
        // add one for accuracy (word count is one higher than space count)
        wc++;
        // too many words!
        if (wc > WORD_COUNT_DEFAULT)  {
            // bad format
            var msg = "Your " + textareaPretty[i]
                    + " text\narea has too many words (" + wc + ").\n\n"
                    + "Please use " + WORD_COUNT_DEFAULT + " words or less.\n";

            // let them know
            return this.processBadElement(formstr, msg);
        }
    }

    // test for phone numbers
    for (var i=0; i < phoneNames.length; i++) {
        var formstr = eval(this.formLocation + "['" + phoneNames[i] + "']");
        if ((! RE_PHONE.test(formstr.value)) && (formstr.value != "")) {
            // bad format
            var msg = "Your " + phonePretty[i] + " is in the wrong format.\n\n"
                    + "Please use " + PHONE_FORMAT_DEFAULT + " format.\n";
            // let them know
            return this.processBadElement(formstr, msg);
        }
    }

    // test for ssn numbers
    for (var i=0; i < ssnNames.length; i++) {
        var formstr = eval(this.formLocation + "['" + ssnNames[i] + "']");
        if ((! RE_SSN.test(formstr.value)) && (formstr.value != "")) {
            // bad format
            var msg = "Your " + ssnPretty[i] + " is in the wrong format.\n\n"
                    + "Please use xxx-xx-xxxx format.\n";
            // let them know
            return this.processBadElement(formstr, msg);
        }
    }

    // test for cc numbers
    for (var i=0; i < ccNames.length; i++) {
        var formstr = eval(this.formLocation + "['" + ccNames[i] + "']");
        if ((formstr.value != "") && (! ccIsValidFormat(formstr.value))) {
            // bad format
            var msg = "Your " + ccPretty[i] + " is not a properly formatted number.\n\n"
                    + "Please check this number again now.\n";
            // let them know
            return this.processBadElement(formstr, msg);
        }
    }

    // test for password quality
    for (var i=0; i < passwordNames.length; i++) {
        var formstr = eval(this.formLocation + "['" + passwordNames[i] + "']");
        if (((! RE_PASSWORD.test(formstr.value)) || (! RE_DIGIT.test(formstr.value))) && (formstr.value != "")) {
            // bad format
            var msg = "Your " + passwordPretty[i] + " is too insecure.\n\n"
                    + "Please use any combination of letters and at\n"
                    + "least one number that results in a password that\n"
                    + "is at least 6 characters in length.\n";
            // let them know
            return this.processBadElement(formstr, msg);
        }
    }

    // all good
    return true;
}

// disables all form buttons for 60 seconds, starting with the
//        passed one (helps ensure that a double click is stopped)
function disableFormButtons (button) {

    if ((BROWSER_NET) || (BROWSER_NET_SIX)) {
        window.captureEvents(Event.CLICK | Event.MOUSEDOWN | Event.MOUSEUP);
    } else {

        var formObj = eval(this.formLocation);
    
        // do this one right away
        if (button != "null") {
            button.disabled = true;
        }

        if (formObj != null) {
            for (var i=0; i < formObj.elements.length; i++) {
                var el = formObj.elements[i];
                if ((el.type == "submit") || (el.type == "reset")
                        || (el.type == "button")) {
                    el.disabled = true;
                }
            }
        }
    }
    
    // see 'ugh' below
    window.setTimeout('enableFormButtons()', 60000);
}

// netscape can't pass arguments in setTimeout() calls, so we must
//        create an enable method to call - ugh...
function enableFormButtons () {

    if ((BROWSER_NET) || (BROWSER_NET_SIX)) {
        window.releaseEvents(Event.CLICK | Event.MOUSEDOWN | Event.MOUSEUP);
    } else {
        var formObj = eval(this.formLocation);

           for (var i=0; i < formObj.elements.length; i++) {
            var el = formObj.elements[i];
            if ((el.type == "submit") || (el.type == "reset")
                        || (el.type == "button")) {
                el.disabled = false;
            }
        }
    }
}

// Wirtten by David Leppek of Pay By Touch Merchant Services as a free easy method of validating a credit card
function ccIsValidFormat (ccNumb) { //v2.0

    var valid 	= "0123456789"
    var len 	= ccNumb.length;
    var bNum 	= true;
    var iCCN 	= ccNumb;
    var sCCN 	= ccNumb.toString();
    var iCCN;
    var iTotal 	= 0;
    var bResult = false;
    var digit;
    var temp;
	iCCN = sCCN.replace (/^\s+|\s+$/g,'');	// strip spaces
    //alert(iCCN);
    for (var j=0; j<len; j++) {
    temp = "" + iCCN.substring(j, j+1);
    if (valid.indexOf(temp) == "-1") bNum = false;
    }
    if(!bNum){
        // MAY EXIT THIS BLOCK
	return false;
    }

        iCCN = parseInt(iCCN);
    	
    if(len == 0){ /* nothing, field is blank */ 
    	bResult = true;
    }else{
	if(len >= 15){		//15 or 16 for Amex or V/MC
		for(var i=len;i>0;i--){
		digit = "digit" + i;
		//alert(digit);
		
			calc = parseInt(iCCN) % 10;	//right most digit
			calc = parseInt(calc);
			//alert(calc);
			iTotal += calc;		//parseInt(cardnum.charAt(count))i:\t" + calc.toString() + " x 2 = " + (calc *2) +" : " + calc2 + "\n";
			// commented out below which wrote NONALTERED digit to page for demo only.
			//document.form1.textfield.value += "" + i + ":\t" + calc.toString() + " x 1 = " + calc + "\n";
			
			i--;
		digit = "digit" + i;
		//alert(digit);
		
			iCCN = iCCN / 10; 	// subtracts right most digit from ccNum
			calc = parseInt(iCCN) % 10 ;	// step 1 double every other digit
			 //alert( iCCN + " " + calc);
			 calc2 = calc *2;
			
			switch(calc2){
				case 10: calc2 = 1; break;	//5*2=10 & 1+0 = 1
				case 12: calc2 = 3; break;	//6*2=12 & 1+2 = 3
				case 14: calc2 = 5; break;	//7*2=14 & 1+4 = 5
				case 16: calc2 = 7; break;	//8*2=16 & 1+6 = 7
				case 18: calc2 = 9; break;	//9*2=18 & 1+8 = 9
				default: calc2 = calc2; 		//4*2= 8 &   8 = 8  -same for all lower numbers
			}
			iCCN = iCCN / 10; 	// subtracts right most digit from ccNum
			iTotal += calc2;
		}
		if ((iTotal%10)==0){
			bResult = true;
 		}else{
			bResult = false;
		}
	}
    }

    return bResult;
}
