|
|
@@ -1,5 +1,5 @@
|
|
|
/**
|
|
|
- * BootstrapValidator v0.2.3 (http://github.com/nghuuphuoc/bootstrapvalidator)
|
|
|
+ * BootstrapValidator v0.3.0 (http://github.com/nghuuphuoc/bootstrapvalidator)
|
|
|
*
|
|
|
* A jQuery plugin to validate form fields. Use with Bootstrap 3
|
|
|
*
|
|
|
@@ -13,16 +13,11 @@
|
|
|
this.$form = $(form);
|
|
|
this.options = $.extend({}, BootstrapValidator.DEFAULT_OPTIONS, options);
|
|
|
|
|
|
- if ('disabled' == this.options.live) {
|
|
|
- // Don't disable the submit buttons if the live validating is disabled
|
|
|
- this.options.submitButtons = null;
|
|
|
- }
|
|
|
+ // Array of deferred
|
|
|
+ this._dfds = {};
|
|
|
|
|
|
- this.invalidFields = {};
|
|
|
- this.xhrRequests = {};
|
|
|
- this.numPendingRequests = null;
|
|
|
- this.formSubmited = false;
|
|
|
- this.submitHandlerCalled = false;
|
|
|
+ // Invalid fields
|
|
|
+ this._invalidFields = {};
|
|
|
|
|
|
this._init();
|
|
|
};
|
|
|
@@ -35,25 +30,6 @@
|
|
|
// Default invalid message
|
|
|
message: 'This value is not valid',
|
|
|
|
|
|
- // The submit buttons selector
|
|
|
- // These buttons will be disabled when the form input are invalid
|
|
|
- submitButtons: 'button[type="submit"]',
|
|
|
-
|
|
|
- // The custom submit handler
|
|
|
- // It will prevent the form from the default submitting
|
|
|
- //
|
|
|
- // submitHandler: function(validator, form) {
|
|
|
- // - validator is the BootstrapValidator instance
|
|
|
- // - form is the jQuery object present the current form
|
|
|
- // }
|
|
|
- submitHandler: null,
|
|
|
-
|
|
|
- // Live validating. Can be one of 3 values:
|
|
|
- // - enabled: The plugin validates fields as soon as they are changed
|
|
|
- // - disabled: Disable the live validating. The error messages are only shown after the form is submitted
|
|
|
- // - submitted: The live validating is enabled after the form is submitted
|
|
|
- live: 'enabled',
|
|
|
-
|
|
|
// Map the field name with validator rules
|
|
|
fields: null
|
|
|
};
|
|
|
@@ -75,85 +51,13 @@
|
|
|
.attr('novalidate', 'novalidate')
|
|
|
.addClass(this.options.elementClass)
|
|
|
.on('submit', function(e) {
|
|
|
- that.formSubmited = true;
|
|
|
- if (that.options.fields) {
|
|
|
- for (var field in that.options.fields) {
|
|
|
- if (that.numPendingRequests > 0 || that.numPendingRequests == null) {
|
|
|
- // Check if the field is valid
|
|
|
- var $field = that.getFieldElement(field);
|
|
|
- if ($field && $field.data('bootstrapValidator.isValid') !== true) {
|
|
|
- that.validateField(field);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- if (!that.isValid()) {
|
|
|
- that.$form.find(that.options.submitButtons).attr('disabled', 'disabled');
|
|
|
- if ('submitted' == that.options.live) {
|
|
|
- that.options.live = 'enabled';
|
|
|
- that._setLiveValidating();
|
|
|
- }
|
|
|
-
|
|
|
- // Focus to the first invalid field
|
|
|
- for (var i in that.invalidFields) {
|
|
|
- that.getFieldElement(i).focus();
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- e.preventDefault();
|
|
|
- } else {
|
|
|
- if (!that.submitHandlerCalled && that.options.submitHandler && 'function' == typeof that.options.submitHandler) {
|
|
|
- // Avoid calling submitHandler recursively
|
|
|
- // in the case user call form.submit() right inside the submitHandler()
|
|
|
- that.submitHandlerCalled = true;
|
|
|
- that.options.submitHandler.call(that, that, that.$form);
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ e.preventDefault();
|
|
|
+ that.validate();
|
|
|
});
|
|
|
|
|
|
for (var field in this.options.fields) {
|
|
|
this._initField(field);
|
|
|
}
|
|
|
-
|
|
|
- this._setLiveValidating();
|
|
|
- },
|
|
|
-
|
|
|
- /**
|
|
|
- * Enable live validating
|
|
|
- */
|
|
|
- _setLiveValidating: function() {
|
|
|
- if ('enabled' == this.options.live) {
|
|
|
- var that = this;
|
|
|
- // Since this should be called once, I have to disable the live validating mode
|
|
|
- this.options.live = 'disabled';
|
|
|
-
|
|
|
- for (var field in this.options.fields) {
|
|
|
- (function(field) {
|
|
|
- var fields = that.getFieldElements(field);
|
|
|
-
|
|
|
- if (fields && fields.length > 0) {
|
|
|
- var $field = $(fields[0]);
|
|
|
- var type = $field.attr('type');
|
|
|
-
|
|
|
- if ('radio' == type) {
|
|
|
- var event = 'change';
|
|
|
- fields.on(event, function() {
|
|
|
- that.formSubmited = false;
|
|
|
- that.validateField(field);
|
|
|
- });
|
|
|
- } else {
|
|
|
- var event = ('checkbox' == type || 'SELECT' == $field.get(0).tagName) ? 'change' : 'keyup';
|
|
|
- $field.on(event, function() {
|
|
|
- that.formSubmited = false;
|
|
|
- that.validateField(field);
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- })(field);
|
|
|
- }
|
|
|
- }
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
@@ -166,17 +70,26 @@
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- var $field = this.getFieldElement(field);
|
|
|
- if (null == $field) {
|
|
|
+ this._dfds[field] = {};
|
|
|
+
|
|
|
+ var fields = this.$form.find('[name="' + field + '"]');
|
|
|
+ if (fields.length == 0) {
|
|
|
+ // We don't need to validate non-existing fields next time
|
|
|
+ delete this.options.fields[field];
|
|
|
+ delete this._dfds[field];
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// Create a help block element for showing the error
|
|
|
- var $parent = $field.parents('.form-group'),
|
|
|
+ var $field = $(fields[0]),
|
|
|
+ $parent = $field.parents('.form-group'),
|
|
|
helpBlock = $parent.find('.help-block');
|
|
|
|
|
|
if (helpBlock.length == 0) {
|
|
|
- var $small = $('<small/>').addClass('help-block').css('display', 'none').appendTo($parent);
|
|
|
+ var $small = $('<small/>')
|
|
|
+ .addClass('help-block')
|
|
|
+ .css('display', 'none')
|
|
|
+ .appendTo($parent);
|
|
|
$field.data('bootstrapValidator.error', $small);
|
|
|
|
|
|
// Calculate the number of columns of the label/field element
|
|
|
@@ -205,177 +118,142 @@
|
|
|
}
|
|
|
}
|
|
|
if (size && offset) {
|
|
|
- $small.addClass(['col-', size, '-offset-', offset].join(''))
|
|
|
- .addClass(['col-', size, '-', 12 - offset].join(''));
|
|
|
+ $small
|
|
|
+ .addClass(['col-', size, '-offset-', offset].join(''))
|
|
|
+ .addClass(['col-', size, '-', 12 - offset].join(''));
|
|
|
}
|
|
|
} else {
|
|
|
$field.data('bootstrapValidator.error', helpBlock.eq(0));
|
|
|
}
|
|
|
},
|
|
|
|
|
|
- /**
|
|
|
- * Get field element
|
|
|
- *
|
|
|
- * @param {String} field The field name
|
|
|
- * @returns {jQuery}
|
|
|
- */
|
|
|
- getFieldElement: function(field) {
|
|
|
- var fields = this.$form.find('[name="' + field + '"]');
|
|
|
- return (fields.length == 0) ? null : $(fields[0]);
|
|
|
- },
|
|
|
+ // --- Public methods ---
|
|
|
|
|
|
/**
|
|
|
- * Get field elements
|
|
|
- * Used for radios
|
|
|
+ * Retrieve the field elements by given name
|
|
|
*
|
|
|
* @param {String} field The field name
|
|
|
- * @returns {jQuery}
|
|
|
+ * @returns {null|jQuery[]}
|
|
|
*/
|
|
|
getFieldElements: function(field) {
|
|
|
var fields = this.$form.find('[name="' + field + '"]');
|
|
|
return (fields.length == 0) ? null : fields;
|
|
|
},
|
|
|
|
|
|
-
|
|
|
/**
|
|
|
- * Validate given field
|
|
|
- *
|
|
|
- * @param {String} field The field name
|
|
|
+ * Validate the form
|
|
|
*/
|
|
|
- validateField: function(field) {
|
|
|
- var $field = this.getFieldElement(field);
|
|
|
- if (null == $field) {
|
|
|
- // Return if cannot find the field with given name
|
|
|
+ validate: function() {
|
|
|
+ // Reset invalid fields
|
|
|
+ this._invalidFields = {};
|
|
|
+ if (!this.options.fields) {
|
|
|
return;
|
|
|
}
|
|
|
- var that = this,
|
|
|
- validators = that.options.fields[field].validators;
|
|
|
- for (var validatorName in validators) {
|
|
|
- if (!$.fn.bootstrapValidator.validators[validatorName]) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- var isValid = $.fn.bootstrapValidator.validators[validatorName].validate(that, $field, validators[validatorName]);
|
|
|
- if (isValid === false) {
|
|
|
- that.showError($field, validatorName);
|
|
|
- break;
|
|
|
- } else if (isValid === true) {
|
|
|
- that.removeError($field);
|
|
|
- }
|
|
|
+ for (var field in this.options.fields) {
|
|
|
+ this.validateField(field);
|
|
|
}
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
- * Show field error
|
|
|
+ * Validate given field
|
|
|
*
|
|
|
- * @param {jQuery} $field The field element
|
|
|
- * @param {String} validatorName
|
|
|
+ * @param {String} field The field name
|
|
|
*/
|
|
|
- showError: function($field, validatorName) {
|
|
|
- var field = $field.attr('name'),
|
|
|
- validator = this.options.fields[field].validators[validatorName],
|
|
|
- message = validator.message || this.options.message,
|
|
|
- $parent = $field.parents('.form-group');
|
|
|
-
|
|
|
- this.invalidFields[field] = true;
|
|
|
+ validateField: function(field) {
|
|
|
+ var that = this,
|
|
|
+ fields = this.$form.find('[name="' + field + '"]'),
|
|
|
+ $field = $(fields[0]),
|
|
|
+ validators = this.options.fields[field].validators,
|
|
|
+ validatorName,
|
|
|
+ validateResult;
|
|
|
+ for (validatorName in validators) {
|
|
|
+ if (this._invalidFields[field]) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
|
|
|
- // Add has-error class to parent element
|
|
|
- $parent.removeClass('has-success').addClass('has-error');
|
|
|
+ // Continue if the validator with given name doesn't exist
|
|
|
+ if (!$.fn.bootstrapValidator.validators[validatorName]) {
|
|
|
+ delete this.options.fields[field].validators[validatorName];
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- $field.data('bootstrapValidator.error').html(message).show();
|
|
|
+ if (this._dfds[field][validatorName]) {
|
|
|
+ this._dfds[field][validatorName].reject();
|
|
|
+ }
|
|
|
|
|
|
- this.$form.find(this.options.submitButtons).attr('disabled', 'disabled');
|
|
|
- },
|
|
|
+ validateResult = $.fn.bootstrapValidator.validators[validatorName].validate(this, $field, validators[validatorName]);
|
|
|
+ if ('object' == typeof validateResult) {
|
|
|
+ this._dfds[field][validatorName] = validateResult;
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * Remove error from given field
|
|
|
- *
|
|
|
- * @param {jQuery} $field The field element
|
|
|
- */
|
|
|
- removeError: function($field) {
|
|
|
- delete this.invalidFields[$field.attr('name')];
|
|
|
- $field.parents('.form-group').removeClass('has-error').addClass('has-success');
|
|
|
- $field.data('bootstrapValidator.error').hide();
|
|
|
- this.$form.find(this.options.submitButtons).removeAttr('disabled');
|
|
|
+ $.when(validateResult).then(function(isValid) {
|
|
|
+ delete that._dfds[field][validatorName];
|
|
|
+ if (isValid) {
|
|
|
+ that.removeError($field);
|
|
|
+ } else {
|
|
|
+ that._invalidFields[field] = true;
|
|
|
+ that.showError($field, validatorName);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
- * Start remote checking
|
|
|
+ * Check the form validity
|
|
|
*
|
|
|
- * @param {jQuery} $field The field element
|
|
|
- * @param {String} validatorName
|
|
|
- * @param {XMLHttpRequest} xhr
|
|
|
+ * @returns {Boolean}
|
|
|
*/
|
|
|
- startRequest: function($field, validatorName, xhr) {
|
|
|
- var field = $field.attr('name');
|
|
|
-
|
|
|
- $field.data('bootstrapValidator.isValid', false);
|
|
|
- this.$form.find(this.options.submitButtons).attr('disabled', 'disabled');
|
|
|
-
|
|
|
- if(this.numPendingRequests == null){
|
|
|
- this.numPendingRequests = 0;
|
|
|
+ isValid: function() {
|
|
|
+ var field, validatorName;
|
|
|
+ for (field in this._invalidFields) {
|
|
|
+ return false;
|
|
|
}
|
|
|
- this.numPendingRequests++;
|
|
|
- // Abort the previous request
|
|
|
- if (!this.xhrRequests[field]) {
|
|
|
- this.xhrRequests[field] = {};
|
|
|
+ for (field in this._dfds) {
|
|
|
+ for (validatorName in this._dfds[field]) {
|
|
|
+ if ('pending' == this._dfds[field][validatorName].state()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- if (this.xhrRequests[field][validatorName]) {
|
|
|
- this.numPendingRequests--;
|
|
|
- this.xhrRequests[field][validatorName].abort();
|
|
|
- }
|
|
|
- this.xhrRequests[field][validatorName] = xhr;
|
|
|
+ return true;
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
- * Complete remote checking
|
|
|
+ * Show field error
|
|
|
*
|
|
|
* @param {jQuery} $field The field element
|
|
|
* @param {String} validatorName
|
|
|
- * @param {Boolean} isValid
|
|
|
*/
|
|
|
- completeRequest: function($field, validatorName, isValid) {
|
|
|
- if (isValid === false) {
|
|
|
- this.showError($field, validatorName);
|
|
|
- } else if (isValid === true) {
|
|
|
- this.removeError($field);
|
|
|
- $field.data('bootstrapValidator.isValid', true);
|
|
|
- }
|
|
|
-
|
|
|
- var field = $field.attr('name');
|
|
|
-
|
|
|
- delete this.xhrRequests[field][validatorName];
|
|
|
+ showError: function($field, validatorName) {
|
|
|
+ var field = $field.attr('name'),
|
|
|
+ validator = this.options.fields[field].validators[validatorName],
|
|
|
+ message = validator.message || this.options.message;
|
|
|
|
|
|
- this.numPendingRequests--;
|
|
|
- if (this.numPendingRequests <= 0) {
|
|
|
- this.numPendingRequests = 0;
|
|
|
- if (this.formSubmited) {
|
|
|
- if (!this.submitHandlerCalled && this.options.submitHandler && 'function' == typeof this.options.submitHandler) {
|
|
|
- this.submitHandlerCalled = true;
|
|
|
- this.options.submitHandler.call(this, this, this.$form);
|
|
|
- } else {
|
|
|
- this.$form.submit();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ // Add has-error class to parent element
|
|
|
+ $field
|
|
|
+ .parents('.form-group')
|
|
|
+ .removeClass('has-success')
|
|
|
+ .addClass('has-error')
|
|
|
+ .end()
|
|
|
+ .data('bootstrapValidator.error')
|
|
|
+ .html(message)
|
|
|
+ .show();
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
- * Check the form validity
|
|
|
+ * Remove error from given field
|
|
|
*
|
|
|
- * @returns {Boolean}
|
|
|
+ * @param {jQuery} $field The field element
|
|
|
*/
|
|
|
- isValid: function() {
|
|
|
- if (this.numPendingRequests > 0) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- for (var field in this.invalidFields) {
|
|
|
- // Don't validate disabled element
|
|
|
- if (this.invalidFields[field] && !this.getFieldElement(field).is(':disabled')) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
- return true;
|
|
|
+ removeError: function($field) {
|
|
|
+ $field
|
|
|
+ .parents('.form-group')
|
|
|
+ .removeClass('has-error')
|
|
|
+ .addClass('has-success')
|
|
|
+ .end()
|
|
|
+ .data('bootstrapValidator.error')
|
|
|
+ .hide();
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -384,7 +262,7 @@
|
|
|
return this.each(function() {
|
|
|
var $this = $(this), data = $this.data('bootstrapValidator');
|
|
|
if (!data) {
|
|
|
- $this.data('bootstrapValidator', (data = new BootstrapValidator(this, options)));
|
|
|
+ $this.data('bootstrapValidator', new BootstrapValidator(this, options));
|
|
|
}
|
|
|
});
|
|
|
};
|
|
|
@@ -435,12 +313,14 @@
|
|
|
* // validator is instance of BootstrapValidator
|
|
|
* }
|
|
|
* - message: The invalid message
|
|
|
- * @returns {Boolean}
|
|
|
+ * @returns {Boolean|Deferred}
|
|
|
*/
|
|
|
validate: function(validator, $field, options) {
|
|
|
var value = $field.val();
|
|
|
if (options.callback && 'function' == typeof options.callback) {
|
|
|
- return options.callback.call(this, value, this);
|
|
|
+ var dfd = new $.Deferred();
|
|
|
+ dfd.resolve(options.callback.call(this, value, validator));
|
|
|
+ return dfd;
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
@@ -665,15 +545,15 @@
|
|
|
* @returns {Boolean}
|
|
|
*/
|
|
|
validate: function(validator, $field, options) {
|
|
|
-
|
|
|
-
|
|
|
var type = $field.attr('type');
|
|
|
- if('radio' == type) {
|
|
|
- var radioSelector = "input[name=" + $field.attr('name') + "]:checked";
|
|
|
- return ($(radioSelector).length > 0);
|
|
|
+ if ('radio' == type || 'checkbox' == type) {
|
|
|
+ return validator
|
|
|
+ .getFieldElements($field.attr('name'))
|
|
|
+ .filter(':checked')
|
|
|
+ .length > 0;
|
|
|
}
|
|
|
|
|
|
- return ('checkbox' == type) ? $field.is(':checked') : ($.trim($field.val()) != '');
|
|
|
+ return $.trim($field.val()) != '';
|
|
|
}
|
|
|
};
|
|
|
}(window.jQuery));
|
|
|
@@ -712,7 +592,7 @@
|
|
|
* <fieldName>: <fieldValue>
|
|
|
* }
|
|
|
* - message: The invalid message
|
|
|
- * @returns {Boolean|String}
|
|
|
+ * @returns {Boolean|Deferred}
|
|
|
*/
|
|
|
validate: function(validator, $field, options) {
|
|
|
var value = $field.val();
|
|
|
@@ -725,18 +605,23 @@
|
|
|
data = {};
|
|
|
}
|
|
|
data[name] = value;
|
|
|
+
|
|
|
+ var dfd = new $.Deferred();
|
|
|
var xhr = $.ajax({
|
|
|
type: 'POST',
|
|
|
url: options.url,
|
|
|
dataType: 'json',
|
|
|
data: data
|
|
|
- }).success(function(response) {
|
|
|
- var isValid = response.valid === true || response.valid === 'true';
|
|
|
- validator.completeRequest($field, 'remote', isValid);
|
|
|
});
|
|
|
- validator.startRequest($field, 'remote', xhr);
|
|
|
+ xhr.then(function(response) {
|
|
|
+ dfd.resolve(response.valid === true || response.valid === 'true');
|
|
|
+ });
|
|
|
+
|
|
|
+ dfd.fail(function() {
|
|
|
+ xhr.abort();
|
|
|
+ });
|
|
|
|
|
|
- return 'pending';
|
|
|
+ return dfd;
|
|
|
}
|
|
|
};
|
|
|
}(window.jQuery));
|