var Validation = {};
Validation.init = function(valData, form, errorClass) {
  var valSpec, fld, jqForm;
  for ( valSpec in valData ) {
    fld = form.elements[valSpec];
    if ( typeof fld == "undefined" ) {
      continue;
    }
    // for radio buttons, attach to each
    //   selects also have a length, radio groups have no tagName
    if (( typeof fld == "object" ) && ( fld.length ) && ( ! fld.tagName )) {
      for ( var i = 0; i < fld.length; i++ ) {
        fld[i].validation = valData[valSpec];
        $(fld[i]).bind("click", function() {
           Validation.validateField(this); 
        });        
      }
    } else {
      fld.validation = valData[valSpec];
      $(fld).bind("change", function() {
         Validation.validateField(this); 
      });
    }
  }
  jqForm = $(form);
  if ( jqForm.hasClass("stopDouble") ) {
    jqForm.unbind("submit");  // remove default stopDouble handler
  }
  jqForm.bind("submit", Validation.validateForm);
  Validation.errorClass = errorClass;
};

Validation.DontValidate = function(form) {
  $(form).unbind("submit", Validation.validateForm);
};

Validation.validateField = function(fld) {
  var aTest, type, params, val, errors = [];
  fld.isValid = true;
  if ( fld.validation ) {
    if ( Validation.checkDependencies(fld) ) {  // dependencies satisfied
      for ( var i = 0; i < fld.validation.length; i++ ) {
        aTest = fld.validation[i].test.split(":");
        type = aTest.shift();
        params = aTest.join(":");
        if ( ! Validation.check(fld, type, params) ) {
          fld.isValid = false;
          errors.push(fld.validation[i].msg);
        }
      }
    }
    this.showErrors(fld, errors);
  }
  return fld.isValid;
};

Validation.validateForm = function() {
  var jqForm, valid = true;
  
  if(this.submitInProgress === true) {
    return false;
  } 

  for ( var i = 0; i < this.elements.length; i++ ) {
    if ( ! Validation.validateField(this.elements[i]) ) {
      valid = false;
    }
  }

  if (valid === true) {
    jqForm = $(this);
    if (( jqForm.hasClass("stopDouble") ) || 
        ( jqForm.hasClass("preventDoubleSubmit") )) {
      this.submitInProgress = true;
      setTimeout(function() {
        $("input[type='submit'], input[type='image']", jqForm).attr("disabled", true);
      }, 2);
    }
  }

  return valid;
};

Validation.showErrors = function(fld, errors) {
  var jQFld = $(fld);
  if ( errors.length > 0 ) {
    tError = "<p class=\"Error\">";
    tError += errors.join(".  ");
    tError += ".</p>";
    jQFld.parent().find(".Error").remove();
    jQFld.addClass(Validation.errorClass);
    var ErrorTarget = null;
    if ( fld.type == "radio" ) {
      // find the last radio of the group and put error there
      var radios = $("input[name='"+fld.name+"']", fld.form);
      ErrorTarget = radios.eq(radios.length - 1);
    } else {
      ErrorTarget = jQFld;
    }
    ErrorTarget.parent().append(tError);
    ErrorTarget.parent().find(".Error").hide().show("slow");
  } else {
    if ( fld.type == "radio" ) {
      $("input[name='"+fld.name+"']", fld.form).removeClass(Validation.errorClass);
    } 
    jQFld.removeClass(Validation.errorClass).parent().find(".Error").hide("slow", function() {
        $(this).remove();
      });
  }
};

Validation.GetValue = function(fld) {
  // radio group
  var name = null;
  if (( typeof fld == "object" ) && ( fld.length ) && ( ! fld.tagName )) {
    name = fld[0].name;
    return $("input[name='" + name + "']:checked").val();
  }
  if ( fld.type == "radio" ) { // got 1 element of a radio group
    name = fld.name;
    return $("input[name='" + name + "']:checked").val();
  }
  if ( fld.tagName.toLowerCase() == "select" ) {
    return fld.options[fld.selectedIndex].value;
  } else {
    if (( fld.type == "checkbox" ) && ( ! fld.checked )) {
      return "";
    }
    return fld.value;
  }
};

Validation.checkDependencies = function(fld) {
  var doValidation = true;
  var aChecks, type, params;
  for ( var i = 0; i < fld.validation.length; i++ ) {
    var aTest = fld.validation[i].test.split(":");
    if ( aTest.shift() == "ValidateIf") {
      var relField = fld.form.elements[aTest.shift()];
      if ( relField ) {
        var checks = aTest.join(":").split("\n");
        for ( var j = 0; j < checks.length; j++ ) {
          aChecks = checks[j].split(":");
          type = aChecks.shift();
          params = aChecks.join(":");
          if ( ! Validation.check(relField, type, params) ) {
            doValidation = false;
          }
        }
      }
    }
  }
  return doValidation;
};

Validation.check = function(fld, type, params) {
  var aParams, val, max, min;
  aParams = params.split(",");
  val = Validation.GetValue(fld);  
  switch ( type ) {
    case "ValidateIf": return true; // used in checkDependencies
    case "Required": return Validation.checkRequired(val);
    case "Email": return Validation.checkEmail(val);
    case "Range":
      max = 32767;
      min = aParams[0];
      if ( aParams.length > 1 ) {
        max = aParams[1];
      }
      return Validation.checkNumberRange(val, min, max);
    case "Length": 
      max = 32767;
      min = aParams[0];
      if ( aParams.length > 1 ) {
        max = aParams[1];
      }
      return Validation.checkStringLength(val, min, max);
    case "Regex": return Validation.checkString(val, params);
    case "Match": return Validation.checkString(val, params);
    case "Function": return Validation.checkFunction(fld, params);
    default: return false;
  }
};

Validation.checkRequired = function(val) {
  return ( val > "" );
};

Validation.checkEmail = function(val) {
  var nameExp = "^[a-z0-9\._-]+$";
  var domainExp = "^([a-z0-9]?[a-z0-9\-_]*[a-z0-9]{1})+[\.]{1}([a-z]+\.){0,1}([a-z]+){1}$";

  var pieces = val.toLowerCase().split("@");

  if (pieces.length != 2) {
    return false;
  }

  return ((Validation.checkString(pieces[0], nameExp)) && 
      (Validation.checkString(pieces[1], domainExp)));
};

Validation.checkNumberRange = function(val, min, max) {
  // strip comma and dollar sign
  val = val.replace(/\$|\,/g, "");
  val = parseInt(val, 10);
  min = parseInt(min, 10);
  max = parseInt(max, 10);
  return (( val >= min ) && ( val <= max ));
};

Validation.checkStringLength = function(val, min, max) {
  min = parseInt(min, 10);
  max = parseInt(max, 10);
  return (( val.length >= min ) && ( val.length <= max ));
};

Validation.checkString = function(val, regex) {
  if ( regex.charAt(0) == "/" ) {
    regex = regex.substring(1, regex.length - 1);
  }
  var re = new RegExp(regex);
  return re.test(val);
};

Validation.checkFunction = function(fld, params) {
  var aParams = params.split(",");
  var func = aParams.shift();
  params = aParams.join(",");
  
  // if it exists and is attached to the field
  if ( typeof fld[func] == "function" ) {
    return fld[func](fld, params);
  }
  // if it exists ans is attached to the form
  if ( typeof fld.form[func] == "function" ) {
    return fld.form[func](fld, params);
  }
  // if it exists ans is attached to the validator
  if ( typeof Validation[func] == "function" ) {
    return Validation[func](fld, params);
  }
  // if it exists and is a standalone (it gets auto-attached to the window obj)
  if ( typeof window[func] == "function" ) {
    return window[func](fld, params);
  }
  return false;
};
