Browse Source

Merge branch 'master' into v0.5.0

Conflicts:
	CHANGELOG.md
	bootstrapValidator.jquery.json
	bower.json
	dist/css/bootstrapValidator.min.css
	dist/js/bootstrapValidator.js
	dist/js/bootstrapValidator.min.js
	package.json
	src/js/bootstrapValidator.js
nghuuphuoc 11 years ago
parent
commit
4c30ea11c2

+ 1 - 1
bootstrapValidator.jquery.json

@@ -1,6 +1,6 @@
 {
     "name": "bootstrapValidator",
-    "version": "0.4.3-dev",
+    "version": "0.5.0-dev",
     "title": "BootstrapValidator",
     "author": {
         "name": "Nguyen Huu Phuoc",

+ 1 - 1
bower.json

@@ -1,7 +1,7 @@
 {
     "name": "bootstrapValidator",
     "description": "The best jQuery plugin to validate form fields. Designed to use with Bootstrap 3",
-    "version": "0.4.3-dev",
+    "version": "0.5.0-dev",
     "main": [
         "dist/css/bootstrapValidator.css",
         "dist/js/bootstrapValidator.js"

+ 1 - 1
dist/css/bootstrapValidator.min.css

@@ -3,7 +3,7 @@
  *
  * A jQuery plugin to validate form fields. Use with Bootstrap 3
  *
- * @version     v0.4.5-dev
+ * @version     v0.5.0-dev
  * @author      https://twitter.com/nghuuphuoc
  * @copyright   (c) 2013 - 2014 Nguyen Huu Phuoc
  * @license     MIT

+ 213 - 140
dist/js/bootstrapValidator.js

@@ -3,7 +3,7 @@
  *
  * A jQuery plugin to validate form fields. Use with Bootstrap 3
  *
- * @version     v0.4.5-dev
+ * @version     v0.5.0-dev
  * @author      https://twitter.com/nghuuphuoc
  * @copyright   (c) 2013 - 2014 Nguyen Huu Phuoc
  * @license     MIT
@@ -40,6 +40,9 @@
         // The flag to indicate that the form is ready to submit when a remote/callback validator returns
         this._submitIfValid = null;
 
+        // Field elements
+        this._cacheFields = {};
+
         this._init();
     };
 
@@ -251,7 +254,7 @@
             var fields = this.getFieldElements(field);
 
             // We don't need to validate non-existing fields
-            if (fields == null) {
+            if (fields == []) {
                 delete this.options.fields[field];
                 return;
             }
@@ -260,66 +263,91 @@
                     delete this.options.fields[field].validators[validatorName];
                 }
             }
+            if (this.options.fields[field]['enabled'] == null) {
+                this.options.fields[field]['enabled'] = true;
+            }
 
+            for (var i = 0; i < fields.length; i++) {
+                this._initFieldElement($(fields[i]));
+            }
+        },
+
+        _initFieldElement: function($field) {
             var that      = this,
-                type      = fields.attr('type'),
-                event     = ('radio' == type || 'checkbox' == type || 'file' == type || 'SELECT' == fields[0].tagName) ? 'change' : that._changeEvent,
+                field     = $field.attr('name') || $field.attr('data-bv-field'),
+                fields    = this.getFieldElements(field),
+                index     = fields.index($field),
+                type      = $field.attr('type'),
                 total     = fields.length,
-                updateAll = (total == 1) || ('radio' == type) || ('checkbox' == type);
-
-            for (var i = 0; i < total; i++) {
-                var $field   = $(fields[i]),
-                    $parent  = $field.parents('.form-group'),
-                    // Allow user to indicate where the error messages are shown
-                    $message = this.options.fields[field].container ? $parent.find(this.options.fields[field].container) : this._getMessageContainer($field);
-
-                // Set the attribute to indicate the fields which are defined by selector
-                if (!$field.attr('data-bv-field')) {
-                    $field.attr('data-bv-field', field);
-                }
-
-                // Whenever the user change the field value, mark it as not validated yet
-                $field.on(event + '.update.bv', function() {
-                    // Reset the flag
-                    that._submitIfValid = false;
-                    updateAll ? that.updateStatus(field, that.STATUS_NOT_VALIDATED, null)
-                              : that.updateElementStatus($(this), that.STATUS_NOT_VALIDATED, null);
-                });
+                updateAll = (total == 1) || ('radio' == type) || ('checkbox' == type),
+                $parent   = $field.parents('.form-group'),
+                // Allow user to indicate where the error messages are shown
+                $message  = this.options.fields[field].container ? $parent.find(this.options.fields[field].container) : this._getMessageContainer($field);
+
+            // Remove all error messages and feedback icons
+            $message.find('.help-block[data-bv-validator]').remove();
+            $parent.find('i[data-bv-field]').remove();
+
+            // Set the attribute to indicate the fields which are defined by selector
+            if (!$field.attr('data-bv-field')) {
+                $field.attr('data-bv-field', field);
+            }
+
+            // Whenever the user change the field value, mark it as not validated yet
+            var event = ('radio' == type || 'checkbox' == type || 'file' == type || 'SELECT' == $field.get(0).tagName) ? 'change' : this._changeEvent;
+            $field.off(event + '.update.bv').on(event + '.update.bv', function() {
+                // Reset the flag
+                that._submitIfValid = false;
+                that.updateElementStatus($(this), that.STATUS_NOT_VALIDATED);
+            });
 
-                // Create help block elements for showing the error messages
-                $field.data('bv.messages', $message);
-                for (validatorName in this.options.fields[field].validators) {
-                    $field.data('bv.result.' + validatorName, this.STATUS_NOT_VALIDATED);
-
-                    if (!updateAll || i == total - 1) {
-                        $('<small/>')
-                            .css('display', 'none')
-                            .attr('data-bv-validator', validatorName)
-                            .attr('data-bv-validator-for', field)
-                            .html(this.options.fields[field].validators[validatorName].message || this.options.fields[field].message || this.options.message)
-                            .addClass('help-block')
-                            .appendTo($message);
-                    }
+            // Create help block elements for showing the error messages
+            $field.data('bv.messages', $message);
+            for (var validatorName in this.options.fields[field].validators) {
+                $field.data('bv.result.' + validatorName, this.STATUS_NOT_VALIDATED);
+
+                if (!updateAll || index == total - 1) {
+                    $('<small/>')
+                        .css('display', 'none')
+                        .attr('data-bv-validator', validatorName)
+                        .html(this.options.fields[field].validators[validatorName].message || this.options.fields[field].message || this.options.message)
+                        .addClass('help-block')
+                        .appendTo($message);
                 }
+            }
 
-                // Prepare the feedback icons
-                // Available from Bootstrap 3.1 (http://getbootstrap.com/css/#forms-control-validation)
-                if (this.options.feedbackIcons
-                    && this.options.feedbackIcons.validating && this.options.feedbackIcons.invalid && this.options.feedbackIcons.valid
-                    && (!updateAll || i == total - 1))
-                {
-                    $parent.addClass('has-feedback');
-                    var $icon = $('<i/>').css('display', 'none').addClass('form-control-feedback').attr('data-bv-field', field).insertAfter($field);
-                    // The feedback icon does not render correctly if there is no label
-                    // https://github.com/twbs/bootstrap/issues/12873
-                    if ($parent.find('label').length == 0) {
-                        $icon.css('top', 0);
-                    }
+            // Prepare the feedback icons
+            // Available from Bootstrap 3.1 (http://getbootstrap.com/css/#forms-control-validation)
+            if (this.options.feedbackIcons
+                && this.options.feedbackIcons.validating && this.options.feedbackIcons.invalid && this.options.feedbackIcons.valid
+                && (!updateAll || index == total - 1))
+            {
+                $parent.removeClass('has-success').removeClass('has-error').addClass('has-feedback');
+                var $icon = $('<i/>').css('display', 'none').addClass('form-control-feedback').attr('data-bv-field', field).insertAfter($field);
+                // The feedback icon does not render correctly if there is no label
+                // https://github.com/twbs/bootstrap/issues/12873
+                if ($parent.find('label').length == 0) {
+                    $icon.css('top', 0);
                 }
             }
 
-            if (this.options.fields[field]['enabled'] == null) {
-                this.options.fields[field]['enabled'] = true;
+            // Set live mode
+            var trigger = this.options.fields[field].trigger || this.options.trigger || event,
+                events  = $.map(trigger.split(' '), function(item) {
+                    return item + '.live.bv';
+                }).join(' ');
+            switch (this.options.live) {
+                case 'submitted':
+                    break;
+                case 'disabled':
+                    $field.off(events);
+                    break;
+                case 'enabled':
+                default:
+                    $field.off(events).on(events, function() {
+                        that.validateFieldElement($(this));
+                    });
+                    break;
             }
         },
 
@@ -355,32 +383,16 @@
          * Called when all validations are completed
          */
         _submit: function() {
-            if (!this.isValid()) {
-                if ('submitted' == this.options.live) {
-                    this.setLiveMode('enabled');
-                }
+            var isValid   = this.isValid(),
+                eventType = isValid ? 'success.form.bv' : 'error.form.bv',
+                e         = $.Event(eventType);
 
-                // Focus to the first invalid field
-                if (this.$invalidField) {
-                    // Activate the tab containing the invalid field if exists
-                    var $tab = this.$invalidField.parents('.tab-pane'),
-                        tabId;
-                    if ($tab && (tabId = $tab.attr('id'))) {
-                        $('a[href="#' + tabId + '"][data-toggle="tab"]').trigger('click.bs.tab.data-api');
-                    }
+            this.$form.trigger(e);
 
-                    this.$invalidField.focus();
-                }
-
-                return;
-            }
-
-            // Call the custom submission if enabled
-            if (this.options.submitHandler && 'function' == typeof this.options.submitHandler) {
-                // If you want to submit the form inside your submit handler, please call defaultSubmit() method
-                this.options.submitHandler.call(this, this, this.$form, this.$submitButton);
-            } else {
-                this.disableSubmitButtons(true).defaultSubmit();
+            // Call default handler
+            // Check if whether the submit button is clicked
+            if (this.$submitButton) {
+                isValid ? this._onSuccess(e) : this._onError(e);
             }
         },
 
@@ -404,22 +416,76 @@
 
             return false;
         },
+        
+        // --- Events ---
 
         /**
-         * Check if the number of characters of field value exceed the threshold or not
+         * The default handler of error.form.bv event.
+         * It will be called when there is a invalid field
          *
-         * @param {jQuery} $field The field element
-         * @returns {Boolean}
+         * @param {jQuery.Event} e The jQuery event object
          */
-        _exceedThreshold: function($field) {
-            var field     = $field.attr('data-bv-field'),
-                threshold = this.options.fields[field].threshold || this.options.threshold;
-            if (!threshold) {
-                return true;
+        _onError: function(e) {
+            if (e.isDefaultPrevented()) {
+                return;
+            }
+
+            if ('submitted' == this.options.live) {
+                // Enable live mode
+                this.options.live = 'enabled';
+                var that = this;
+                for (var field in this.options.fields) {
+                    (function(f) {
+                        var fields  = that.getFieldElements(f);
+                        if (fields.length) {
+                            var type    = $(fields[0]).attr('type'),
+                                event   = ('radio' == type || 'checkbox' == type || 'file' == type || 'SELECT' == $(fields[0]).get(0).tagName) ? 'change' : that._changeEvent,
+                                trigger = that.options.fields[field].trigger || that.options.trigger || event,
+                                events  = $.map(trigger.split(' '), function(item) {
+                                    return item + '.live.bv';
+                                }).join(' ');
+
+                            for (var i = 0; i < fields.length; i++) {
+                                $(fields[i]).off(events).on(events, function() {
+                                    that.validateFieldElement($(this));
+                                });
+                            }
+                        }
+                    })(field);
+                }
+            }
+
+            // Focus to the first invalid field
+            if (this.$invalidField) {
+                // Activate the tab containing the invalid field if exists
+                var $tab = this.$invalidField.parents('.tab-pane'),
+                    tabId;
+                if ($tab && (tabId = $tab.attr('id'))) {
+                    $('a[href="#' + tabId + '"][data-toggle="tab"]').trigger('click.bs.tab.data-api');
+                }
+
+                this.$invalidField.focus();
+            }
+        },
+
+        /**
+         * The default handler of success.form.bv event.
+         * It will be called when all the fields are valid
+         *
+         * @param {jQuery.Event} e The jQuery event object
+         */
+        _onSuccess: function(e) {
+            if (e.isDefaultPrevented()) {
+                return;
+            }
+
+            // Call the custom submission if enabled
+            if (this.options.submitHandler && 'function' == typeof this.options.submitHandler) {
+                // If you want to submit the form inside your submit handler, please call defaultSubmit() method
+                this.options.submitHandler.call(this, this, this.$form, this.$submitButton);
+            } else {
+                this.disableSubmitButtons(true).defaultSubmit();
             }
-            var type       = $field.attr('type'),
-                cannotType = ['button', 'checkbox', 'file', 'hidden', 'image', 'radio', 'reset', 'submit'].indexOf(type) != -1;
-            return (cannotType || $field.val().length >= threshold);
         },
 
         // --- Public methods ---
@@ -431,51 +497,13 @@
          * @returns {null|jQuery[]}
          */
         getFieldElements: function(field) {
-            var fields = this.options.fields[field].selector ? $(this.options.fields[field].selector) : this.$form.find('[name="' + field + '"]');
-            return (fields.length == 0) ? null : fields;
-        },
-
-        /**
-         * Set live validating mode
-         *
-         * @param {String} mode Live validating mode. Can be 'enabled', 'disabled', 'submitted'
-         * @returns {BootstrapValidator}
-         */
-        setLiveMode: function(mode) {
-            this.options.live = mode;
-            if ('submitted' == mode) {
-                return this;
-            }
-
-            var that = this;
-            for (var field in this.options.fields) {
-                (function(f) {
-                    var fields = that.getFieldElements(f);
-                    if (fields) {
-                        var type      = fields.attr('type'),
-                            total     = fields.length,
-                            updateAll = (total == 1) || ('radio' == type) || ('checkbox' == type),
-                            trigger   = that.options.fields[field].trigger
-                                        || that.options.trigger
-                                        || (('radio' == type || 'checkbox' == type || 'file' == type || 'SELECT' == fields[0].tagName) ? 'change' : that._changeEvent),
-                            events    = $.map(trigger.split(' '), function(item) {
-                                return item + '.live.bv';
-                            }).join(' ');
-
-                        for (var i = 0; i < total; i++) {
-                            ('enabled' == mode)
-                                ? $(fields[i]).on(events, function() {
-                                    if (that._exceedThreshold($(this))) {
-                                        updateAll ? that.validateField(f) : that.validateFieldElement($(this), false);
-                                    }
-                                })
-                                : $(fields[i]).off(events);
-                        }
-                    }
-                })(field);
+            if (!this._cacheFields[field]) {
+                this._cacheFields[field] = this.options.fields[field].selector
+                                         ? $(this.options.fields[field].selector)
+                                         : this.$form.find('[name="' + field + '"]');
             }
 
-            return this;
+            return this._cacheFields[field];
         },
 
         /**
@@ -510,10 +538,7 @@
                 this.validateField(field);
             }
 
-            // Check if whether the submit button is clicked
-            if (this.$submitButton) {
-                this._submit();
-            }
+            this._submit();
 
             return this;
         },
@@ -530,7 +555,7 @@
                 n      = (('radio' == type) || ('checkbox' == type)) ? 1 : fields.length;
 
             for (var i = 0; i < n; i++) {
-                this.validateFieldElement($(fields[i]), (n == 1));
+                this.validateFieldElement($(fields[i]));
             }
 
             return this;
@@ -540,12 +565,14 @@
          * Validate field element
          *
          * @param {jQuery} $field The field element
-         * @param {Boolean} updateAll If true, update status of all elements which have the same name
          * @returns {BootstrapValidator}
          */
-        validateFieldElement: function($field, updateAll) {
+        validateFieldElement: function($field) {
             var that       = this,
                 field      = $field.attr('data-bv-field'),
+                fields     = this.getFieldElements(field),
+                type       = $field.attr('type'),
+                updateAll  = (fields && fields.length == 1) || ('radio' == type) || ('checkbox' == type),
                 validators = this.options.fields[field].validators,
                 validatorName,
                 validateResult;
@@ -776,6 +803,52 @@
         // Useful APIs which aren't used internally
 
         /**
+         * Add new field element
+         *
+         * @param {jQuery} $field The field element
+         * @param {Object} options The field options
+         * @returns {BootstrapValidator}
+         */
+        addFieldElement: function($field, options) {
+            var field      = $field.attr('name') || $field.attr('data-bv-field'),
+                type       = $field.attr('type'),
+                isNewField = !this._cacheFields[field];
+
+            // Update cache
+            if (!isNewField && this._cacheFields[field].index($field) == -1) {
+                this._cacheFields[field] = this._cacheFields[field].add($field);
+            }
+
+            if ('checkbox' == type || 'radio' == type || isNewField) {
+                this._initField(field);
+            } else {
+                this._initFieldElement($field);
+            }
+
+            return this;
+        },
+
+        /**
+         * Remove given field element
+         *
+         * @param {jQuery} $field The field element
+         * @returns {BootstrapValidator}
+         */
+        removeFieldElement: function($field) {
+            var field = $field.attr('name') || $field.attr('data-bv-field'),
+                type  = $field.attr('type'),
+                index = this._cacheFields[field].index($field);
+
+            (index == -1) ? (delete this._cacheFields[field]) : this._cacheFields[field].splice(index, 1);
+
+            if ('checkbox' == type || 'radio' == type) {
+                this._initField(field);
+            }
+
+            return this;
+        },
+
+        /**
          * Reset the form
          *
          * @param {Boolean} resetFormData Reset current form data
@@ -794,7 +867,7 @@
                 }
 
                 // Mark field as not validated yet
-                this.updateStatus(field, this.STATUS_NOT_VALIDATED, null);
+                this.updateStatus(field, this.STATUS_NOT_VALIDATED);
 
                 if (resetFormData) {
                     type = fields.attr('type');
@@ -820,7 +893,7 @@
          */
         enableFieldValidators: function(field, enabled) {
             this.options.fields[field]['enabled'] = enabled;
-            this.updateStatus(field, this.STATUS_NOT_VALIDATED, null);
+            this.updateStatus(field, this.STATUS_NOT_VALIDATED);
 
             return this;
         }

File diff suppressed because it is too large
+ 3 - 3
dist/js/bootstrapValidator.min.js


+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
     "name": "bootstrapValidator",
-    "version": "0.4.3-dev",
+    "version": "0.5.0-dev",
     "description": "The best jQuery plugin to validate form fields. Designed to use with Bootstrap 3",
     "keywords": ["jQuery", "plugin", "validate", "validator", "form", "Bootstrap"],
     "author": {