(function() {
  var $, plugin, validateFns;
  $ = jQuery;
  validateFns = {
    alphanum: function(val, next) {
      if (next == null) {
        next = function() {};
      }
      if (val.match(/^[a-z0-9 \-_'\.]+$/i)) {
        next(null, val);
        return val;
      }
      next(new Error('Letters or numbers only'));
      return false;
    },
    required: function(val, next) {
      if (next == null) {
        next = function() {};
      }
      if (!_.isEmpty(val.trim())) {
        next(null, val);
        return val;
      }
      next(new Error('Required'));
      return false;
    },
    phone: function(val, next) {
      if (next == null) {
        next = function() {};
      }
      val = val.replace(/[^0-9]+/g, '');
      if (val.length === 10) {
        next(null, val);
        return val;
      }
      next(new Error('Invalid phone. Please include area code.'));
      return false;
    },
    email: function(val, next) {
      if (next == null) {
        next = function() {};
      }
      if (val.match(/^([0-9a-zA-Z]([-\.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$/)) {
        next(null, val);
        return val;
      }
      next(new Error('Invalid email'));
      return false;
    },
    website: function(val, next) {
      if (next == null) {
        next = function() {};
      }
      if (val.match(/^(http:\/\/)*([a-z0-9_-]+\.)*[a-z0-9_-]+(\.[a-z]{2,6}){1,2}$/i)) {
        next(null, val);
        return val;
      }
      next(new Error('Invalid url'));
      return false;
    },
    number: function(val, next) {
      if (next == null) {
        next = function() {};
      }
      if (val.match(/[0-9\,\.]+/)) {
        next(null, val);
        return val;
      }
      next(new Error('Invalid number'));
      return false;
    }
  };
  plugin = {
    init: function(options) {
      var modifyEl, tipsyOpts;
      if (options == null) {
        options = {};
      }
      tipsyOpts = {
        className: 'error',
        fade: true,
        html: true,
        trigger: 'manual',
        gravity: 'w'
      };
      modifyEl = function(el) {
        var $el, name, type;
        $el = $(el);
        name = $el.attr('name');
        type = $el.attr('type');
        if (type === 'radio' || type === 'checkbox') {
          $el = $('[name="' + name + '"]');
        }
        if ($el.hasClass('ui-plugin-validate')) {
          return;
        }
        $el.addClass('ui-plugin-validate').data('ui-plugin-validate', options).tipsy(tipsyOpts).bind('keyup focus change click', function() {
          if (type === 'radio') {
            $('.ui-error[name="' + $el.attr('name') + '"]').tipsy('hide');
          } else {
            $(this).tipsy('hide');
          }
        });
      };
      tipsyOpts = _.extend(tipsyOpts, options);
      return this.each(function() {
        var $t, el, tag, _i, _len, _ref, _ref2;
        $t = $(this);
        tag = (_ref = $t.get(0).tagName) != null ? _ref.toLowerCase() : void 0;
        if (tag.match(/input|textarea|select/)) {
          modifyEl($t);
        } else {
          _ref2 = $t.find('input[class*="validate-"], textarea[class*="validate-"], select[class*="validate-"]');
          for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
            el = _ref2[_i];
            modifyEl(el);
          }
        }
      });
    },
    /*
    	Hides errors.
    	*/
    hide: function() {
      return this.each(function() {
        var $t, el, tag, _i, _len, _ref, _ref2;
        $t = $(this);
        tag = (_ref = $t.get(0).tagName) != null ? _ref.toLowerCase() : void 0;
        if (tag.match(/input|textarea|select/)) {
          $t.tipsy('hide');
        } else {
          _ref2 = $t.find('input[class*="validate-"], textarea[class*="validate-"], select[class*="validate-"]');
          for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
            el = _ref2[_i];
            $(el).tipsy('hide');
          }
        }
      });
    },
    /*
    	Initiates validation.
    	@param {Function} next - Passes errors to this when validation is complete
    	@param {Object} validateFn - Hash of different validate functions
    	*/
    validate: function(next, validateFn) {
      var errors, self, validateEl;
      if (next == null) {
        next = (function() {});
      }
      if (validateFn == null) {
        validateFn = {};
      }
      self = this;
      errors = {};
      validateEl = function(el, next) {
        var $checked, $el, name, testFn, type, v, validateTypes, _i, _len;
        if (next == null) {
          next = function() {};
        }
        $el = $(el);
        name = $el.attr('name');
        type = $el.attr('type');
        validateTypes = $el.attr('class').match(/validate-([a-z0-9\-]+)/);
        errors = {};
        if (!validateTypes) {
          return false;
        } else {
          validateTypes = validateTypes[1].split('-').reverse();
        }
        if (!$el.hasClass('ui-plugin-validate')) {
          plugin.init.call(self);
        }
        for (_i = 0, _len = validateTypes.length; _i < _len; _i++) {
          v = validateTypes[_i];
          if (typeof validateFn[v] === 'function') {
            validateFn[v]($el.val(), function(err) {
              if (err) {
                errors[name] = err;
                next(err);
                return;
              }
              next();
            });
          } else if (type === 'radio' && v === 'required') {
            $checked = $('[name="' + name + '"][type="radio"]:checked', $el.parent());
            if (!$checked.length) {
              $.validate.error($('[name="' + name + '"]:last', $el.parent()).get(0), 'Required');
              errors[name] = new Error('required');
              next(new Error('required'));
            } else {
              next(null);
            }
          } else if (typeof validateFns[v] === 'function') {
            if (!$el.is(':visible')) {
              next(null);
              return;
            }
            if ($el.val() === '' && v !== 'required' && validateTypes.indexOf('required') === -1) {
              next(null);
              return;
            }
            testFn = validateFns[v];
            testFn($el.val(), function(err) {
              if (err) {
                errors[name] = err;
                $.validate.error($el, err.message);
                next(err);
                return;
              }
              next();
            });
          }
        }
      };
      plugin.hide.call(self);
      return this.each(function() {
        var $els, $t, tag, _ref;
        $t = $(this);
        tag = (_ref = $t.get(0).tagName) != null ? _ref.toLowerCase() : void 0;
        if (tag.match(/input|textarea|select/)) {
          validateEl($t, function(err) {
            if (err) {
              errors[$t.attr('name')] = err;
              next(errors);
              return;
            }
            next();
          });
        } else {
          $els = $t.find('input[class*="validate-"], textarea[class*="validate-"], select[class*="validate-"]');
          async.forEach($els, (function(el, cb) {
            validateEl(el, cb);
          }), (function(err) {
            if (_.isEmpty(errors)) {
              errors = null;
            }
            next(errors);
          }));
        }
      });
    }
  };
  $.validate = {
    error: function(el, msg, opts) {
      var $el, defaultOpts;
      if (opts == null) {
        opts = {};
      }
      $el = $(el);
      $el.attr('title', msg);
      defaultOpts = {
        className: 'error',
        gravity: 'w',
        trigger: 'manual'
      };
      opts = _.extend(defaultOpts, opts);
      return $el.tipsy(opts).addClass('ui-error').tipsy('show');
    }
  };
  $.fn.validate = function(method) {
    if (typeof plugin[method] === 'function') {
      return plugin[method].apply(this, _.toArray(arguments).slice(1));
    } else if (typeof method === 'object' || !method) {
      return plugin.init.apply(this, arguments);
    } else {
      $.error("static/vendor/root/validate - " + method + " is not a valid method");
    }
    return false;
  };
}).call(this);

