diff --git a/js/validator.js b/js/validator.js index 78b0bde..a7e5150 100644 --- a/js/validator.js +++ b/js/validator.js @@ -40,12 +40,13 @@ } var Validator = function (element, options) { - this.options = options - this.validators = $.extend({}, Validator.VALIDATORS, options.custom) - this.$element = $(element) - this.$btn = $('button[type="submit"], input[type="submit"]') - .filter('[form="' + this.$element.attr('id') + '"]') - .add(this.$element.find('input[type="submit"], button[type="submit"]')) + this.options = options + this.validators = $.extend({}, Validator.VALIDATORS, options.custom) + this.deferredValidators = $.extend({}, Validator.DEFERRED_VALIDATORS, options.customDeferred) + this.$element = $(element) + this.$btn = $('button[type="submit"], input[type="submit"]') + .filter('[form="' + this.$element.attr('id') + '"]') + .add(this.$element.find('input[type="submit"], button[type="submit"]')) this.update() @@ -82,6 +83,7 @@ disable: true, focus: true, custom: {}, + customDeferred: {}, errors: { match: 'Does not match', minlength: 'Not long enough' @@ -109,6 +111,18 @@ } } + Validator.DEFERRED_VALIDATORS = { + 'remote': function ($el) { + var deferred = $.Deferred() + var data = {} + data[$el.attr('name')] = getValue($el) + $.get($el.attr('data-remote'), data) + .fail(function (jqXHR, textStatus, error) { deferred.reject(error) }) + .done(function (jqXHR, textStatus) { deferred.resolve() }) + return deferred + } + } + Validator.prototype.update = function () { var self = this @@ -124,18 +138,16 @@ } Validator.prototype.onInput = function (e) { - var self = this - var $el = $(e.target) - var deferErrors = e.type !== 'focusout' + var self = this + var $el = $(e.target) + var immediate = e.type === 'focusout' if (!this.$inputs.is($el)) return - this.validateInput($el, deferErrors).done(function () { - self.toggleSubmit() - }) + self.defer($el, self.validateInput, immediate) } - Validator.prototype.validateInput = function ($el, deferErrors) { + Validator.prototype.validateInput = function ($el) { var value = getValue($el) var prevErrors = $el.data('bs.validator.errors') @@ -151,7 +163,7 @@ $el.data('bs.validator.errors', errors) errors.length - ? deferErrors ? self.defer($el, self.showErrors) : self.showErrors($el) + ? self.showErrors($el) : self.clearErrors($el) if (!prevErrors || errors.toString() !== prevErrors.toString()) { @@ -211,14 +223,21 @@ } }, this)) - if (!errors.length && getValue($el) && $el.attr('data-remote')) { - this.defer($el, function () { - var data = {} - data[$el.attr('name')] = getValue($el) - $.get($el.attr('data-remote'), data) - .fail(function (jqXHR, textStatus, error) { errors.push(getErrorMessage('remote') || error) }) - .always(function () { deferred.resolve(errors)}) - }) + if (!errors.length && getValue($el)) { + var self = this + var deferredErrors = [] + $.each(self.deferredValidators, $.proxy(function (key, validator) { + if ($el.attr('data-' + key) === undefined) { + return + } + deferredErrors.push(validator.call(self, $el) + .fail(function (error) { + error = getErrorMessage(key) || error + !~errors.indexOf(error) && errors.push(error) + })) + }, self)) + $.when.apply($, deferredErrors) + .always(function () { deferred.resolve(errors) }) } else deferred.resolve(errors) return deferred.promise() @@ -313,10 +332,10 @@ this.$btn.toggleClass('disabled', this.isIncomplete() || this.hasErrors()) } - Validator.prototype.defer = function ($el, callback) { + Validator.prototype.defer = function ($el, callback, immediate) { callback = $.proxy(callback, this, $el) - if (!this.options.delay) return callback() window.clearTimeout($el.data('bs.validator.timeout')) + if (!this.options.delay || immediate) return callback() $el.data('bs.validator.timeout', window.setTimeout(callback, this.options.delay)) } @@ -361,11 +380,12 @@ this.$inputs .off('.bs.validator') - this.options = null - this.validators = null - this.$element = null - this.$btn = null - this.$inputs = null + this.options = null + this.validators = null + this.deferredValidators = null + this.$element = null + this.$btn = null + this.$inputs = null return this }