//*************************************************************************
//                                                                        *
//  STANDARDIZE FORM VALIDATOR                                            *
//  Written by Brian K. Hargrove                                          *
//  Copyright 2008. All rights reserved.                                  *
//                                                                        *
//  To use this code contact brian_hargrove@yahoo.com for permission.     *
//                                                                        *
//                                                                        *
//  This is standardized way to validate datafileds on a HTML form.       *
//  All you have to do is call the validateDataFields function and        *
//  pass in the link to the form you want validated. The function         *
//  will validate the data based on the names of the fields. In           *
//  order for this to work you must adhere to the the following           *
//  field naming standards. If you don't it won't work properly.          *
//                                                                        *
//  _req - Ending of field that is required                               *
//  _num - Indicates numeric field                                        *
//  _num_min3 - Indicates a minimum value of 3 (or whatever number)       *
//  _num_max8 - Indicates a minimum value of 8 (or whatever number)       *
//  _email - Indicates an email field                                     *
//  _zip - indicates a zip code field                                     *
//                                                                        *
//  Set field attributes to check required min/max lengths of data        *
//  minlength='6' - Indicates data must be at least 6 characters long     *
//  maxlength='9' - Indicates data cant be longer than 9 characters long  *
//                                                                        *
//*************************************************************************

//************************************************************************************
// validateDataFields
//      Parameters: vForm   - id of form to validate
//                  fld     - id of element to display error messages
//                  pop_up_alert    - flag to show errors in a pop up dialog box
//                                    If true a message box will pop up with the errors listed, else error messages will be added to display messasge area ONLY
//
// This function checks to see if the required fields are populated and if they are
// if makes sure they have valid data
//************************************************************************************
function validateDataFields(vForm, fld, pop_up_alert) { // id of form to validate and id of element to display message in

    //alert('In validateDataFields: \nvForm:' + vForm.name + '\nfld:' + fld + '\npop_up_alert:' + pop_up_alert); // Use for testing
    
    // If no pop_up_alert value is sent then defaul to false
    if (typeof pop_up_alert == 'undefined' )
        arg2 = false;
        
    // Set some default values for variables
    var rtnMessage = '';
    var tempMsg = '';
    var carriageReturn = '\n'; // Used if pop_up_alert is true
    var firstFieldWithError = '';
    var notFound = true;
    var radioArray = new Array();
    var radioArrayIndex = -1;

    // Reset the display message area with no elements & hide it from the user
    removeAllChildElements(fld);
    HideContent(fld);

    // Loop through each elment within the form and validate each one
    for(var i = 0; i < vForm.elements.length; i++) {

        // Set(reset) values of variables for processing
        tempMsg = '';

        // Possible Values for vForm.elements[i].type == text, select-one, checkbox, radio, textarea, button
        if ( vForm.elements[i].type != 'radio' && (vForm.elements[i].type != 'button' || vForm.elements[i].type != 'submit') ) {  // If not a radio or button then validate

            if ( vForm.elements[i].name != "" ) { // Make sure the form element has valid name
        		// Set the color of field back to original color -- don't call this on every form element because some crash the javascript
    			setDefaultFieldColor( vForm.name, vForm.elements[i].name );
    	        tempMsg = validate(vForm.elements[i]);
            }
    	
        } else {

            if ( vForm.elements[i].type == 'radio' )  {  // If the element is radio button do a special check for dublicated named radio buttons

	    		// Set the color of field back to original color -- don't call this on every form element because some crash the javascript
				setDefaultFieldColor( vForm.name, vForm.elements[i].name );

                // Add to radio buttons to check
                var x=document.getElementsByName(vForm.elements[i].id);
                if (x.length>1) {

                    // Loop through radio buttons with duplicate name that are already validated to see if this one has been validated
                    for(var zz = 0; zz < radioArray.length; zz++) {
                        if ( radioArray[zz] == vForm.elements[i].id ) {
                            notFound = false;
                        }
                    }

                    // If the radio has not been validated then validate it, add it to the list, and reset not found
                    if ( notFound ){
                        tempMsg = validate(vForm.elements[i]);
                        radioArrayIndex++;
                        radioArray[radioArrayIndex] = vForm.elements[i].id;
                        notFound = true;
                    }

                } else {

                    // If the radio only has one element then validate it solo
                    tempMsg = validate(vForm.elements[i]);

                }
                
            } // end if type == radio
            
        }
        if ( tempMsg.length > 2 ) {

			// Set the error indicator on the field with the error
			setErrorIndicator( vForm.name, vForm.elements[i].name );
			
            // If this is the first error in the form then set the flag field and initiate the error message fld and rtnMessage
            if ( firstFieldWithError == '' ) {
                firstFieldWithError = vForm.elements[i].name;
                // Insert heading before the errors start...
                rtnMessage += "Please correct the following:" + carriageReturn;
                insertError("Please correct the following:", fld);
                ShowContent(fld); // If we have errors then you have to show the error display area
            }
            
            // 2 things to do here: 1. Add to the string containing all errors; 2. Insert the error in the fld
            rtnMessage += tempMsg + carriageReturn;
            insertError(tempMsg, fld);
        }
   	}
   	// If errors then return false
   	if (rtnMessage.length>2){
         if (pop_up_alert) { alert(rtnMessage); } // If pop_up_alert flag is true then show alert dialog box with errors in rtnMessage variable
         return false
    } else {
        return true;
    }
}

//************************************************************************************
// insertError
//      Parameters: errorText   - text of the error message
//                  fld         - id of element to display error messages
//
// This function inserts the message passed in into a new <div> and adds the new
// <div> to the end of the error message element (fld)
//************************************************************************************
function insertError(errorText, fld) {

    //alert(errorText + ' -- ' + fld);
    
    // create an empty element node without an ID, any attributes, or any content
    var sp1 = document.createElement("div");

    // give it an id attribute called 'newSpan'
    sp1.setAttribute("id", "newErrorSpan");

    // create some content for the newly created element.
    var sp1_content = document.createTextNode(errorText);

    // apply that content to the new element
    sp1.appendChild(sp1_content);
    
    // Get the parentDiv
    var parentDiv = document.getElementById(fld);
    
    // Get the only child of this parent
    var sp2 = null; //parentDiv.lastChild;
    
    // insert the new element into the DOM before sp2
    parentDiv.insertBefore(sp1, sp2);
}

//************************************************************************************
// removeAllChildElements
//      Parameters: fld   - id of element to display error messages
//
// This function removes all the child nodes in the error message element (fld)
//************************************************************************************
function removeAllChildElements(fld) {
    var testNode=document.getElementById(fld);
    x=testNode.childNodes;
    numNodes = x.length;
    for (i=0;i<numNodes;i++) {
        testNode.removeChild(testNode.firstChild);
    }
}

//************************************************************************************
// validate
//      Parameters: valfield   - id of element to validate
//
// This function peforms validation on the field (valfield) passed in.
// It checks for required fields, numeric, max and min values, email address, and zip
// The following naming standard should be followed:
//  _req - Ending of field that is required
//  _num - Indicates numeric field
//  _num_min3 - Indicates a minimum value of 3 (whatever number)
//  _num_max8 - Indicates a minimum value of 8 (whatever number)
//  _email - Indicates an email field
//  _zip - indicates a zip code field
//************************************************************************************
function validate (valfield) {    // element to be validated

    var rtnMesg = '';
    var carriageReturn = '';
    // Start by removing any spaces on each end
    valfield.value = trim(valfield.value);

    // If this is an required field make sure it has a value / or selection if a select option, checkbox, or radio
    var required_reg_exp = /_req/;
    if ( required_reg_exp.test( valfield.name ) ) {

        if ( valfield.value == "" ) {
            rtnMesg = carriageReturn + 'Data required for: ' + valfield.id;
            return rtnMesg;
        }

        // If this is an check box field and required then make sure it is checked
        if ( valfield.type == 'checkbox' ) {
            // Get a collection of elements with the given name (could be 1 or many in collection)
            checkboxElement = document.getElementsByName(valfield.name);
            // Call NoneWithCheck to see if any of them are checked
            if ( NoneWithCheck( checkboxElement) ) { 
                rtnMesg = carriageReturn + 'You must check the ' + valfield.id + ' field';
                return rtnMesg;
            }
        }

        // If this is an radio button field and required then make sure it is checked
        if ( valfield.type == 'radio' ) {
            var x=document.getElementsByName(valfield.name) // * Must get array of the elementS by NAME on a radio button
            found_one = false;
            for(i=0;i<=x.length-1;i++){
            	if(x[i].checked) {
                    found_one = true;
                }
            }
            if (! found_one) {
                rtnMesg = carriageReturn + 'Select a option for the ' + valfield.id + " field";
                return rtnMesg;
            }
            
        }

/*        // If this is an select drop down field ************************************************************************* TODO
        if ( valfield.type == 'select-option' ) {
            if( WithoutSelectionValue( ) )
                rtnMesg = carriageReturn + 'Please select a option for the ' + valfield.id + ' field';
                return rtnMesg;
            }
        }*/
    }

    // If this is an email address field and it has a value then validate that value
    var email_reg_exp = /_email/;
    if ( ( email_reg_exp.test( valfield.name ) ) && ! ( valfield.value == "" ) ) {
        if ( ! validEmailField(valfield) ) {
            rtnMesg = carriageReturn + 'The email address provided is not valid: ' + valfield.id;
        } 
    }

    // Zip code validation
    var zip_reg_exp = /_zip/;
    if ( ( zip_reg_exp.test( valfield.name ) ) && ! ( valfield.value == "" ) ) {
        if ( ! validZip(valfield) ) {
            rtnMesg = carriageReturn + valfield.id + ' must be 5 numbers in length: ';
        }
    }

    // If this is a numeric field and it has a value then validate that value
    var numeric_reg_exp = /_num/;
    if ( ( numeric_reg_exp.test( valfield.name ) ) && ! ( valfield.value == "" ) ) {

        if ( ! IsNumeric(valfield.value) ) {

            rtnMesg = carriageReturn + 'Invalid numeric field: ' + valfield.id;
            
        } else {
        
            // Min value validation
            var min_reg_exp = /_min/;
            if ( ( min_reg_exp.test( valfield.name ) ) && ! ( valfield.value == "" ) ) {

                var tmin_value, temp_val, next_scor;
                tmin_value = valfield.name.indexOf('_min')+4;
                temp_val = valfield.name.substring(tmin_value); // This truncates the first part of the variable name up to _min
                min_value = temp_val;
                next_scor = temp_val.indexOf('_');

                // check to see if the standard was used in the middle of the variable name
                if (temp_val.indexOf('_') > 0 ) {
                      min_value = temp_val.substring(0,next_scor);
                }
                
                if ( parseInt(valfield.value) < parseInt(min_value) ) {
                    rtnMesg = carriageReturn + 'Value for ' + valfield.id + ' (' + valfield.value + ') must be at least ' + min_value + '.';
                }
                
            } // end of min value validation

            // Max value validation
            var max_reg_exp = /_max/;
            if ( ( max_reg_exp.test( valfield.name ) ) && ! ( valfield.value == "" ) ) {

                var tmax_value, temp_val, next_scor;
                tmax_value = valfield.name.indexOf('_max')+4;
                temp_val = valfield.name.substring(tmax_value); // This truncates the first part of the variable name up to _min
                max_value = temp_val;
                next_scor = temp_val.indexOf('_');
                
                // check to see if the standard was used in the middle of the variable name
                if (temp_val.indexOf('_') > 0 ) {
                      max_value = temp_val.substring(0,next_scor);
                }
                
                if ( parseInt(valfield.value) > parseInt(max_value) ) {
                    rtnMesg = carriageReturn + 'Value for ' + valfield.id + ' (' + valfield.value + ') cannot be greater than ' + max_value + '.';
                }
                
            } // end of max value validation

        } // end if ( ! IsNumeric(valfield.value))
        
    } // end of numeric field testing

	// Test the Lenght of the field (minLength and maxLength)
	// and report if under or over
	if ( maxLength(valfield) )
        rtnMesg = carriageReturn + 'Value for ' + valfield.id + ' is too long and must be no longer than ' + valfield.getAttribute("maxlength") + ' characters.';
	if ( minLength(valfield) )
        rtnMesg = carriageReturn + 'Value for ' + valfield.id + ' is not long enough and must be at least ' + valfield.getAttribute("minlength") + ' characters.';

    return rtnMesg;
}

//************************************************************************************
// validEmailField
//      Parameters: valfield   - id of element to validate
//
// The function validates email address fields are in the correct format
//************************************************************************************
function validEmailField(valfield) {
    // local part must not exceed 64 characters and the domain part cannot to be longer than 64 or 255 (?) characters.
    // An email address can be 319+.com+@=324 characters long at most.
    if ( (valfield.value.length>324) || (valfield.value.length<1) ){
      return false;
    }
    var filter=/^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i
    // eregi('^[a-zA-Z0-9._-]+@[a-zA-Z0-9-]+\.[a-zA-Z.]{2,5}$', $email)
	if (filter.test(valfield.value)) {
        return true;
    } else{
        return false;
    }
}

//************************************************************************************
// validZip
//      Parameters: valfield   - id of element to validate
//
// The function validates zip code fields are in the correct format
//************************************************************************************
function validZip(valfield) {
	//alert( 'validZip? - |' + valfield.value + '|  - ' + valfield.value.length );
    if ( ! IsNumeric( trim(valfield.value) ) ) { // First the value has to be numeric
        return false;
    } else {
 	    //alert('data is numeric');
        // Next check length is either 5 or 9
        if ( ! ( valfield.value.length == 5 )  ) {
            return false;
        } else {
		  	return true;
		    //alert('length is ok');
		}
        //if ( ! ( valfield.value.length == 9 ) ) {
        //    return false;
        //}
    }
    return true;
}

//************************************************************************************
// IsNumeric
//      Parameters: strString   - value to check
//
// The function validates if the value is numeric
//************************************************************************************
function IsNumeric(strString) {
    return isFinite( (strString * 1.0) );
}

//************************************************************************************
// WithoutContent
//      Parameters: ss   - control to check
//
// check if the text, textarea, password, or file fields has no content.
//************************************************************************************
function WithoutContent(ss) {
    if(ss.length > 0) { return false; }
    return true;
}

//************************************************************************************
// NoneWithContent
//      Parameters: ss   - control to check
//
// check if none of the set of text, textarea, password, or file fields have content.
// (Set: More than one with the same field name.)
//************************************************************************************
function NoneWithContent(ss) {
    for(var i = 0; i < ss.length; i++) {
    	if(ss[i].value.length > 0) { return false; }
   	}
    return true;
} 

//************************************************************************************
// NoneWithCheck
//      Parameters: ss   - control to check
//
// check if none of the set of radio buttons or checkboxes are checked.
// (Set: More than one with the same field name.)
//************************************************************************************
function NoneWithCheck(ss) {
    for(var i = 0; i < ss.length; i++) {
    	if(ss[i].checked) { return false; }
   	}
    return true;
} 

//************************************************************************************
// WithoutCheck
//      Parameters: ss   - control to check
//
// check if the single radio button or checkbox is unchecked.
//************************************************************************************
function WithoutCheck(ss) {
    //alert(ss + ' ' + ss.checked);
    if(ss.checked) { return false; }
    return true;
} 

//************************************************************************************
// WithoutSelectionValue
//      Parameters: ss   - control to check
//
// check if selected drop-down list or select box entries have no value.
//************************************************************************************
function WithoutSelectionValue(ss) {
    for(var i = 0; i < ss.length; i++) {
    	if(ss[i].selected) {
    		if(ss[i].value.length) { return false; }
   		}
   	}
    return true;
} 

//************************************************************************************
// trim
//      Parameters: str   - string to trim
//
// This function trims spaces of the ends of the string
//************************************************************************************
function trim(str) {
    return str.replace(/^\s+|\s+$/g, '');
}

//************************************************************************************
// These functions hide and unhide sections/division of the html page
//************************************************************************************
function HideContent(d) {
    if(d.length < 1) { return; }
    document.getElementById(d).style.display = "none";
}
function ShowContent(d) {
    if(d.length < 1) { return; }
    document.getElementById(d).style.display = "block";
}
function ReverseContentDisplay(d) {
    if(d.length < 1) { return; }
    if(document.getElementById(d).style.display == "none") { document.getElementById(d).style.display = "block"; }
    else { document.getElementById(d).style.display = "none"; }
}

//************************************************************************************
// This function shows the innerHTML of the DOM and is primarily used for debugging
//************************************************************************************
function showInnerHTML(fld) {
    var testNode=document.getElementById(fld);
    alert('Field to show: ' +fld + '\n' + testNode.innerHTML );
}

//************************************************************************************
// numbersonly
//      Parameters: e   - event
//
// This disables keys that are not numeric but should allow tab, etc.
//************************************************************************************
function numbersonly(e) {
    var unicode=e.charCode? e.charCode : e.keyCode
    //if (charCode < 32 || (charCode >= 48 && charCode <= 57))
    if ( unicode < 32 ) {
      return true;
    }
    if ( unicode!=8 && unicode!=37 && unicode!=38 && unicode!=39 && unicode!=40 && unicode!=46 && unicode!=114 ) {
    //if the key isn't the backspace(8), arrow left(37), right(38), up(39), down(40), delete(46), or ctrl-R(114) keys which we should allowed then check if it is a number...
        if ( unicode<48 || unicode>57 ) //if not a number
            return false //disable key press
    }
}

//************************************************************************************
// autotab
//      Parameters: original - orignal field
//                  destination - destination field
//
// This sets the focus to the next field
//************************************************************************************
function autotab(original,destination){
    if (original.getAttribute&&original.value.length==original.getAttribute("maxlength"))
        destination.focus()
// javascript:AutoNext(event) ****************** look at code in TextBoxAutoTabbing.js
}

//************************************************************************************
// compareFields
//      Parameters: display_msg - message to display
//                  display_fld - area to diplay the message
//                  fld1 - 1st field to compare
//                  fld2 - 2nd field to compare
//
// This function compares the value of 2 fields and displays an error if the are not the same
//************************************************************************************
function compareFields(display_msg, display_fld, fld1, fld2 ) { // id of form to validate and id of element to display message in
    //alert('compareFields:\n vForm.name' + '\nfld1 - ' + fld1 + '\nfld2 - ' + fld2);
    var x=document.getElementsByName(fld1);
    var y=document.getElementsByName(fld2);
    //alert(x.length);
    //alert(y.length);
    if( x[0].value != y[0].value ) {
        insertError(display_msg, display_fld);
        ShowContent(display_fld); // If we have errors then you have to show the error display area
        return false;
    }
    return true;
}

//************************************************************************************
// maxlength
//      Parameters: obj - field to test
//
// This function tests to see if the max length of user input field has been reached on
// a particular field (obj) and returns true or false
//************************************************************************************
function maxLength(obj) {
	var mlength=obj.getAttribute? parseInt(obj.getAttribute("maxlength")) : ""
	if (obj.getAttribute && obj.value.length>mlength) {
		//alert('too long');
		return true;
	}
	//alert('max ok ' + obj.name);
	//alert(obj.getAttribute("maxlength"));
	//+ ' -- ' + obj.getAttribute("maxlength") +' -- max ok');
	return false;
}

//************************************************************************************
// minlength
//      Parameters: obj - field to test
//
// This function tests to see if the min length of user input field has been reached on
// a particular field (obj) and returns true or false
//************************************************************************************
function minLength(obj) {
	var mlength=obj.getAttribute? parseInt(obj.getAttribute("minlength")) : ""
	if (obj.getAttribute && obj.value.length<mlength) {
		return true;
	}
	return false;
}

//************************************************************************************
// ismaxlength
//      Parameters: obj - field to test
//
// This function test to see if the max length of user input has been reached on
// a particular field and if it has it stops the user from entering any more data
//************************************************************************************
function ismaxlength(obj) {
	//Get the max length from the object
	var mlength=obj.getAttribute? parseInt(obj.getAttribute("maxlength")) : "";
	// If max has be reached then truncate the extra data
	if ( maxLength(obj) )
		obj.value=obj.value.substring(0,mlength); // truncate extra data
}

//************************************************************************************
// typeCounter
//      Parameters: fld - message to display
//                  max - area to diplay the message
//                  countFld - 1st field to compare
//
// This function displays to the user the remaining characters allows or a message
// that the maximum length has been reached
//************************************************************************************
function typeCounter(fld,max,countFld) {
	var strTemp = "";
	if (fld.value.length > max) {  // Max was hit
		fld.value = fld.value.substring(0, max); // Reduce the length to the max
		msg = "You have reached the maximum " + max + " characters";
		document.getElementById(countFld).innerHTML = msg;
	} else {
		document.getElementById(countFld).innerHTML = (max - fld.value.length) + " characters remaining";
	}
}

//************************************************************************************
// echoUnicode
//      Parameters: e - event
//
// This function echos out the unicode value of the character typed for testing purposes
//***************************************************************************************
function echoUnicode(e){
    var unicode=e.charCode? e.charCode : e.keyCode
    alert ('The unicode value of the key just pressed is: ' + unicode );
}

//************************************************************************************
// setDefaultFieldColor
//      Parameters: frm - form the fld is on
//                  fld - fld to change the color
//
// This function changes the color of the field to the original color
//************************************************************************************
function setDefaultFieldColor( frm, fld ) {
	var colr = '#3A709E';
	var borderSize = '1';
	setFieldColor( frm, fld, colr, borderSize );
}

//************************************************************************************
// setErrorIndicator
//      Parameters: frm - form the fld is on
//                  fld - fld to change the color
//
// This function changes the color of the field were an error has occured
//************************************************************************************
function setErrorIndicator( frm, fld ) {
	var colr = '#d11414';
	var borderSize = '2';
	setFieldColor( frm, fld, colr, borderSize );
}

//************************************************************************************
// setFieldColor
//      Parameters: frm - form the fld is on
//                  fld - fld to change the color
//                  colr - color to change to
//
// This function changes the color of the field
//************************************************************************************
function setFieldColor( frm, fld, colr, borderSize ) {
	if (typeof colr == "undefined") { colr = "red"; } // Set default color to red if not set
	var len = document.forms[ frm ].elements[ fld ].length + "";
	if ( document.forms[ frm ].elements[ fld ].type == "select-one" ) {
		//document.forms[ frm ].elements[ fld ].style.border = "1px solid " + colr;
		document.forms[ frm ].elements[ fld ].style.color = "white";
		document.forms[ frm ].elements[ fld ].style.backgroundColor = colr;
	} else {
		if ( len != "undefined" ) {
		    for(var i = 0; i < len; i++) {
				document.forms[ frm ].elements[ fld ][i].style.border = borderSize + "px solid " + colr;
			}
		} else {
			document.forms[ frm ].elements[ fld ].style.border = borderSize + "px solid " + colr;
		}
	}
}

//************************************************************************************
// setErrorOnAllErrorFields
//      Parameters: frm - form the fld is on
//                  fld - fld to change the color
//
// This function changes the color of the all fields with errors to red
//************************************************************************************
function setErrorOnAllErrorFields( frm, fld ) {
//	alert('set all the fields with error to red');
	// Set the error indicator on the field with the error
	//setErrorIndicator( vForm.name, vForm.elements[i].name );
}


//	var x = document.getElementsByName( fld );
//  var x = document.getElementById( fld );
//************************************************************************************
// TEST CODE BELOW
/* removed from typeCounter function... not needed or at least it doesn't appear to be needed now
		strTemp = fld.value.substring(0, max); // Reduce the length to the max
		fld.value = strTemp;
		if (fld.value.length > max) {
			strTemp = fld.value.substring(0, (max - 1));
			fld.value = strTemp;
		} */
		


//************************************************************************************
// processForm
//      Parameters: vForm - form to validate
//                  vErrorField - Area to display errors
//
//************************************************************************************
function processForm(vForm,vErrorField){
    if ( validateDataFields(vForm,vErrorField) ) {
        return true;
    } else {
        return false;
    }
}


//************************************************************************************
// checkEnter
//      Parameters: e - event
//                  frm - form the fld is on
//
// This function forcing submission of a form when the user hits the ENTER key
//************************************************************************************
function checkEnter(e, frm){
    var characterCode
         if(e && e.which){
             e = e;
             characterCode = e.which;
         } else{
             e = event;
             characterCode = e.keyCode;
         }
         if(characterCode == 13){
             //document.forms[0].submit()
             document.frm.submit();
             return false;
         }
    return true;
}


/*
//function for forcing form submission
function checkEnter(e){
    var characterCode
         if(e && e.which){
             e = e
             characterCode = e.which
         } else{
             e = event
             characterCode = e.keyCode
         }
         if(characterCode == 13){
             document.forms[0].submit()
             return false
         }
    return true
}
*/
