// The core validation script library.
(function($){
  var config = {
    ajaxCache: {},
    validators: {},
    methods: {
      checked: function(required) {
        return {
          valid: !required || this.elem().attr('checked'),
          message: this.caption=="" ? "Required." : "'" + this.caption + "' is required."
        };
      },
      compare: function(id) {
        var val = this.elem().val();
        var compare = config.validators[id];
        return {
          valid: val == compare.elem().val(),
          message: "'" + this.caption + "' must match '" + compare.caption + "'."
        };
      },
      requiredIf: function(params) {
        var val = this.elem().val();
        var compareVal = config.validators[params.id].elem().val();
        var required = (params.value && compareVal == params.value) || (!params.value && compareVal.length > 0);
        return {
            valid: !required || this.elem().val().length > 0,
            message: this.caption == "" ? "Required." : "'" + this.caption + "' is required."
        };
      },
      requiredIfNot: function(params) {
        var val = this.elem().val();
        var compareVal = config.validators[params.id].elem().val();
        var required = compareVal.length == 0;
        return {
          valid: !required || this.elem().val().length > 0,
          message: this.caption == "" ? "Required." : "'" + this.caption + "' is required."
        };
      },
      creditCard: function() {
        var val = this.elem().val();
        if (val.length == 0)
          return { valid: true, message: '' };
        var stripped = val.replace(/[^\d]/g, "");
        var valid = (stripped.length>=13 && stripped.length<=16) || (stripped.length==4 && val.length>4);
        if (valid && !this.elem()[0].focused) {
          var formatting = stripped.length>4 ? stripped : 'xxxxxxxxxxxx'+stripped;
          var formatted = formatting.substring(0,4) + "-" +
            formatting.substring(4,8) + "-" +
            formatting.substring(8,12) + "-" +
            formatting.substring(12,16);
          if (val != formatted) this.elem().val(formatted);
        }
        return {
          valid: valid,
          message: "Enter a valid credit card number."
        };
      },
      currency: function() {
        var val = this.elem().val();
        if (val.length == 0)
          return { valid: true, message: '' };
        var stripped = val.replace(/[^\d\.]/g, "");
        var valid = stripped.length > 0;
        if (valid && !this.elem()[0].focused) {
          var formatted = $.corp.validation.formatCurrency(stripped);
          if (val != formatted) this.elem().val(formatted);
        }
        return {
          valid: valid,
          message: "Enter a dollar amount."
        };
      },
      date: function() {
        var val = this.elem().val();
        if (val.length == 0)
          return { valid: true, message: '' };
        var offset=Date.parse(val);
        return {
          valid: !isNaN(offset) && offset>=Date.parse('1/1/1900') && offset<Date.parse('1/1/2080'),
          message: "Enter a date in mm/dd/yyyy format."
        };
      },
      email: function() {
        var val = this.elem().val();
        if (val.length == 0)
          return { valid: true, message: '' };
        return {
          valid: /.+@.+\..+/.test(val),
          message: "Enter a valid email address."
        };
      },
      integer: function() {
        var val = this.elem().val();
        if (val.length == 0)
          return { valid: true, message: '' };
        var stripped = val.replace(/[^\d\.]/g, "").replace(/\.\d*/g, "");
        var valid = stripped.length > 0;
        if (valid && !this.elem()[0].focused) {
          if (val != stripped) this.elem().val(stripped);
        }
        return {
          valid: valid,
          message: "Enter a valid number."
        };
      },
      maxLength: function(maxLength) {
        var val = this.elem().val();
        if (val.length == 0)
          return { valid: true, message: '' };
        return {
          valid: val.length <= maxLength,
          message: "'" + this.caption + "' must be less than " + maxLength + " characters."
        };
      },
      minLength: function(minLength) {
        var val = this.elem().val();
        if (val.length == 0)
          return { valid: true, message: '' };
        return {
          valid: val.length >= minLength,
          message: "'" + this.caption + "' must be at least " + minLength + " characters."
        };
      },
      phone: function() {
        var val = this.elem().val();
        if (val.length == 0)
          return { valid: true, message: '' };
        var stripped = val.replace(/[^\d]/g, "");
        var valid = stripped.length == 10;
        if (valid && !this.elem()[0].focused) {
          var formatted = "(" + stripped.substring(0,3) + ") " +
            stripped.substring(3,6) + "-" +
            stripped.substring(6,10);
          if (val != formatted) this.elem().val(formatted);
        }
        return {
          valid: valid,
          message: "Enter a valid phone number."
        };
      },
      radioRequired: function(required) {
        return {
          valid: !required || this.elem().find("input[@checked]").length>0,
          message: "'" + this.caption + "' is required."
        };
      },
      required: function(required) {
        return {
          valid: !required || this.elem().val().length > 0,
          message: "'" + this.caption + "' is required."
        };
      },
      selectRequired: function(required) {
          return {
              valid: !required || this.elem()[0].selectedIndex > 0,
              message: "'" + this.caption + "' is required."
          };
      },
      ssn: function() {
        var val = this.elem().val();
        if (val.length == 0)
          return { valid: true, message: '' };
        var stripped = val.replace(/[^\d]/g, "");
        var valid = stripped.length == 9;
        if (valid && !this.elem()[0].focused) {
          var formatted = stripped.substring(0,3) + "-" +
            stripped.substring(3,5) + "-" +
            stripped.substring(5,9);
          if (val != formatted) this.elem().val(formatted);
        }
        return {
          valid: valid,
          message: "Enter a valid social security number."
        };
      },
      zip: function() {
        var val = this.elem().val();
        if (val.length == 0)
          return { valid: true, message: '' };
        return {
          valid: /^\d{5}$/.test(val),
          message: "Enter a five digit zip code."
        };
      }      
    }
  };
  // put everything in a corp namespace to avoid polution
  $.extend({
    corp: { 
      validation : {
        memoizedAjax: function (options) {
          var cache = config.ajaxCache;
          var key = [ options.url ];
          for (var prop in options.data) {
            key[key.length] = options.data[prop];
          }
          if(cache[key]) {
		        options.success(cache[key]);
		        return;
          }
          var success = options.success;
          var wrap = function (result) {
		        cache[key]=result;
            success(result);
          };
          options.success = wrap;
          $.ajax(options);
        },
				formatCurrency: function (num) {
					num = num.toString().replace(/\$|\,/g,'');
					if(isNaN(num))
						num = '0';
					var sign = (num == (num = Math.abs(num)));
					num = Math.floor(num*100+0.50000000001);
					var cents = num%100;
					num = Math.floor(num/100).toString();
					if(cents<10)
						cents = '0' + cents;
					for (var i = 0; i < Math.floor((num.length-(1+i))/3); i++)
						num = num.substring(0,num.length-(4*i+3))+','+ num.substring(num.length-(4*i+3));
					return (((sign)?'':'-') + '$' + num + '.' + cents);
				},
        init: function (options) {
          if (!options.enabled || !corp_validation) return;
          $.extend(config, options);
          $.each(corp_validation, function(i, obj) {
            var validator = {
              check: function () {
                this.dirty = true;
                var result = this.validate();
                if (! (result.valid === "ajax") ) // HACK: skip update for ajax callbacks
                  this.update(result);
                return result;
              },
              dirty: false,
              elem: function() {
                return $("#" + this.id);
              },
              listeners: [],
              methods: [],
              rules: {},
              update: function (result) {
                if (result.valid) {
                  this.elem().removeClass("error");
                } else {
                  this.elem().addClass("error");
                }
                for (var i = 0; i < this.listeners.length; ++i) {
                  this.listeners[i](result);
                }
              },
              validate: function() {
                if (!this.elem().attr('disabled')) {
                  for (var i = 0; i < this.methods.length; ++i) {
                    var result = this.methods[i].call(this);
                    if (! (result.valid === true) ) return result;
                  }
                }
                return { valid: true, message: "" };
              }
            };
            $.extend(validator, obj);
            $.each(validator.rules, function (method, param) {
              validator.methods[validator.methods.length] = function () {
                return config.methods[method].call(validator, param);
              };
            });
            validator.elem().blur(function () {
							this.focused = false;
              if (validator.dirty || validator.elem().val().length > 0)
                validator.check();
            });
            validator.elem().filter("select").change(function () {
              validator.check();
            });
            validator.elem().focus(function () {
							this.focused = true;
            });
            $('.novalidation').click(function() { $.corp.validation.config.enabled=false; } );
            validator.elem().keyup(function () {
              if (validator.dirty) {
                // wait a bit in case there's another key press
                if (validator.delayed) clearTimeout(validator.delayed);
                validator.delayed = setTimeout(function() {
                  validator.check();
                }, 400);
              }
            });
            if (config.validators[obj.id])
              $.merge(validator.methods, config.validators[obj.id].methods);
            config.validators[obj.id] = validator;
          });
          $("label").each(function(i, l) {
            var elem = $(l);
            var f = elem.attr("for");
            var c = elem.attr("class");
            var v = config.validators[f];
            if (v) {
              if (/indicator|caption/.test(c)) {
                  v.listeners[v.listeners.length] = function(result) {
                    elem.removeClass("required ok error").addClass(result.valid ? "ok" : "error");
                  };
              }
              if (/message/.test(c)) {
                v.listeners[v.listeners.length] = function(result) {
                  if (result.valid) {
                    elem.removeClass("error").find("span").text("");
                  } else {
                    elem.addClass("error").find("span").text(result.message);
                  }
                };
              }
            }
          });
          $.corp.validation.config=config;
        },
        validate: function () {
          var valid = true;
          var invalid = [];
          if(!config.enabled) return valid;
          $.each(config.validators, function(i, v) {
					  if(v.check) {
              var result = v.check();
              if (!result.valid) {
                valid = false;
                invalid[invalid.length] = v.caption;
              }
            }
          });
          if (config.summary) {
            $(config.summary).find("span").remove();
            if (valid) {
              $(config.summary).hide();
            } else {
              $(config.summary).show().append($("<span>'" + invalid.join("', '") + "'</span>"));
            }
          }
          if (valid && config.valid) return config.valid();
          return valid;
        }
      }
    }
  });

})(jQuery);
