/**
 * Finds and focuses on the first invalid field.
 */
function identifyInvalidField(passedForm) {
  var msgFields = passedForm.getElementsByTagName("span");
  var inputFields = passedForm.getElementsByTagName("input");
  var i = 0;
  var j = 0;
  var badFieldFound = false;
  var badInputAreaFound = false;
  var mainFieldName = "";

  while (!badFieldFound && i < msgFields.length) {
    if (msgFields[i].className == "error" && msgFields[i].style.visibility == "visible") {
      badFieldFound = true;
      mainFieldName = msgFields[i].id.substr(0, msgFields[i].id.length - 6);
      while (!badInputAreaFound && j < inputFields.length) {
        if (inputFields[j].id.indexOf(mainFieldName) >= 0) {
          badInputAreaFound = true;
          inputFields[j].focus();
        } else {
          j++;
        } // end if
      } // end while
    } else {
      i++;
    }// end if
  } // end while
} // end identifyInvalidField
 
/*
 *  elementDisplay class
 *
 *  Tracks which elements/groups' warnings to display
 */
function elementDisplay(displayID, valid, message) {
    this.id      = (displayID != null && displayID != 'null') ? displayID : 'default';
    this.isValid = (valid != null && valid != 'null') ? valid : false;
    this.text    = (message != null && message != 'null') ? message : 'Required';
}


/*
 *  formValidator class
 *
 *  Re-writing code to use an object-oriented validator
 */
function formValidator(frm) {
    
    // Reference to form object
    this.form   = frm;
    
    // Disabling validation allows immediate form submission
    this.enableValidation = true;
    
    // Track show/hide status of elements and element groups
    this.showWarnings = new Array();
    this.hideWarnings = new Array();
    this.passwordVals = new Array();
    
    // Store regular expressions
    this.regExp = new Array();
    this.regExp['email'] = /^\w(\.?-?\w)*@\w(\.?[-\w])*\.[a-z]{2,4}$/i;
    
    // Store references to elements
    this.reqElements = new Array();
    this.groups      = new Array();
    this.warnings    = new Array();
    this.radioRefs   = new Array();
    this.radioCount  = new Array();
    this.checkRefs   = new Array();
    this.checkCount  = new Array();
    
    // Loop through all form elements
    for(i=0;i<frm.elements.length;i++) {
        
        element = frm.elements[i];
        info    = this.getInfo(element);
        // Collect info on, and reference to, all required elements
        if(info['req'] && !(element.type == 'hidden' || element.type == 'button')) {
            
            // Handle radio buttons, must create an array of references to each button
            if(info['fieldType'] == 'radio') {
                
                if(this.radioRefs[info['name']] == null || this.radioRefs[info['name']] == 'null') {
                   this.radioRefs[info['name']]  = new Array();
                   this.radioCount[info['name']] = 0;
                }
                
                this.radioRefs[info['name']][this.radioCount[info['name']]] = element;
                this.radioCount[info['name']]++;
                
                info['ref'] = this.radioRefs[info['name']];
                
            } else if(info['fieldType'] == 'checkbox') {
                if(info['group'] == null) {
                    checkName = info['name'];
                } else {
                    checkName = info['group']
                }
                
                if(this.checkRefs[checkName] == null || this.checkRefs[checkName] == 'null') {
                   this.checkRefs[checkName]  = new Array();
                   this.checkCount[checkName] = 0;
                }
                
                this.checkRefs[checkName][this.checkCount[checkName]] = element;
                this.checkCount[checkName]++;
                
                info['ref'] = this.checkRefs[checkName];
                
            }
            
            this.reqElements[info['name']] = info;
            
            // Collect element references by group
            if(info['group'] !== null && info['groupNum'] !== null) {
                
                // Create/add to group array
                if(this.groups[info['group']] == null) {
                    
                    this.groups[info['group']] = new Array();
                    this.groups[info['group']]['checked'] = false;
                }
                
                this.groups[info['group']][info['groupNum']] = info;
            
            }
        }
        
    
    } // end for()
    
}

/*
 *  formValidator.addValidMessage() method
 */
formValidator.prototype.addValidMessage = function(name) {
    this.hideWarnings[this.hideWarnings.length] = name + "_error";
}


/*
 *  formValidator.addInvalidMessage() method
 */
formValidator.prototype.addInvalidMessage = function(name) {
    this.showWarnings[this.showWarnings.length] = name + "_error";
}


/*
 *  Element name interpreter
 *
 *  Assumes element is formatted 'required_infoType_varName'
 *
 *  required = Is this required for validation (1 or 0)
 *  infoType = What type of information this contains (NOT form type)
 *  varName  = Actual variable name (either renamed to this, or interpreted by form handler)
 *
 */
formValidator.prototype.getInfo = function(element) {
    out = new Array();
    
    parts = element.name.split('_');
    // If only one part, the proper naming convention was not used
    
    if((!parts.length || parts.length < 3) 
      || element.type == 'hidden' 
      || element.type == 'button') {
        out['req']    = false;
        out['reqAll'] = false;
        out['type']   = 'default';
        out['name']   = 'default';
    } else {
        out['req']    = (parts[0] == 1 || parts[0] == 2);
        out['reqAll'] = (parts[0] == 2);
        out['type']   = parts[1];
        out['name']   = parts[2];
    }
    
    
    // *** DOES NOT WORK PROPERLY -- CANNOT HANDLE INPUT NAMES WITH UNDERSCORES ***
    // Allow for var names using '_'
    if(parts.length > 3) {
        for(i = 3; i < parts.length; i++) {
            out['name'] += '_' + parts[i];
        }
    }
    
    out['ref'] = element;
    out['fieldType'] = element.type;
    
    // Check last two characters; if numeric, assume element is part of a group
    pwNum = out['name'].charAt(out['name'].length-1);
    
    if(!isNaN(pwNum)) {
        
        // Check for second character
        preNum = out['name'].charAt(out['name'].length-2);
        if(!isNaN(preNum)) {
            pwNum = Number(String(preNum) + String(pwNum));
        }
        
        // Get all but last character of variable name (used as group key)
        pwName = out['name'].substring(0, out['name'].length-1);
        
        out['group']    = pwName;
        out['groupNum'] = pwNum;
        
    } else if(element.type == 'checkbox'){
        out['group']    = out['name'];
        
        if(out['groupNum'] == null) {
            out['groupNum'] = 0;
        } else {
            out['groupNum']++;
        }
        
    } else {
        out['group']    = null;
        out['groupNum'] = null;
    }
    
    return out;
}


/*
 *  formValidator.enable() method
 */
formValidator.prototype.enable = function() {
    this.enable = true;
}


/*
 *  formValidator.disable() method
 */
formValidator.prototype.disable = function() {
    this.enable = false;
}


/*
 *  formValidator.submit() method
 */
formValidator.prototype.submit = function() {
    if(!this.enableValidation) {
        this.prepareAndSubmit();
        
        // Add formContents field if mailman is active
        if (this.form.action.indexOf("/mailman") >= 0) {
          this.constructContentsInput()
        }
        
        return true;
    }
    
    // Reset warning tracking
    this.showWarnings = new Array();
    this.hideWarnings = new Array();
    this.passwordVals = new Array();
    
    // Reset groups' 'checked' status to allow re-checking
    for(groupID in this.groups) {
        this.groups[groupID].checked = false;
    }
    
    // Loop through all required fields
    for(key in this.reqElements) {
        info = this.reqElements[key];
        status = null;
        
        handlerFunction = this.getMethodByType(info);
        
        if(handlerFunction != 'unknown') {
            this.groupChecker(info);
        }
        
        
    } // end for()
    
    
    // If no errors were encountered, submit
    if(!this.showWarnings.length) {
        this.prepareAndSubmit();
        
        // Add formContents field if mailman is active
        if (this.form.action.indexOf("/mailman") >= 0) {
          this.constructContentsInput()
        }
        
        return true;
    
    // If errors exist, update display
    } else {
        
        // Display warnings
        for(i=0;i<this.showWarnings.length;i++) {
            this.changeVisibility(this.showWarnings[i], 'visible');
        }
        
        // Hide warnings
        for(i=0;i<this.hideWarnings.length;i++) {
            this.changeVisibility(this.hideWarnings[i], 'hidden');
        }
        return false;
    }
}

/*
 *  formValidator.getMethodByType() method
 *
 *  Returns name of function that validates a particular type
 */
formValidator.prototype.getMethodByType = function(info) {
    switch(info['fieldType']) {
        case 'text':
            return 'validateText';
            break;
        
        case 'password':
            return 'validatePassword';
            break;
        
        case 'textarea':
            return 'validateTextarea';
            break;
        
        case 'select-one':
            return 'validateSelect';
            break;
        
        case 'select-multiple':
            return 'validateSelect';
            break;
        
        case 'checkbox':
            return 'validateCheckbox';
            break;
        
        case 'radio':
            return 'validateRadiobutton';
            break;
        
        case 'file':
            return 'validateFileUpload';
            break;
        
        default:
            return 'unknown';
            break;
    }
}


/*
 *  formValidator.groupChecker() method
 *
 *  Using this method eliminates the need to put 
 *  this code in each type validation method
 */
formValidator.prototype.groupChecker = function(info) {
    
    // Check for group membership
    if(info['group'] !== null) {
        groupID    = info['group'];
        group      = this.groups[groupID];
        requireAll = info['reqAll'];
        
        // If group has already been processed, do nothing
        if(group.checked) {
            return null;
            
        // If group has not been processed, test all elements
        } else {
            
            // Groups are a convenient way to check for password value agreement
            isPassword = (info['ref'].type == 'password');
            
            oneIsValid   = false;
            results      = new Array();
            
            // Loop through group
            for(key in group) {
                result = null;
                
                // Ignore 'checked' property in this loop
                if(key != 'checked') {
                    currItem = group[key];
                    
                    functionName = this.getMethodByType(currItem);
                    
                    // Check for password agreement
                    if(isPassword) {
                        if(this.passwordVals[groupID] == null) {
                            this.passwordVals[groupID] = currItem['ref'].value;
                        }
                        passwordMismatch = (this.passwordVals[groupID] != currItem['ref'].value);
                        
                    } else {
                        passwordMismatch = false;
                    }
                    
                    eval("result = this." + functionName + "(currItem);");
                    
                    if(result == false) {
                        results[results.length] = true;
                    } else {
                        oneIsValid = true;
                        if(passwordMismatch) results[results.length] = true;
                    }
                    
                } // end if(key != 'checked')
            } // end for(key in group) 
            
            
            // Prevent re-checking any additional group members (should all be validated)
            this.groups[info['group']].checked = true;
            
            if(!results.length || (!isPassword && !requireAll && oneIsValid)) {
                this.addValidMessage(groupID);
                return true;
            } else {
                this.addInvalidMessage(groupID);
                return false;
            }
            return out;
        }
    
    // If not a group member, process normally
    } else {
        functionName = this.getMethodByType(info);
        eval("result = this." + functionName + "(info);");
        return result;
    }
}



/************************ TEXT ************************
 *  formValidator.validateText() method
 */
formValidator.prototype.validateText = function(info) {
    value = info['ref'].value;
    text  = null;
    
    // Validate by infoType
    switch(info['type']) {
        case 'text':
            if(value == '') text = 'Year';
            break;
        
        case 'email':
            if(value == '' || !(this.regExp['email'].test(value))) text = 'Email';
            break;
        
        case 'numeric':
            if(value == '' || isNaN(value)) text = 'Number';
            break;
        
        case 'time':
            if(value == '' || value == '0' || value == '00:00') text = 'Time';
            break;
        
        case 'firstName':
            if(value == '') text = 'First Name';
            break;
        
        case 'lastName':
            if(value == '') text = 'Last Name';
            break;
        
        case 'phoneNumber':
            if(value == '') text = 'Phone Number';
            break;
        
        case 'faxNumber':
            if(value == '') text = 'Fax Number';
            break;
            
        case 'dateMonth':
            if(value == '') text = 'Month';
            break;
        
        case 'dateDay':
            if(value == '') text = 'Day';
            break;
        
        case 'dateYear':
            if(value == '') text = 'Year';
            break;
            
        default:
            if(value == '') text = 'Text';
            break;
    }
    
    if(text == null) {
        this.addValidMessage(info['name']);
        return true;
    } else {
        this.addInvalidMessage(info['name']);
        return false;
    }
}



/************************ TEXTAREA ************************
 *  formValidator.validateTextarea() method
 */
formValidator.prototype.validateTextarea = function(info) {
    value = info['ref'].value;
    text  = null;
    
    // Validate by infoType
    switch(info['type']) {
        case 'textarea':
            if(value == '') text = 'Text Area';
            break;
        
        case 'description':
            if(value == '') text = 'Description';
            break;
        
        default:
            if(value == '') text = 'Text';
            break;
    }
    
    if(text == null) {
        this.addValidMessage(info['name']);
        return true;
    } else {
        this.addInvalidMessage(info['name']);
        return false;
    }
}



/************************ PASSWORD ************************
 *  formValidator.validatePassword() method
 */
formValidator.prototype.validatePassword = function(info) {
    value = info['ref'].value;
    text  = null;
    
    // Validate by infoType
    switch(info['type']) {
        case 'password':
            if(value == '') text = 'Password';
            break;
        
        case 'passwordConfirm':
            if(value == '') text = 'Password Confirmation';
            break;
                
        default:
            if(value == '') text = 'Password';
            break;
    }
    
    if(text == null) {
        this.addValidMessage(info['name']);
        return true;
    } else {
        this.addInvalidMessage(info['name']);
        return false;
    }
}



/************************ SELECT ************************
 *  formValidator.validateSelect() method
 */
formValidator.prototype.validateSelect = function(info) {
    index = info['ref'].selectedIndex;
    text  = null;
    
    // Validate by infoType
    switch(info['type']) {
        case 'select':
            if(index == 0 || index == -1) text = 'List';
            break;
        
        case 'list':
            if(index == 0 || index == -1) text = 'List';
            break;
        
        case 'state':
            if(index == 0 || index == -1) text = 'State';
            break;
        
        case 'country':
            if(index == 0 || index == -1) text = 'Country';
            break;
        
        case 'dateMonth':
            if(index == 0 || index == -1) text = 'Month';
            break;
        
        case 'dateDay':
            if(index == 0 || index == -1) text = 'Day';
            break;
        
        case 'dateYear':
            if(index == 0 || index == -1) text = 'Year';
            break;
        
        default:
            if(index == 0 || index == -1) text = 'List';
            break;
    }
    
    if(text == null) {
        this.addValidMessage(info['name']);
        return true;
    } else {
        this.addInvalidMessage(info['name']);
        return false;
    }
}



/************************ CHECKBOX ************************
 *  formValidator.validateCheckbox() method
 */
formValidator.prototype.validateCheckbox = function(info) {
    checkboxes = info['ref'];
    
    numChecked = 0;
    for(i=0;i<checkboxes.length;i++) {
        if(checkboxes[i].checked) {
            numChecked++;
        }
    }
    
    text  = null;
    
    // Validate by infoType
    switch(info['type']) {
        case 'checkbox':
            text = 'Checkbox';
            break;
        
        case 'agreement':
            text = 'Agreement';
            break;
        
        case 'acknowledgement':
            text = 'Acknowledgement';
            break;
                
        default:
            text = 'Checkbox';
            break;
    }
    
    if((!info['reqAll'] && info['req'] && numChecked) || (info['reqAll'] && (numChecked == checkboxes.length))) {
        if(checkboxes.length == 1) this.addValidMessage(info['name']);
        return true;
    } else {
        if(checkboxes.length == 1) this.addInvalidMessage(info['name']);
        return false;
    }
}



/************************ RADIO BUTTONS ************************
 *  formValidator.validateRadiobutton() method
 */
formValidator.prototype.validateRadiobutton = function(info) {
    buttons = info['ref'];
    text    = null;
    
    buttonSelected = false;
    
    for(i=0;i<buttons.length;i++) {
        if(buttons[i].checked) buttonSelected = true;
    }
    
    // Validate by infoType
    switch(info['type']) {
        case 'radio':
            text = 'Radio Button';
            break;
        
        case 'radiobutton':
            text = 'Radio Button';
            break;
        
        case 'loginOption':
            text = 'Login Option';
            break;
        
        case 'ampm':
            text = 'Radio Button';
            break;
        
        default:
            text = 'Options';
            break;
    }
    
    if(buttonSelected) {
        this.addValidMessage(info['name']);
        return true;
    } else {
        this.addInvalidMessage(info['name']);
        return false;
    }
}



/************************ FILE ************************
 *  formValidator.validateFileUpload() method
 */
formValidator.prototype.validateFileUpload = function(info) {
    value = info['ref'].value;
    text  = null;
    
    // Validate by infoType
    switch(info['type']) {
        case 'file':
            if(value == '') text = 'File';
            break;
        
        case 'doc':
            if(value == '') text = 'Document';
            break;
        
        case 'msword':
            if(value == '') text = 'MS-Word Document';
            break;
        
        case 'graphic':
            if(value == '') text = 'Graphic';
            break;
        
        default:
            if(value == '') text = 'File';
            break;
    }
    
    if(text == null) {
        this.addValidMessage(info['name']);
        return true;
    } else {
        this.addInvalidMessage(info['name']);
        return false;
    }
}



/*
 *  formValidator.changeVisibility
 *  Change visibility of specified element
 */
formValidator.prototype.changeVisibility = function(tagName, visibility) {
    doc = window.document;
    
    if (doc.layers){
	    if(doc[tagName] != null) doc[tagName].visibility = visibility;
	} else if (doc.all) {
	    if(doc.all[tagName] != null) doc.all[tagName].style.visibility = visibility;
	} else {
	    if(doc.getElementById(tagName) != null) doc.getElementById(tagName).style.visibility = visibility;
	}
}



/*
 *  Renames all elements appropriately and submits
 *
 *  This allows renaming of all elements if necessary for processing
 */
formValidator.prototype.prepareAndSubmit = function() {
    // Loop through all form elements
    for(i = 0; i < this.form.elements.length; i++) {
        elementInfo = this.getInfo(this.form.elements[i]);
        if(elementInfo['name'] != 'default') {
            this.form.elements[i].name = elementInfo['name'];
        }
    }
    
    return true;
}

/*
 * 12/17/2004, Bahnck - constructs input listing form contents
 */
formValidator.prototype.constructContentsInput = function() {
  var contentsList = "";
  var formContents = document.createElement("input");
  
  // Set new element's attributes
  formContents.setAttribute("type", "hidden");
  formContents.setAttribute("name", "formContents");
  formContents.setAttribute("id", "formContents");
  
  // Constuct string of all form's input names, using "," as delimiter
  for (var i = 0; i < this.form.elements.length; i++) {
    contentsList += this.form.elements[i].name;
    if ((i + 1) < this.form.elements.length) {
      contentsList += ":";
    }
  }
  
  // Set set input's value to comma-delimited string and attach for <form> parent
  formContents.setAttribute("value", contentsList);
  this.form.appendChild(formContents);
} // end constructContentsInput

