// Types we support
FormCheck.Types = new Array('String', 'ZipCode', 'Phone', 'Email', 'Integer', 'Float');

// Constructor
// Arguments:
//	myForm - a reference to the form being validated.
//	errorDiv - the name of the div that will be populated with error messages.
//	highlightFields - true to highlight the fields that raised an error.
//	highlightColor (optional) - the color to highlight fields (if highlightFields is true)
//	dimColor (optional) - the color to use when an error has been fixed (if highlightFields is true)

function FormCheck(myForm, errorDiv, highlightFields, highlightColor, dimColor) {
  // Take care of optional paramaters.
  if (arguments.length < 5) dimColor = "black";
  if (arguments.length < 4) highlightColor = "red";
  if (arguments.length < 3) highlight = false;

  this.form = myForm;

  this.highlightFields = highlightFields;
  this.highlightColor = highlightColor;
  this.dimColor = dimColor;

  this.errorDiv = errorDiv;

  this.Items = new Array();
  this.Errors = new Array();

  // addItem
  // Add a field to be checked.

  this.addItem = function(fieldName, type, label, required, min, max, customError, altField) {
    if (arguments.length < 8) altField = false;
    if (arguments.length < 7) customError = false;
    if (arguments.length < 6) max = 0;
    if (arguments.length < 5) min = 0;
    if (arguments.length < 4) required = false;

    // Make sure the field exists.
    if (!this.form.elements[fieldName]) return false;

    // Make sure we support the requested field type.
    if (!(type = this.findType(type))) return false;

    // Add the item.
    var itemId = this.Items.length;

    this.Items[itemId] = new Array();
    this.Items[itemId]['fieldName'] = fieldName;
    this.Items[itemId]['type'] = type;
    this.Items[itemId]['label'] = label;
    this.Items[itemId]['required'] = required;

    if (max > min) {
      this.Items[itemId]['min'] = min;
      this.Items[itemId]['max'] = max;
    }

    if (customError) this.Items[itemId]['customError'] = customError;
    if (altField) this.Items[itemId]['altField'] = altField;
  };

  // checkFields()
  // Checks the required fields for valid input.

  this.checkFields = function() {
    // Dim fields
    if (this.highlightFields)
      this.highlightErrors(this.dimColor);

    // Delete old errors
    for (var i in this.Errors)
      this.Errors[i] = null;

    this.Errors.length = 0;

    for (var i in this.Items) {
      var Item = new Array();
      Item = this.Items[i];

      var value = this.getValue(Item['fieldName']);

      var errorId = this.Errors.length;

      // If our original field is empty, and we have an alternate field defined,
      // use the alternate field's value.
      if ((this.isEmpty(value)) && (Item['altField']))
	value = this.getValue(Item['altField']);

      if (this.isEmpty(value)) {

	if (Item['required']) {
	  this.Errors[errorId] = new Array();
	  this.Errors[errorId]['fieldName'] = Item['fieldName'];
	  this.Errors[errorId]['label'] = Item['label'];
	  this.Errors[errorId]['customError'] = Item['customError'];
	}

	continue;
      }

      var functionCall = "this.is" + Item['type'] + "('" + value + "'";

      if ((Item['min']) || (Item['max'])) {
	functionCall = functionCall + ", " + Item['min'] + ", " + Item['max'];
      }
      functionCall = functionCall + ")";

      var newValue;
      if (!(newValue = eval(functionCall))) {

	this.Errors[errorId] = new Array();
	this.Errors[errorId]['fieldName'] = Item['fieldName'];
	this.Errors[errorId]['label'] = Item['label'];
	this.Errors[errorId]['customError'] = Item['customError'];

      } else {
	if (value != newValue)
	  this.setValue(Item['fieldName'], newValue)
      }
    }

    if (this.Errors.length) {
      if (this.highlightFields)
	this.highlightErrors(this.highlightColor);

      if (this.errorDiv)
	this.showError(this.errorDiv)

      this.setFocus(this.Errors[0]['fieldName']);
      return false;
    }

    return true;
  };

  this.setFocus = function(fieldName) {
    var theField = this.form.elements[fieldName];

    if (theField.type != 'hidden')
      theField.focus();
  }


  // getValue(fieldName)
  // Get the value of the current field.

  this.getValue = function(fieldName) {
    var theField = this.form.elements[fieldName];
    var type = theField.type;
    var value;

    if ((type == 'text') || (type == 'textarea') || (type == 'hidden'))
      value = theField.value;
    else if (type.indexOf('select') != -1)
      value = theField.options[theField.selectedIndex].value;
    else if (type == 'radio')
      for (var i in theField)
	if (theField[i].checked) 
	  value = theField[i].value;

    // Escape single quotes.
    value = value.replace(/\'/, "\\'");
    return value;
  };

  this.setValue = function(fieldName, value) {

    var theField = this.form.elements[fieldName];
    var type = theField.type;

    if ((type == 'text') || (type == 'textarea'))
      theField.value = value;
    else if (type == 'select') {

      for (var i in type.elements) {
	if (type.elements[i].value == value) {
	  type.elements[i].selected = true;
	  break;
	}
      }
    } else if (type == 'radio') {
      // FIXME - Add radio buttons.
    }

    return true;
  };

  // findType(type)
  // Determines if our class supports a given field type.
  this.findType = function(type) {

    for (var i in FormCheck.Types)
      if (FormCheck.Types[i].toLowerCase() == type.toLowerCase()) return FormCheck.Types[i];

    return false;
  };

  this.writeDiv = function(error) {

    var showed = 0;

    if (document.all) {
      if (document.all(errorDiv)) {
	document.all(errorDiv).innerHTML = error;
	showed = 1;
      }
    } else if (document.getElementById) {

      var myObj = document.getElementById(errorDiv);

      if (myObj) {
	var rng = document.createRange();
	rng.setStartBefore(myObj);

	var htmlFrag = rng.createContextualFragment(error);

	while (myObj.hasChildNodes())
	  myObj.removeChild(myObj.lastChild);

	myObj.appendChild(htmlFrag);

	showed = 1;
      }
    }

    if (!showed)
      alert(error);

  };

  this.showError = function(errorDiv) {
    var error = "Please fix the following fields: "

      for (var i in this.Errors) {
	if (this.Errors[i]['customError'])
	  error = error + this.Errors[i]['customError']
	else
	  error = error + this.Errors[i]['label'];

	if (i != this.Errors.length - 1)
	  error = error + ", ";
      }

      this.writeDiv(error);
  };

  // highlightErrors(color)
  // Changes the border color of the fields that contained errors. IE4+, NS6+

  this.highlightErrors = function(color) {

    if (document.all || document.getElementById) {
      for (var i in this.Errors) {

	if (document.getElementById) {

	  //var field = document.getElementById(this.Errors[i]['fieldName']);
	  var field = this.form.elements[this.Errors[i]['fieldName']];

	  //alert(field);
	  field.style.borderColor = color;
	} else {
	  document.all.elements[this.Errors[i]['fieldName']].style.borderColor = color;
	}
      }
    }
  };

  this.hasErrors = function() {
    if (this.Errors.length) return true;

    return false;
  }

  // Validation Functions 

  // isEmpty(value)
  this.isEmpty = function(value) {

    // Remove leading and trailing whitespace
    value = value.replace(/^\s*(.+)\s*$/, "$1"); 

    if ((value == null) || (value == ''))
      return true;

    return false;
  };

  this.isInteger = function(value, min, max) {
    if (arguments.length < 3) max = 0;
    if (arguments.length < 2) min = 0;

    if (isNaN(value)) return false;

    // No decimals allowed
    if (value.indexOf(".") != -1) return false;

    if (max > min)
      if ((value < min) || (value > max))
	return false;

    return true;
  }

  this.isFloat = function(value) {
    if (arguments.length < 3) max = 0;
    if (arguments.length < 2) min = 0;

    if (isNaN(value)) return false;

    if (max > min) 
      if ((value < min) || (value > max))
	return false;

    return true;
  }

  this.isZipCode = function (value) { 
    value = value.replace(/\D/g, "");

    if ((value.length == 5) || (value.length == 9)) {
      return this.reformatZipCode(value);
    }

    return false;
  };

  this.isPhone = function (value) { 
    value = value.replace(/\D/g, "");

    if (value.length == 10) {
      return this.reformatPhone(value);
    }

    return false;
  };

  this.isString = function(value) {
    if (this.isEmpty(value)) 
      return false

	return value;
  };

  this.isEmail = function (value) {

    var i = 1;
    var length = value.length;

    while ((i < length) && (value.charAt(i) != "@"))
      i++;

    if ((i >= length) || (value.charAt(i) != "@")) 	
      return false;
    else i += 2;
    while ((i < length) && (value.charAt(i) != "."))
      i++;

    if ((i >= length - 1) || (value.charAt(i) != ".")) return false;
    else return value;
  }

  // Reformat Functions

  this.reformatZipCode = function(value, required) {

    value = value.replace(/\D/g, "");

    if (value.length == 5)
      return value;
    else if (value.length == 9)
      return value.replace(/(\d{5})(\d{4})/, "$1-$2");

    return false;		
  };

  this.reformatPhone = function(value, required) {

    value = value.replace(/\D/g, "");

    if (value.length == 10)
      return value.replace(/(\d{3})(\d{3})(\d{4})/, "$1-$2-$3");

    return false;		
  };	
}
