/*
 * jQuery Validator plugin v1.2.5
 * File Date: 11/12/2008
 *
 * Copyright (c) 2008 Jim Salyer
 * Licensed under the MIT License:
 *   http://www.opensource.org/licenses/mit-license.php
 */

// create the validator object
$.extend({
  validator: new Object()
});

// create the members of the validator object
$.extend($.validator, {
  defs: {}, // validation definitions list
  // add a new validation definition
  addDef: function(defName, defMethod, defMessage, defValueDriven)
  {
    this.defs[defName.toLowerCase()] = {
      method: defMethod,
      message: defMessage,
      valueDriven: defValueDriven
    };
    return this;
  },
  // get the date's month, day and year based upon the given format
  dateParts: function(val, format)
  {
    var month = 0, day = 0, year = 0;
    if (/^%m %d,? %y$/i.test(format))
    {
      var matches = val.match(/^(\w+) (\d{1,2})(nd|rd|st|th)?,? (\d{4})$/i);
      if (matches)
      {
        month = $.validator.monthFromName(matches[1]);
        day = parseInt(matches[2].replace(/^0/, ""));
        if (matches[3] && !$.validator.isValidNumberSuffix(day, matches[3])) day = 0;
        year = matches[4];
      }
    }
    else if (/^%d %m %y$/i.test(format))
    {
      var matches = val.match(/^(\d{1,2})(nd|rd|st|th)? (\w+) (\d{4})$/i);
      if (matches)
      {
        day = parseInt(matches[1].replace(/^0/, ""));
        if (matches[2] && !$.validator.isValidNumberSuffix(day, matches[2])) day = 0;
        month = $.validator.monthFromName(matches[3]);
        year = matches[4];
      }
    }
    else if (/^%y %m %d$/i.test(format))
    {
      var matches = val.match(/^(\d{4}) (\w+) (\d{1,2})(nd|rd|st|th)?$/i);
      if (matches)
      {
        year = matches[1];
        month = $.validator.monthFromName(matches[2]);
        day = parseInt(matches[3].replace(/^0/, ""));
        if (matches[4] && !$.validator.isValidNumberSuffix(day, matches[4])) day = 0;
      }
    }
    else if (/^%m([\/\-\.])%d\1%y$/i.test(format))
    {
      var matches = val.match(/^(\d{1,2})([\/\-\.])(\d{1,2})\2(\d{4})$/);
      if (matches)
      {
        month = parseInt(matches[1].replace(/^0/, ""));
        day = parseInt(matches[3].replace(/^0/, ""));
        year = matches[4];
      }
    }
    else if (/^%d([\/\-\.])%m\1%y$/i.test(format))
    {
      var matches = val.match(/^(\d{1,2})([\/\-\.])(\d{1,2})\2(\d{4})$/);
      if (matches)
      {
        day = parseInt(matches[1].replace(/^0/, ""));
        month = parseInt(matches[3].replace(/^0/, ""));
        year = matches[4];
      }
    }
    else if (/^%y([\/\-\.])%m\1%d$/i.test(format))
    {
      var matches = val.match(/^(\d{4})([\/\-\.])(\d{1,2})\2(\d{1,2})$/);
      if (matches)
      {
        year = matches[1];
        day = parseInt(matches[3].replace(/^0/, ""));
        month = parseInt(matches[4].replace(/^0/, ""));
      }
    }
    
    // return an object with the resulting date parts
    return {
      "month": month,
      "day": day,
      "year": year
    }
  },
  // return the number of days in the given month in the given year
  daysInMonth: function(month, year)
  {
  	var numOfDays = 0;
  	switch (parseInt(month))
  	{
  		case 1: case 3: case 5: case 7: case 8: case 10: case 12:
  			numOfDays = 31;
  			break;
  		case 4: case 6: case 9: case 11:
  			numOfDays = 30;
  			break;
  		case 2:
  			if (parseInt(year) % 4 == 0)
  				numOfDays = 29;
  			else
  				numOfDays = 28;
  			break;
  		default: numOfDays = 0;
  	}
  	return numOfDays;
  },
  // format error messages for display
  formatMessage: function(defName, fieldName, fieldDisplay, fieldParams, fieldValue)
  {
    var message = this.defs[defName].message.replace(new RegExp("\{name\}", "g"), fieldName).replace(new RegExp("\{display\}", "g"), fieldDisplay).replace(new RegExp("\{value\}", "g"), fieldValue);
    $.each(fieldParams, function(i, n)
    {
      message = message.replace(new RegExp("\{[" + (i + 1) + "]\}", "g"), n);
    });
    return message;
  },
  // get the value of the given field (by field name, not field ID)
  getValue: function(fieldName)
  {
    // select all possible field types with the given name
    var val = "";
    $("input[name=" + fieldName + "], select[name=" + fieldName + "], textarea[name=" + fieldName + "]").each(function()
    {
      // make sure we have JQuery capabilities with the field
      var obj = $(this);
      if (val != "") val += ","; // if we're concatenating values, add a comma between each one
      
      // process the value by the field's type
      switch (obj.attr("type"))
      {
        case "checkbox":
          if (obj.attr("checked")) val += obj.val();
          break;
        case "radio":
          if (obj.attr("checked")) val = obj.val();
          break;
        default:
          val = obj.val();
      }
    });
    return $.trim(val); // trim the final result
  },
  // check if the given suffix is appropriate for the given number
  isValidNumberSuffix: function(num, suffix)
  {
    num = num.toString();
    suffix = suffix.toLowerCase();
    var lastDigit = parseInt(num.charAt(num.length - 1));
    
    switch (lastDigit)
    {
      case 0: case 4: case 5: case 6: case 7: case 8: case 9:
        return suffix == "th";
        break;
      case 1:
        return suffix == "st";
        break;
      case 2:
        return suffix == "nd";
        break;
      case 3:
        return suffix == "rd";
        break;
    }
    return false;
  },
  // check if a value is "blank" (undefined or zero length)
  isBlank: function(val)
  {
    return (typeof(val) == "undefined" || val == "");   
  },
  // check if the given date is valid using the given format
  isDate: function(val, format)
  {
    // create a new date based upon the parsed month, day and year and check its validity
    var dp = $.validator.dateParts(val, format);
    var d = new Date(dp.year, dp.month - 1, dp.day);
    return (d.getMonth() + 1 == dp.month && d.getDate() == dp.day);
  },
  // create an implementation of maxlength for textareas
  limit: function(fieldName, fieldDisplay, fieldLength)
  {
    // get the elements and values involved in the calculations
    var textarea = $("textarea[name=" + fieldName + "]");
    var val = textarea.val();
    var display = $("#" + fieldName + "Length");
    
    // check the length of what's currently in the field
    if (val.length > fieldLength)
    {
      // if there is too much in the field, tell the user and reset the length accordingly
      if (display.length > 0) display.html(fieldDisplay + " must contain no more than " + fieldLength + " character(s).");
      textarea.val(val.substr(0, fieldLength));
    }
    else if (display.length > 0)
      display.html((fieldLength - val.length) + " character(s) remaining."); // keep the user up to date on what they have left
  },
  // perform a Luhn algorithm check on a given credit card number
  luhn: function(val)
  {
    val = val.replace(/\D/g, "");
    var sum = 0, digit = 0;
		var even = false;

		for (var i=val.length-1; i>=0; i--)
    {
			digit = parseInt(val.charAt(i), 10);
			if (even && (digit *= 2) > 9) digit -= 9;
      sum += digit;
      even = !even;
    }
		return sum % 10 == 0;
  },
  monthFromName: function(val)
  { 
    // format the given value for comparison and create the array of months to compare against
    val = val.toLowerCase();
    var months = ["january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december"];
    
    // if the given value matches a month or month abbreviation, return that month's index
    for (var i=0; i<months.length; i++)
      if (months[i] == val || months[i].substr(0, 3) == val)
        return i + 1;
    return 0;
  },
  // calculate the given password's strength
  passwordStrength: function(val)
  {
    var pwdLength = val.length;
    if (pwdLength > 4) pwdLength = 4;
    var percent = 0;
    var rating = "Not Rated";
  
    if (val.length > 0)
    {
      // use of numbers in the password
      var numOfNumbers = val.replace(/[^0-9]/g, "").length;
      if (numOfNumbers > 3) numOfNumbers = 3;
    
      // use of symbols in the password
      var numOfSymbols = val.replace(/[^\W]/g, "").length;
      if (numOfSymbols > 3) numOfSymbols = 3;
    
      // use of uppercase in the password
      var numOfUpper = val.replace(/[^A-Z]/g, "").length;
      if (numOfUpper > 3) numOfUpper = 3;
      
      // use of lowercase in the password
      var numOfLower = val.replace(/[^a-z]/g, "").length;
      if (numOfLower > 3) numOfLower = 3;
    
      // calculate and return a strength number
      percent = pwdLength * 10 - 20 + 8 * (numOfNumbers + numOfSymbols) + 7 * (numOfUpper + numOfLower);
      if (percent < 0) percent = 0; // make sure we're give a value between 0 and 100
      if (percent > 100) percent = 100;
    }
    
    if (percent <= 0)
      rating = "Not Rated";
    else if (percent <= 25)
      rating = "Very Weak";
    else if (percent <= 50)
      rating = "Weak";
    else if (percent <= 75)
      rating = "Medium";
    else
      rating = "Strong";
      
    return {
      "percent": percent,
      "rating": rating
    };
  },
  // get the decimal precision of the given number
  precision: function(val)
  {
    if (!isNaN(val))
    {
      val = val.toString();
      var parts = val.split(".");
      return parts.length == 2 ? parts[1].length : 0;
    }
    else
      return 0;
  },
  // show the password strength value
  showPasswordStrength: function(fieldName, fieldValue)
  {
    var strength = this.passwordStrength(fieldValue);
    var meterClass = "validator_passwordmeter_";
    var textClass = "validator_passwordtext_";
    var strengthClass;
    var bar = $("#" + fieldName + "StrengthBar");
    var rating = $("#" + fieldName + "StrengthText");
    
    // get a text and color rating based on the strength rating
    if (strength.percent <= 0)
      strengthClass = "notrated";
    else if (strength.percent <= 25)
      strengthClass = "veryweak";
    else if (strength.percent <= 50)
      strengthClass = "weak";
    else if (strength.percent <= 75)
      strengthClass = "medium";
    else
      strengthClass = "strong";
     
    if (bar.length > 0)
    {
      bar.css("width", strength.percent + "%");
      var oldClasses = bar.attr("class");
      var newClasses = [meterClass + strengthClass];
      
      if (oldClasses)
      {
        oldClasses = oldClasses.split(" ");
        $.each(oldClasses, function(i, n)
        {
          if (n.indexOf(meterClass) < 0) newClasses.push(n);
        });
      }
      bar.attr("class", newClasses.join(" "));
    }
    
    if (rating.length > 0)
    {
      rating.html(strength.rating);
      var oldClasses = rating.attr("class");
      var newClasses = [textClass + strengthClass];
      
      if (oldClasses)
      {
        oldClasses = oldClasses.split(" ");
        $.each(oldClasses, function(i, n)
        {
          if (n.indexOf(textClass) < 0) newClasses.push(n);
        });
      }
      rating.attr("class", newClasses.join(" "));
    }
  }
});

// create the members of HTML elements that allow interaction with the validator
$.fn.extend({
  addField: function(fieldName, fieldOptions, fieldDisplay)
  {
    // reserve this functionality only for forms
    if (!this.attr("action")) return;
    if (!this.data("validator_fields")) this.data("validator_fields", {});
    
    // remap the required validation options accordingly
    var tip = $("#" + fieldName + "Tip");
    fieldOptions = $.map(fieldOptions, function(n, i)
    {
      // separate the validation definition from its parameters
      var opt = n.split("=");
      var optionName = opt[0].toLowerCase();
      var optionParams = opt.length > 1 ? opt[1].split("|") : [];
      
      // handle any vertical bar placeholders in any regular expressions
      optionParams = $.map(optionParams, function(n, i)
      {
        return n.replace(/&#124;/g, "|");
      });
      
      if ($.validator.defs[optionName])
      {
        // if the field's tip container exists, write this option's message to it
        if (tip.length > 0) tip.append($.validator.formatMessage(optionName, fieldName, fieldDisplay, optionParams, $.validator.getValue(fieldName)) + '<br>');
        
        // set up the maxlength control for a textarea
        if (optionName == "maxlength")
        {
          var textarea = $("textarea[name=" + fieldName + "]");
          if (optionParams.length > 0 && textarea.length > 0)
          {
            var fieldLength = optionParams[0];
            $.validator.limit(fieldName, fieldDisplay, fieldLength);
            
            textarea.keyup(function()
            {
              $.validator.limit(fieldName, fieldDisplay, fieldLength);
            });
          }
        }
        
        // set up the password strength control for applicable fields
        if (optionName == "passwordstrength")
        {
          var fld = $("input[name=" + fieldName + "]");
          $.validator.showPasswordStrength(fieldName, $.validator.getValue(fieldName));
          
          if (fld.length > 0)
          {
            fld.keyup(function()
            {
              $.validator.showPasswordStrength(fieldName, $.validator.getValue(fieldName));
            });
          }
        }
        
        // if the field's tip container exists and has messages in it, set up its display method
        if (tip.length > 0 && tip.html() != '')
        {
          // initialize the tip's visibility
          tip.css("visibility", "visible");
          tip.hide();
          
          $("input[name=" + fieldName + "], select[name=" + fieldName + "], textarea[name=" + fieldName + "]").each(function()
          {
            // make sure we have JQuery capabilities with the field
            var obj = $(this);
            
            // show the tips on focus
            obj.focus(function()
            {
              var tipLeft = 0, tipTop = 0;
              var lbl, pos;
              
              // see if the field is part of a group (i.e. checkbox or radio button)
              if (obj.is("input[type=checkbox], input[type=radio]") && obj.attr("id") && (lbl = $("label[for=" + obj.attr("id") + "]:last")).length > 0)
              {
                // align the tip with the label instead of the field
                pos = lbl.position();
                tipLeft = pos.left;
                tipTop = pos.top + lbl.fullHeight();
              }
              else
              {
                // align the tip with the field
                pos = obj.position();
                tipLeft = pos.left;
                tipTop = pos.top + obj.fullHeight();
              }
              
              tip.css({
                left: tipLeft + "px",
                top: tipTop + "px"
              });
              tip.show();
            });
            
            // hide the tips on blur
            obj.blur(function(e)
            {
              tip.hide();
            });
          });
        }
        
        // return an object containing the field validation information
        return {
          def: optionName,
          params: optionParams,
          "tip": tip
        };
      }
    });
    
    // add a new field definition
    this.data("validator_fields")[fieldName] = {
      validation: fieldOptions,
      display: fieldDisplay
    };
    return this;
  },
  // get an elements full height (i.e. including padding and border)
  fullHeight: function()
  {
    return this.height() + parseInt(this.css("paddingTop")) + parseInt(this.css("paddingBottom")) + (!isNaN(parseInt(this.css("borderTopWidth"))) ? parseInt(this.css("borderTopWidth")) : 0) + (!isNaN(parseInt(this.css("borderBottomWidth"))) ? parseInt(this.css("borderBottomWidth")) : 0);
  },
  // get an elements full width (i.e. including padding and border)
  fullWidth: function()
  {
    return this.width() + parseInt(this.css("paddingLeft")) + parseInt(this.css("paddingRight")) + (!isNaN(parseInt(this.css("borderLeftWidth"))) ? parseInt(this.css("borderLeftWidth")) : 0) + (!isNaN(parseInt(this.css("borderRightWidth"))) ? parseInt(this.css("borderRightWidth")) : 0);
  },
  // set up validation for the given form
  validate: function(callback)
  {
    // reserve this functionality only for forms
    var frm = $(this);
    if (frm.length <= 0 || !frm.attr("action")) return;
    
    // set the form up to validate on submit
    frm.submit(function()
    {
      // initialize the error checker variable and loop through the field definitions
      var hasErrors = false;
      frm.data("validator_result", false);
      
      $.each(frm.data("validator_fields"), function(i, n)
      {
        // get the field's name, definition, and an initial array of error messages for future use
        var fieldName = i;
        var fieldDisplay = n.display;
        var fieldValue = $.validator.getValue(i);
        var messages = [];
        
        // loop through the field's validation definitions
        $.each(this.validation, function(i, n)
        {
          // 1. get the result of the validation
          // 2. if a validation error occurred, record it and note that an error occurred
          if ($.validator.defs[n.def])
          {
            var result = $.validator.defs[n.def].method(fieldValue, n.params);
            if ($.validator.isBlank(result)) result = false;
            if ($("input[name=" + fieldName + "], select[name=" + fieldName + "], textarea[name=" + fieldName + "]").length > 0 && ($.validator.defs[n.def].valueDriven || !$.validator.isBlank(fieldValue)) && !result)
            {
              messages.push($.validator.formatMessage(n.def, fieldName, fieldDisplay, n.params, fieldValue));
              hasErrors = true;
            }
          }
        });
        
        // show any validation errors for the current field
        if (messages.length > 0)
          $("#" + fieldName + "Error").html(messages.join('<br />')).show();
        else
          $("#" + fieldName + "Error").empty().hide();
      });
      
      frm.data("validator_result", hasErrors);
      if ($.isFunction(callback)) callback(hasErrors);
      return !hasErrors; // make sure the form won't submit if we encountered any errors
    });
    return this;
  }
});

// add the default validation definitions
$.validator
  .addDef("compare", function(fieldValue, fieldParams)
  {
    if (fieldParams.length > 1)
    {
      var val = $.validator.getValue(fieldParams[0]);
      return $.validator.isBlank(val) || fieldValue == val;
    }
  }, "{display} must have the same value as {2}.", false)
  .addDef("creditcarddate", function(fieldValue, fieldParams)
  {
    if (/^\d{1,2}([\/\-\.]?)(\d{2}\1)?\d{2,4}$/.test(fieldValue))
    {
      var matches = fieldValue.match(/^(\d{1,2})([\/\-\.]?)((\d{2})\2)?(\d{2,4})$/);
      if (matches)
      {
        var month = parseInt(matches[1].replace(/^0/, ""));
        var day = matches[4] ? parseInt(matches[4].replace(/^0/, "")) : 1;
        var year = matches[5].length == 2 ? "20" + matches[5] : matches[5];
        
        if ($.validator.isDate(month + "/" + day + "/" + year, "%m/%d/%y"))
        {
          if (!matches[4]) day = $.validator.daysInMonth(month, year);
          var d1 = new Date();
          var d2 = new Date(year, month - 1, day);
          return d1 < d2;
        }
      }
    }
  }, "{display} must contain a valid credit card expiration date.", false)
  .addDef("creditcardnumber", function(fieldValue, fieldParams)
  {
    return $.validator.luhn(fieldValue);
  }, "{display} must contain a valid credit card number.", false)
  .addDef("date", function(fieldValue, fieldParams)
  {
    if (fieldParams.length > 0)
    {
      var val;
      if (fieldParams[0].charAt(0) == "%")
        val = fieldParams[0];
      else
        val = $.validator.getValue(fieldParams[0]);
      return $.validator.isDate(fieldValue, val);
    }
  }, "{display} must contain a valid date in the form {1}.", false)
  .addDef("daterange", function(fieldValue, fieldParams)
  {
    if (fieldParams.length > 2)
    {
      var val;
      if (fieldParams[0].charAt(0) == "%")
        val = fieldParams[0];
      else
        val = $.validator.getValue(fieldParams[0]);
        
      if (!$.validator.isDate(fieldValue, val)) return false;
      if ($.validator.isDate(fieldParams[1], val) && $.validator.isDate(fieldParams[2], val))
      {
        fieldValue = fieldValue.replace(/(nd|rd|st|th)/i, "").replace(/[\-\.]/g, "/");
        fieldParams[1] = fieldParams[1].replace(/(nd|rd|st|th)/i, "").replace(/[\-\.]/g, "/");
        fieldParams[2] = fieldParams[2].replace(/(nd|rd|st|th)/i, "").replace(/[\-\.]/g, "/");
        
        var cd = new Date(fieldValue);
        return cd >= new Date(fieldParams[1]) && cd <= new Date(fieldParams[2]);
      }
    }
  }, "{display} must contain a date that is between {2} and {3}.", false)
  .addDef("decimal", function(fieldValue, fieldParams)
  {
    return /^\-?\d*\.\d+$/.test(fieldValue);
  }, "{display} must contain a decimal value.", false)
  .addDef("decimalrange", function(fieldValue, fieldParams)
  {
    if (fieldParams.length > 1)
      if (/^\-?\d*\.\d+$/.test(fieldValue) && /^\-?\d*\.\d+$/.test(fieldParams[0]) && /^\-?\d*\.\d+$/.test(fieldParams[1]))
        if (parseFloat(fieldParams[1]) > parseFloat(fieldParams[0]))
          return parseFloat(fieldValue) >= parseFloat(fieldParams[0]) && parseFloat(fieldValue) <= parseFloat(fieldParams[1]);
  }, "{display} must contain a decimal value between {1} and {2}.", false)
  .addDef("dependency", function(fieldValue, fieldParams)
  {
    if (fieldParams.length > 1 && $.validator.getValue(fieldParams[0]))
      return !$.validator.isBlank(fieldValue);
    return true;
  }, "{display} is required when {2} contains a value.", true)
  .addDef("email", function(fieldValue, fieldParams)
  {
    return /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*\.(\w{2}|(com|net|org|edu|int|mil|gov|arpa|biz|aero|name|coop|info|pro|museum))$/i.test(fieldValue);
  }, "{display} must contain a valid e-mail address.", false)
  .addDef("equals", function(fieldValue, fieldParams)
  {
    if (fieldParams.length > 0) return fieldValue == fieldParams[0];
  }, "{display} must have a value equal to {1}.", false)
  .addDef("extension", function(fieldValue, fieldParams)
  {
    if (fieldParams.length > 0)
    {
      var parts = fieldValue.split(".");
      return parts.length > 1 && !$.validator.isBlank(parts[parts.length - 1]) && new RegExp(",?" + parts[parts.length - 1] + ",?", "i").test(fieldParams[0]);
    }
  }, "{display} must have one of the following extensions: {1}.", false)
  .addDef("integer", function(fieldValue, fieldParams)
  { 
    return /^\-?\d+$/.test(fieldValue);
  }, "{display} must contain an integer value.", false)
  .addDef("integerrange", function(fieldValue, fieldParams)
  {
    if (fieldParams.length > 1)
      if (/^\-?\d+$/.test(fieldValue) && /^\-?\d+$/.test(fieldParams[0]) && /^\-?\d+$/.test(fieldParams[1]))
        if (parseInt(fieldParams[1]) > parseInt(fieldParams[0]))
          return parseInt(fieldValue) >= parseInt(fieldParams[0]) && parseInt(fieldValue) <= parseInt(fieldParams[1]);
  }, "{display} must contain an integer value between {1} and {2}.", false)
  .addDef("maxdate", function(fieldValue, fieldParams)
  {
    if (fieldParams.length > 1)
    {
      var val;
      if (fieldParams[0].charAt(0) == "%")
        val = fieldParams[0];
      else
        val = $.validator.getValue(fieldParams[0]);
      
      if ($.validator.isDate(fieldValue, val) && $.validator.isDate(fieldParams[1], val))
      {
        fieldValue = fieldValue.replace(/(nd|rd|st|th)/i, "").replace(/[\-\.]/g, "/");
        fieldParams[1] = fieldParams[1].replace(/(nd|rd|st|th)/i, "").replace(/[\-\.]/g, "/");
        return new Date(fieldValue) <= new Date(fieldParams[1])
      }
    }
  }, "{display} must contain a date that is earlier than or equal to {2}.", false)
  .addDef("maxlength", function(fieldValue, fieldParams)
  {
    if (fieldParams.length > 0 && !isNaN(fieldParams[0])) return fieldValue.length <= parseInt(fieldParams[0]);
  }, "The length of {display} must be less than or equal to {1}.", false)
  .addDef("maxvalue", function(fieldValue, fieldParams)
  {
    if (fieldParams.length > 0)
      if (!isNaN(fieldParams[0]))
        return !isNaN(fieldValue) && (fieldValue.indexOf(".") > -1 ? parseFloat(fieldValue) : parseInt(fieldValue)) <= (fieldParams[0].indexOf(".") > -1 ? parseFloat(fieldParams[0]) : parseInt(fieldParams[0]));
  }, "{display} must contain a value that is less than or equal to {1}.", false)
  .addDef("mindate", function(fieldValue, fieldParams)
  {
    if (fieldParams.length > 1)
    {
      var val;
      if (fieldParams[0].charAt(0) == "%")
        val = fieldParams[0];
      else
        val = $.validator.getValue(fieldParams[0]);
      
      if ($.validator.isDate(fieldValue, val) && $.validator.isDate(fieldParams[1], val))
      {
        fieldValue = fieldValue.replace(/(nd|rd|st|th)/i, "").replace(/[\-\.]/g, "/");
        fieldParams[1] = fieldParams[1].replace(/(nd|rd|st|th)/i, "").replace(/[\-\.]/g, "/");
        return new Date(fieldValue) >= new Date(fieldParams[1])
      }
    }
  }, "{display} must contain a date that is later than or equal to {2}.", false)
  .addDef("minlength", function(fieldValue, fieldParams)
  {
    if (fieldParams.length > 0 && !isNaN(fieldParams[0])) return fieldValue.length >= parseInt(fieldParams[0]);
  }, "The length of {display} must be greater than or equal to {1}.", false)
  .addDef("minvalue", function(fieldValue, fieldParams)
  {
    if (fieldParams.length > 0)
      if (!isNaN(fieldParams[0]))
        return !isNaN(fieldValue) && (fieldValue.indexOf(".") > -1 ? parseFloat(fieldValue) : parseInt(fieldValue)) >= (fieldParams[0].indexOf(".") > -1 ? parseFloat(fieldParams[0]) : parseInt(fieldParams[0]))
  }, "{display} must contain a value that is greater than or equal to {1}.", false)
  .addDef("negativedecimal", function(fieldValue, fieldParams)
  {
    return /^\-\d*\.\d+$/.test(fieldValue);
  }, "{display} must contain a negative decimal value.", false)
  .addDef("negativeinteger", function(fieldValue, fieldParams)
  {
    return /^\-\d+$/.test(fieldValue);
  }, "{display} must contain a negative integer value.", false)
  .addDef("nowhitespace", function(fieldValue, fieldParams)
  {
    return !/\s/g.test(fieldValue);
  }, "{display} cannot contain whitespace.", false)
  .addDef("numeric", function(fieldValue, fieldParams)
  {
    return !isNaN(fieldValue);
  }, "{display} must contain a numeric value.", false)
  .addDef("passwordstrength", function(fieldValue, fieldParams)
  {
    var strength = fieldParams.length > 1 && !isNaN(fieldParams[0]) ? parseInt(fieldParams[0]) : 51;
    return $.validator.passwordStrength(fieldValue).percent >= strength;
  }, "The strength of {display} must be at least {2}.", false)
  .addDef("positivedecimal", function(fieldValue, fieldParams)
  {
    return /^\d*\.\d+$/.test(fieldValue);
  }, "{display} must contain positive decimal value.", false)
  .addDef("positiveinteger", function(fieldValue, fieldParams)
  {
    return /^\d+$/.test(fieldValue);
  }, "{display} must contain a positive integer value.", false)
  .addDef("precision", function(fieldValue, fieldParams)
  {
    if (fieldParams.length > 0 && /^[1-31]$/.test(fieldParams[0]))
      return $.validator.precision(fieldValue) <= parseInt(fieldParams[0]);
  }, "{display} must contain a value with no more than {1} digits of precision.", false)
  .addDef("regexp", function(fieldValue, fieldParams)
  {
    if (fieldParams.length > 0) return new RegExp(fieldParams[0], fieldParams[1] ? fieldParams[1] : "").test(fieldValue);
  }, "{display} must contain a valid value.", false)
  .addDef("required", function(fieldValue, fieldParams)
  {
    return !$.validator.isBlank(fieldValue);
  }, "{display} is required.", true)
  .addDef("ssn", function(fieldValue, fieldParams)
  {
    return /^\d{9}$/.test(fieldValue);
  }, "{display} must contain a valid social security number.", false)
  .addDef("time", function(fieldValue, fieldParams)
  {
    return /^(0?[1-9]|1[0-2]):([0-5]\d)(:([0-5]\d))? ?(am|pm)?$/i.test(fieldValue) || /^(0?[1-9]|1[0-9]|2[0-4]):([0-5]\d)(:([0-5]\d))?$/i.test(fieldValue);
  }, "{display} must contain a valid time.", false)
  .addDef("url", function(fieldValue, fieldParams)
  {
    return /^(f|ht)tps?:\/\/\w+\.\S+(\.\w+)?$/i.test(fieldValue);
  }, "{display} must contain a valid URL.", false)
  .addDef("valuedependency", function(fieldValue, fieldParams)
  {
    if (fieldParams.length > 3)
      return !new RegExp(fieldParams[2], fieldParams[3]).test($.validator.getValue(fieldParams[0])) || !$.validator.isBlank(fieldValue);
  }, "{display} is required when {2} contains a specific value.", true);