var errMsg = {
   // Checks for when a specified field is required
   required: {
      msg: "The % field is required.",
      test: function(obj,load) {
         // Make sure that there is no text was entered in the field and that
         // we aren't checking on page load (showing 'field required' messages
         // would be annoying on page load)
         //return obj.value.length > 0 || load || obj.value != obj.defaultValue;
         return obj.value.length > 0;
      }
   },
   // Makes sure that the field s a valid email address
   email: {
      msg: "The % field must contain a valid email address.",
      test: function(obj) {
         // Make sure that something was entered and that it looks like
         // an email address
         return !obj.value ||
            /^[a-z0-9_+.-]+\@([a-z0-9-]+\.)+[a-z0-9]{2,4}$/i.test( obj.value );
      }
   },
   // Makes sure the field is a phone number and
   // auto-formats the number if it is one
   phone: {
      msg: "The % field must contain a valid phone number.",
      test: function(obj) {
         // Check to see if we have something that looks like
         // a valid phone number
         var m = /(\d{3}).*(\d{3}).*(\d{4})/.exec( obj.value );
         // If it is, seemingly, valid - force it into the specific
         // format that we desire: (123) 456-7890
         if ( m ) obj.value = "(" + m[1] + ") " + m[2] + "-" + m[3];
         return !obj.value || m;
      }
   },
   // Makes sure that the field is a valid MM/DD/YYYY date
   date: {
      msg: "The % field must contain a valid date.",
      test: function(obj) {
         // Make sure that something is entered, and that it
         // looks like a valid MM/DD/YYYY date
         return !obj.value || /^\d{2}\/\d{2}\/\d{2,4}$/.test(obj.value);
      }
   },
   // Makes sure that the field is a valid URL
   url: {
      msg: "The % field must contain a valid URL.",
      test: function(obj) {
         // Make sure that some text was entered, and that it's
         // not the default http:// text
         return !obj.value || obj.value == 'http://' ||
            // Make sure that it looks like a valid URL
            /^(http:\/\/)?([a-z0-9-]+\.)+[a-z0-9]{2,4}.*$/i.test( obj.value );
      }
   },
   // alpha_numeric
   alpha_numeric: {
      msg: "The % field may only contain alpha-numeric characters.",
      test: function(obj) {
         // Make sure that something is entered, and that it
         // looks like a valid MM/DD/YYYY date
         return !obj.value || /^[a-z0-9_]+$/i.test(obj.value);
      }
   },
   // match
   matches: {
      msg: "The % field does not match the % field."
   },
   // length range
   min_length: {
      msg: "The % field must be at least % characters in length.",
      test: null
   },
   max_length: {
      msg: "The % field can not exceed % characters in length.",
      test: null
   },
   // in use
   in_use: {
      msg: "The % is already being used.",
      test: null
   }
};

var formErrors={_count:0};

function getFormError(args)
{
   var i = 0, p = 0, msg = errMsg[args[0]].msg;
   // Cannot use replace() with a callback, because it is incompatible with IE5.
   while ((p = msg.indexOf('%', p)) >= 0)
   {
      var a = args[++i] + "";
      if (a=="undefined") a=args[0];
      msg = msg.substring(0, p) + a + msg.substring(p + 1, msg.length);
      p += 1 + a.length;
   }
   return msg;
}

function showFormError(formid)
{
   hideFormError(formid);
   
   var errstr=[];
   var errI=0;
   for (var i in formErrors)
   {
      if (i=='_count' || !formErrors[i]) continue;
      errstr[errI]=formErrors[i];
      errI++;
   }
   $(formid).before('<div class="error">'+errstr.join('<br/>')+'</div>');
}

function showFormErrorString(formid,msg)
{
   hideFormError(formid);
   $(formid).before('<div class="error">'+msg+'</div>');
}

function hideFormError(formid)
{
   if ($(formid).prev().is('div.error') || $(formid).prev().is('div.message'))
      $(formid).prev().remove();
}

function validateForm(formid)
{
   formErrors={_count:0};
   hideFormError(formid)
   
   $(this).find('input,textarea,select').each(function()
   {
      if (this.name && this.className)
      {
         for (var name in errMsg)
         {
            var re = new RegExp("(^|\\s)" + name + "(\\s|$)");
            if (re.test(this.className) && !errMsg[name].test(this, false))
            {
               formErrors[this.name]=getFormError([name,getFieldLabel('#'+this.id)]);
               formErrors._count++;
               break;
            }
         }
      }
   });
}

function getFieldLabel(fieldId)
{
   var s=$(fieldId).siblings('label[@for=\''+fieldId.substring(1)+'\']');
   return s.is('label')
      ? s.text().replace(/\*?:?$/,'')
      : $(fieldId).attr('title');
}

function validLengthRange(fieldId,minL,maxL)
{
   if (minL>0 && $(fieldId).val().length<minL)
      return getFormError(['min_length',getFieldLabel(fieldId),minL]);
   if (maxL>0 && $(fieldId).val().length>maxL)
      return getFormError(['max_length',getFieldLabel(fieldId),maxL]);
   return null;
}

function validMatchField(fieldId,targetId)
{
   if ($(fieldId).val()!=$(targetId).val()) return getFormError(['matches',getFieldLabel(fieldId),getFieldLabel(targetId)]);
   return null;
}

function setFormError(name,error)
{
   if (!formErrors[name] && error)
   {
      formErrors[name]=error;
      formErrors._count++;
   }
}

function emptyFormErrors()
{
   formErrors={_count:0};
}