vendor/assets/javascripts/bootstrapValidator.js in bootstrap-validator-rails-0.5.1 vs vendor/assets/javascripts/bootstrapValidator.js in bootstrap-validator-rails-0.5.2

- old
+ new

@@ -1,14 +1,18 @@ /*! * BootstrapValidator (http://bootstrapvalidator.com) * The best jQuery plugin to validate form fields. Designed to use with Bootstrap 3 * - * @version v0.5.1, built on 2014-08-22 4:55:09 PM + * @version v0.5.2, built on 2014-09-25 4:01:07 PM * @author https://twitter.com/nghuuphuoc * @copyright (c) 2013 - 2014 Nguyen Huu Phuoc * @license MIT */ +if (typeof jQuery === 'undefined') { + throw new Error('BootstrapValidator\'s JavaScript requires jQuery'); +} + (function($) { var BootstrapValidator = function(form, options) { this.$form = $(form); this.options = $.extend({}, $.fn.bootstrapValidator.DEFAULT_OPTIONS, options); @@ -52,26 +56,11 @@ * Init form */ _init: function() { var that = this, options = { - excluded: this.$form.attr('data-bv-excluded'), - trigger: this.$form.attr('data-bv-trigger'), - message: this.$form.attr('data-bv-message'), container: this.$form.attr('data-bv-container'), - group: this.$form.attr('data-bv-group'), - submitButtons: this.$form.attr('data-bv-submitbuttons'), - threshold: this.$form.attr('data-bv-threshold'), - live: this.$form.attr('data-bv-live'), - onSuccess: this.$form.attr('data-bv-onsuccess'), - onError: this.$form.attr('data-bv-onerror'), - fields: {}, - feedbackIcons: { - valid: this.$form.attr('data-bv-feedbackicons-valid'), - invalid: this.$form.attr('data-bv-feedbackicons-invalid'), - validating: this.$form.attr('data-bv-feedbackicons-validating') - }, events: { formInit: this.$form.attr('data-bv-events-form-init'), formError: this.$form.attr('data-bv-events-form-error'), formSuccess: this.$form.attr('data-bv-events-form-success'), fieldAdded: this.$form.attr('data-bv-events-field-added'), @@ -80,11 +69,27 @@ fieldError: this.$form.attr('data-bv-events-field-error'), fieldSuccess: this.$form.attr('data-bv-events-field-success'), fieldStatus: this.$form.attr('data-bv-events-field-status'), validatorError: this.$form.attr('data-bv-events-validator-error'), validatorSuccess: this.$form.attr('data-bv-events-validator-success') - } + }, + excluded: this.$form.attr('data-bv-excluded'), + feedbackIcons: { + valid: this.$form.attr('data-bv-feedbackicons-valid'), + invalid: this.$form.attr('data-bv-feedbackicons-invalid'), + validating: this.$form.attr('data-bv-feedbackicons-validating') + }, + group: this.$form.attr('data-bv-group'), + live: this.$form.attr('data-bv-live'), + message: this.$form.attr('data-bv-message'), + onError: this.$form.attr('data-bv-onerror'), + onSuccess: this.$form.attr('data-bv-onsuccess'), + submitButtons: this.$form.attr('data-bv-submitbuttons'), + threshold: this.$form.attr('data-bv-threshold'), + trigger: this.$form.attr('data-bv-trigger'), + verbose: this.$form.attr('data-bv-verbose'), + fields: {} }; this.$form // Disable client side validation in HTML 5 .attr('novalidate', 'novalidate') @@ -122,15 +127,21 @@ .addClass('bv-hidden-submit') .css({ display: 'none', width: 0, height: 0 }); this.$form .on('click.bv', '[type="submit"]', function(e) { - // Don't perform validation when clicking on the submit button/input - // which aren't defined by the 'submitButtons' option - var $button = $(e.target).eq(0); - if (that.options.submitButtons && !$button.is(that.options.submitButtons) && !$button.is(that.$hiddenButton)) { - that.$form.off('submit.bv').submit(); + // #746: Check if the button click handler returns false + if (!e.isDefaultPrevented()) { + var $target = $(e.target), + // The button might contain HTML tag + $button = $target.is('[type="submit"]') ? $target.eq(0) : $target.parent('[type="submit"]').eq(0); + + // Don't perform validation when clicking on the submit button/input + // which aren't defined by the 'submitButtons' option + if (that.options.submitButtons && !$button.is(that.options.submitButtons) && !$button.is(that.$hiddenButton)) { + that.$form.off('submit.bv').submit(); + } } }); for (var field in this.options.fields) { this._initField(field); @@ -197,21 +208,22 @@ } } } var opts = { + container: $field.attr('data-bv-container'), excluded: $field.attr('data-bv-excluded'), feedbackIcons: $field.attr('data-bv-feedbackicons'), - trigger: $field.attr('data-bv-trigger'), - message: $field.attr('data-bv-message'), - container: $field.attr('data-bv-container'), group: $field.attr('data-bv-group'), - selector: $field.attr('data-bv-selector'), - threshold: $field.attr('data-bv-threshold'), + message: $field.attr('data-bv-message'), + onError: $field.attr('data-bv-onerror'), onStatus: $field.attr('data-bv-onstatus'), onSuccess: $field.attr('data-bv-onsuccess'), - onError: $field.attr('data-bv-onerror'), + selector: $field.attr('data-bv-selector'), + threshold: $field.attr('data-bv-threshold'), + trigger: $field.attr('data-bv-trigger'), + verbose: $field.attr('data-bv-verbose'), validators: validators }, emptyOptions = $.isEmptyObject(opts), // Check if the field options are set using HTML attributes emptyValidators = $.isEmptyObject(validators); // Check if the field validators are set using HTML attributes @@ -241,19 +253,19 @@ break; default: break; } - if (this.options.fields[field] === null || this.options.fields[field].validators === null) { + // We don't need to validate non-existing fields + if (fields.length === 0) { return; } - // We don't need to validate non-existing fields - if (fields.length === 0) { - delete this.options.fields[field]; + if (this.options.fields[field] === null || this.options.fields[field].validators === null) { return; } + var validatorName; for (validatorName in this.options.fields[field].validators) { if (!$.fn.bootstrapValidator.validators[validatorName]) { delete this.options.fields[field].validators[validatorName]; } @@ -275,11 +287,11 @@ for (var i = 0; i < total; i++) { var $field = fields.eq(i), group = this.options.fields[field].group || this.options.group, $parent = $field.parents(group), // Allow user to indicate where the error messages are shown - container = this.options.fields[field].container || this.options.container, + container = ('function' === typeof (this.options.fields[field].container || this.options.container)) ? (this.options.fields[field].container || this.options.container).call(this, $field, this) : (this.options.fields[field].container || this.options.container), $message = (container && container !== 'tooltip' && container !== 'popover') ? $(container) : this._getMessageContainer($field, group); if (container && container !== 'tooltip' && container !== 'popover') { $message.addClass('has-error'); } @@ -290,11 +302,11 @@ // Whenever the user change the field value, mark it as not validated yet $field.off(events).on(events, function() { that.updateStatus($(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); @@ -307,31 +319,26 @@ .attr('data-bv-result', this.STATUS_NOT_VALIDATED) .html(this._getMessage(field, validatorName)) .appendTo($message); } - // Prepare the validator events - if (this.options.fields[field].validators[validatorName].onSuccess) { - $field.on(this.options.events.validatorSuccess, function(e, data) { - $.fn.bootstrapValidator.helpers.call(that.options.fields[field].validators[validatorName].onSuccess, [e, data]); - }); + // Init the validator + if ('function' === typeof $.fn.bootstrapValidator.validators[validatorName].init) { + $.fn.bootstrapValidator.validators[validatorName].init(this, $field, this.options.fields[field].validators[validatorName]); } - if (this.options.fields[field].validators[validatorName].onError) { - $field.on(this.options.events.validatorError, function(e, data) { - $.fn.bootstrapValidator.helpers.call(that.options.fields[field].validators[validatorName].onError, [e, data]); - }); - } } // Prepare the feedback icons // Available from Bootstrap 3.1 (http://getbootstrap.com/css/#forms-control-validation) if (this.options.fields[field].feedbackIcons !== false && this.options.fields[field].feedbackIcons !== 'false' && this.options.feedbackIcons && this.options.feedbackIcons.validating && this.options.feedbackIcons.invalid && this.options.feedbackIcons.valid && (!updateAll || i === total - 1)) { - $parent.removeClass('has-success').removeClass('has-error').addClass('has-feedback'); + // $parent.removeClass('has-success').removeClass('has-error').addClass('has-feedback'); + // Keep error messages which are populated from back-end + $parent.addClass('has-feedback'); var $icon = $('<i/>') .css('display', 'none') .addClass('form-control-feedback') .attr('data-bv-icon-for', field) .insertAfter($field); @@ -348,38 +355,84 @@ } // 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); + $icon.addClass('bv-no-label'); } // Fix feedback icons in input-group if ($parent.find('.input-group').length !== 0) { - $icon.css({ - 'top': 0, - 'z-index': 100 - }).insertAfter($parent.find('.input-group').eq(0)); + $icon.addClass('bv-icon-input-group') + .insertAfter($parent.find('.input-group').eq(0)); } + + if (container) { + $field + // Show tooltip/popover message when field gets focus + .off('focus.bv') + .on('focus.bv', function() { + switch (container) { + case 'tooltip': + $icon.tooltip('show'); + break; + case 'popover': + $icon.popover('show'); + break; + default: + break; + } + }) + // and hide them when losing focus + .off('blur.bv') + .on('blur.bv', function() { + switch (container) { + case 'tooltip': + $icon.tooltip('hide'); + break; + case 'popover': + $icon.popover('hide'); + break; + default: + break; + } + }); + } } } // Prepare the events - if (this.options.fields[field].onSuccess) { - fields.on(this.options.events.fieldSuccess, function(e, data) { - $.fn.bootstrapValidator.helpers.call(that.options.fields[field].onSuccess, [e, data]); + fields + .on(this.options.events.fieldSuccess, function(e, data) { + var onSuccess = that.getOptions(data.field, null, 'onSuccess'); + if (onSuccess) { + $.fn.bootstrapValidator.helpers.call(onSuccess, [e, data]); + } + }) + .on(this.options.events.fieldError, function(e, data) { + var onError = that.getOptions(data.field, null, 'onError'); + if (onError) { + $.fn.bootstrapValidator.helpers.call(onError, [e, data]); + } + }) + .on(this.options.events.fieldStatus, function(e, data) { + var onStatus = that.getOptions(data.field, null, 'onStatus'); + if (onStatus) { + $.fn.bootstrapValidator.helpers.call(onStatus, [e, data]); + } + }) + .on(this.options.events.validatorError, function(e, data) { + var onError = that.getOptions(data.field, data.validator, 'onError'); + if (onError) { + $.fn.bootstrapValidator.helpers.call(onError, [e, data]); + } + }) + .on(this.options.events.validatorSuccess, function(e, data) { + var onSuccess = that.getOptions(data.field, data.validator, 'onSuccess'); + if (onSuccess) { + $.fn.bootstrapValidator.helpers.call(onSuccess, [e, data]); + } }); - } - if (this.options.fields[field].onError) { - fields.on(this.options.events.fieldError, function(e, data) { - $.fn.bootstrapValidator.helpers.call(that.options.fields[field].onError, [e, data]); - }); - } - if (this.options.fields[field].onStatus) { - fields.on(this.options.events.fieldStatus, function(e, data) { - $.fn.bootstrapValidator.helpers.call(that.options.fields[field].onStatus, [e, data]); - }); - } // Set live mode events = $.map(trigger, function(item) { return item + '.live.bv'; }).join(' '); @@ -622,11 +675,12 @@ numValidators = 0, data = { bv: this, field: field, element: $field, - validator: validatorName + validator: validatorName, + result: $field.data('bv.response.' + validatorName) }; // Trigger an event after given validator completes if (validatorName) { switch ($field.data('bv.result.' + validatorName)) { @@ -692,10 +746,41 @@ return this._cacheFields[field]; }, /** + * Get the field options + * + * @param {String|jQuery} [field] The field name or field element. If it is not set, the method returns the form options + * @param {String} [validator] The name of validator. It null, the method returns form options + * @param {String} [option] The option name + * @return {String|Object} + */ + getOptions: function(field, validator, option) { + if (!field) { + return this.options; + } + if ('object' === typeof field) { + field = field.attr('data-bv-field'); + } + if (!this.options.fields[field]) { + return null; + } + + var options = this.options.fields[field]; + if (!validator) { + return option ? options[option] : options; + } + if (!options.validators || !options.validators[validator]) { + return null; + } + + return option ? options.validators[validator][option] : options.validators[validator]; + }, + + + /** * Disable/enable submit buttons * * @param {Boolean} disabled Can be true or false * @returns {BootstrapValidator} */ @@ -748,71 +833,89 @@ break; default: break; } - if (this.options.fields[field] && this.options.fields[field].enabled === false) { + if (fields.length === 0 || (this.options.fields[field] && this.options.fields[field].enabled === false)) { return this; } var that = this, type = fields.attr('type'), total = ('radio' === type || 'checkbox' === type) ? 1 : fields.length, updateAll = ('radio' === type || 'checkbox' === type), validators = this.options.fields[field].validators, + verbose = this.options.fields[field].verbose === 'true' || this.options.fields[field].verbose === true || this.options.verbose === 'true' || this.options.verbose === true, validatorName, validateResult; for (var i = 0; i < total; i++) { var $field = fields.eq(i); if (this._isExcluded($field)) { continue; } + var stop = false; for (validatorName in validators) { if ($field.data('bv.dfs.' + validatorName)) { $field.data('bv.dfs.' + validatorName).reject(); } + if (stop) { + break; + } // Don't validate field if it is already done var result = $field.data('bv.result.' + validatorName); - if (result === this.STATUS_VALID || result === this.STATUS_INVALID || validators[validatorName].enabled === false) { + if (result === this.STATUS_VALID || result === this.STATUS_INVALID) { this._onFieldValidated($field, validatorName); continue; + } else if (validators[validatorName].enabled === false) { + this.updateStatus(updateAll ? field : $field, this.STATUS_VALID, validatorName); + continue; } $field.data('bv.result.' + validatorName, this.STATUS_VALIDATING); validateResult = $.fn.bootstrapValidator.validators[validatorName].validate(this, $field, validators[validatorName]); // validateResult can be a $.Deferred object ... if ('object' === typeof validateResult && validateResult.resolve) { this.updateStatus(updateAll ? field : $field, this.STATUS_VALIDATING, validatorName); $field.data('bv.dfs.' + validatorName, validateResult); - validateResult.done(function($f, v, isValid, message) { + validateResult.done(function($f, v, response) { // v is validator name - $f.removeData('bv.dfs.' + v); - if (message) { - that.updateMessage($f, v, message); + $f.removeData('bv.dfs.' + v).data('bv.response.' + v, response); + if (response.message) { + that.updateMessage($f, v, response.message); } - that.updateStatus(updateAll ? $f.attr('data-bv-field') : $f, isValid ? that.STATUS_VALID : that.STATUS_INVALID, v); + that.updateStatus(updateAll ? $f.attr('data-bv-field') : $f, response.valid ? that.STATUS_VALID : that.STATUS_INVALID, v); - if (isValid && that._submitIfValid === true) { + if (response.valid && that._submitIfValid === true) { // If a remote validator returns true and the form is ready to submit, then do it that._submit(); + } else if (!response.valid && !verbose) { + stop = true; } }); } // ... or object { valid: true/false, message: 'dynamic message' } else if ('object' === typeof validateResult && validateResult.valid !== undefined && validateResult.message !== undefined) { + $field.data('bv.response.' + validatorName, validateResult); this.updateMessage(updateAll ? field : $field, validatorName, validateResult.message); this.updateStatus(updateAll ? field : $field, validateResult.valid ? this.STATUS_VALID : this.STATUS_INVALID, validatorName); + if (!validateResult.valid && !verbose) { + break; + } } // ... or a boolean value else if ('boolean' === typeof validateResult) { + $field.data('bv.response.' + validatorName, validateResult); this.updateStatus(updateAll ? field : $field, validateResult ? this.STATUS_VALID : this.STATUS_INVALID, validatorName); + if (!validateResult && !verbose) { + break; + } } } } return this; @@ -886,11 +989,11 @@ var $parent = $field.parents(group), $message = $field.data('bv.messages'), $allErrors = $message.find('.help-block[data-bv-validator][data-bv-for="' + field + '"]'), $errors = validatorName ? $allErrors.filter('[data-bv-validator="' + validatorName + '"]') : $allErrors, $icon = $parent.find('.form-control-feedback[data-bv-icon-for="' + field + '"]'), - container = this.options.fields[field].container || this.options.container, + container = ('function' === typeof (this.options.fields[field].container || this.options.container)) ? (this.options.fields[field].container || this.options.container).call(this, $field, this) : (this.options.fields[field].container || this.options.container), isValidField = null; // Update status if (validatorName) { $field.data('bv.result.' + validatorName, status); @@ -974,26 +1077,28 @@ switch (true) { // Only show the first error message if it is placed inside a tooltip ... case ($icon && 'tooltip' === container): (isValidField === false) ? $icon.css('cursor', 'pointer').tooltip('destroy').tooltip({ + container: 'body', html: true, placement: 'top', title: $allErrors.filter('[data-bv-result="' + that.STATUS_INVALID + '"]').eq(0).html() }) - : $icon.css('cursor', '').tooltip('destroy'); + : $icon.tooltip('hide'); break; // ... or popover case ($icon && 'popover' === container): (isValidField === false) ? $icon.css('cursor', 'pointer').popover('destroy').popover({ + container: 'body', content: $allErrors.filter('[data-bv-result="' + that.STATUS_INVALID + '"]').eq(0).html(), html: true, placement: 'top', trigger: 'hover click' }) - : $icon.css('cursor', '').popover('destroy'); + : $icon.popover('hide'); break; default: (status === this.STATUS_INVALID) ? $errors.show() : $errors.hide(); break; } @@ -1098,19 +1203,13 @@ for (var field in map) { var $f = map[field]; if ($f.data('bv.messages') .find('.help-block[data-bv-validator][data-bv-for="' + field + '"]') - .filter(function() { - var v = $(this).attr('data-bv-validator'), - f = $(this).attr('data-bv-for'); - return (that.options.fields[f].validators[v].enabled !== false - && $f.data('bv.result.' + v) && $f.data('bv.result.' + v) !== that.STATUS_VALID); - }) - .length !== 0) + .filter('[data-bv-result="' + this.STATUS_INVALID +'"]') + .length > 0) { - // The field is not valid return false; } } return true; @@ -1204,40 +1303,10 @@ return messages; }, /** - * Get the field options - * - * @param {String|jQuery} [field] The field name or field element. If it is not set, the method returns the form options - * @param {String} [validator] The name of validator. It null, the method returns form options - * @param {String} [option] The option name - * @return {String|Object} - */ - getOptions: function(field, validator, option) { - if (!field) { - return this.options; - } - if ('object' === typeof field) { - field = field.attr('data-bv-field'); - } - if (!this.options.fields[field]) { - return null; - } - - var options = this.options.fields[field]; - if (!validator) { - return options; - } - if (!options.validators || !options.validators[validator]) { - return null; - } - - return option ? options.validators[validator][option] : options.validators[validator]; - }, - - /** * Update the option of a specific validator * * @param {String|jQuery} field The field name or field element * @param {String} validator The validator name * @param {String} option The option name @@ -1517,14 +1586,13 @@ /** * Destroy the plugin * It will remove all error messages, feedback icons and turn off the events */ destroy: function() { - var field, fields, $field, validator, $icon, container, group; + var field, fields, $field, validator, $icon, group; for (field in this.options.fields) { fields = this.getFieldElements(field); - container = this.options.fields[field].container || this.options.container, group = this.options.fields[field].group || this.options.group; for (var i = 0; i < fields.length; i++) { $field = fields.eq(i); $field // Remove all error messages @@ -1541,10 +1609,11 @@ .removeAttr('data-bv-field'); // Remove feedback icons, tooltip/popover container $icon = $field.parents(group).find('i[data-bv-icon-for="' + field + '"]'); if ($icon) { + var container = ('function' === typeof (this.options.fields[field].container || this.options.container)) ? (this.options.fields[field].container || this.options.container).call(this, $field, this) : (this.options.fields[field].container || this.options.container); switch (container) { case 'tooltip': $icon.tooltip('destroy').remove(); break; case 'popover': @@ -1558,11 +1627,18 @@ for (validator in this.options.fields[field].validators) { if ($field.data('bv.dfs.' + validator)) { $field.data('bv.dfs.' + validator).reject(); } - $field.removeData('bv.result.' + validator).removeData('bv.dfs.' + validator); + $field.removeData('bv.result.' + validator) + .removeData('bv.response.' + validator) + .removeData('bv.dfs.' + validator); + + // Destroy the validator + if ('function' === typeof $.fn.bootstrapValidator.validators[validator].destroy) { + $.fn.bootstrapValidator.validators[validator].destroy(this, $field, this.options.fields[field].validators[validator]); + } } } } this.disableSubmitButtons(false); // Enable submit buttons @@ -1696,11 +1772,19 @@ fieldError: 'error.field.bv', fieldSuccess: 'success.field.bv', fieldStatus: 'status.field.bv', validatorError: 'error.validator.bv', validatorSuccess: 'success.validator.bv' - } + }, + + // Whether to be verbose when validating a field or not. + // Possible values: + // - true: when a field has multiple validators, all of them will be checked, and respectively - if errors occur in + // multiple validators, all of them will be displayed to the user + // - false: when a field has multiple validators, validation for this field will be terminated upon the first encountered error. + // Thus, only the very first error message related to this field will be displayed to the user + verbose: true }; // Available validators $.fn.bootstrapValidator.validators = {}; @@ -1958,10 +2042,39 @@ }; } }; }(window.jQuery)); ;(function($) { + $.fn.bootstrapValidator.validators.blank = { + /** + * Placeholder validator that can be used to display a custom validation message + * returned from the server + * Example: + * + * (1) a "blank" validator is applied to an input field. + * (2) data is entered via the UI that is unable to be validated client-side. + * (3) server returns a 400 with JSON data that contains the field that failed + * validation and an associated message. + * (4) ajax 400 call handler does the following: + * + * bv.updateMessage(field, 'blank', errorMessage); + * bv.updateStatus(field, 'INVALID'); + * + * @see https://github.com/nghuuphuoc/bootstrapvalidator/issues/542 + * @see https://github.com/nghuuphuoc/bootstrapvalidator/pull/666 + * @param {BootstrapValidator} validator The validator plugin instance + * @param {jQuery} $field Field element + * @param {Object} options Can consist of the following keys: + * - message: The invalid message + * @returns {Boolean} + */ + validate: function(validator, $field, options) { + return true; + } + }; +}(window.jQuery)); +;(function($) { $.fn.bootstrapValidator.i18n.callback = $.extend($.fn.bootstrapValidator.i18n.callback || {}, { 'default': 'Please enter a valid value' }); $.fn.bootstrapValidator.validators.callback = { @@ -1981,23 +2094,24 @@ * // fieldValue is the value of field * // validator is instance of BootstrapValidator * // $field is the field element * } * - message: The invalid message - * @returns {Boolean|Deferred} + * @returns {Deferred} */ validate: function(validator, $field, options) { - var value = $field.val(); + var value = $field.val(), + dfd = new $.Deferred(), + result = { valid: true }; if (options.callback) { - var dfd = new $.Deferred(), - response = $.fn.bootstrapValidator.helpers.call(options.callback, [value, validator, $field]); - dfd.resolve($field, 'callback', 'boolean' === typeof response ? response : response.valid, 'object' === typeof response && response.message ? response.message : null); - return dfd; + var response = $.fn.bootstrapValidator.helpers.call(options.callback, [value, validator, $field]); + result = ('boolean' === typeof response) ? { valid: response } : response; } - return true; + dfd.resolve($field, 'callback', result); + return dfd; } }; }(window.jQuery)); ;(function($) { $.fn.bootstrapValidator.i18n.choice = $.extend($.fn.bootstrapValidator.i18n.choice || {}, { @@ -2498,21 +2612,28 @@ var value = $field.val(); if (value === '') { return true; } - var compareWith = validator.getFieldElements(options.field); - if (compareWith === null || compareWith.length === 0) { - return true; - } + var fields = options.field.split(','), + isValid = true; - if (value !== compareWith.val()) { - validator.updateStatus(options.field, validator.STATUS_VALID, 'different'); - return true; - } else { - return false; + for (var i = 0; i < fields.length; i++) { + var compareWith = validator.getFieldElements(fields[i]); + if (compareWith == null || compareWith.length === 0) { + continue; + } + + var compareValue = compareWith.val(); + if (value === compareValue) { + isValid = false; + } else if (compareValue !== '') { + validator.updateStatus(compareWith, validator.STATUS_VALID, 'different'); + } } + + return isValid; } }; }(window.jQuery)); ;(function($) { $.fn.bootstrapValidator.i18n.digits = $.extend($.fn.bootstrapValidator.i18n.digits || {}, { @@ -2582,32 +2703,88 @@ $.fn.bootstrapValidator.i18n.emailAddress = $.extend($.fn.bootstrapValidator.i18n.emailAddress || {}, { 'default': 'Please enter a valid email address' }); $.fn.bootstrapValidator.validators.emailAddress = { + html5Attributes: { + message: 'message', + multiple: 'multiple', + separator: 'separator' + }, + enableByHtml5: function($field) { return ('email' === $field.attr('type')); }, /** * Return true if and only if the input value is a valid email address * * @param {BootstrapValidator} validator Validate plugin instance * @param {jQuery} $field Field element * @param {Object} [options] + * - multiple: Allow multiple email addresses, separated by a comma or semicolon; default is false. + * - separator: Regex for character or characters expected as separator between addresses; default is comma /[,;]/, i.e. comma or semicolon. * @returns {Boolean} */ validate: function(validator, $field, options) { var value = $field.val(); if (value === '') { return true; } // Email address regular expression // http://stackoverflow.com/questions/46155/validate-email-address-in-javascript - var emailRegExp = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; - return emailRegExp.test(value); + var emailRegExp = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/, + allowMultiple = options.multiple === true || options.multiple === 'true'; + + if (allowMultiple) { + var separator = options.separator || /[,;]/, + addresses = this._splitEmailAddresses(value, separator); + + for (var i = 0; i < addresses.length; i++) { + if (!emailRegExp.test(addresses[i])) { + return false; + } + } + + return true; + } else { + return emailRegExp.test(value); + } + }, + + _splitEmailAddresses: function(emailAddresses, separator) { + var quotedFragments = emailAddresses.split(/"/), + quotedFragmentCount = quotedFragments.length, + emailAddressArray = [], + nextEmailAddress = ''; + + for (var i = 0; i < quotedFragmentCount; i++) { + if (i % 2 === 0) { + var splitEmailAddressFragments = quotedFragments[i].split(separator), + splitEmailAddressFragmentCount = splitEmailAddressFragments.length; + + if (splitEmailAddressFragmentCount === 1) { + nextEmailAddress += splitEmailAddressFragments[0]; + } else { + emailAddressArray.push(nextEmailAddress + splitEmailAddressFragments[0]); + + for (var j = 1; j < splitEmailAddressFragmentCount - 1; j++) { + emailAddressArray.push(splitEmailAddressFragments[j]); + } + nextEmailAddress = splitEmailAddressFragments[splitEmailAddressFragmentCount - 1]; + } + } else { + nextEmailAddress += '"' + quotedFragments[i]; + if (i < quotedFragmentCount - 1) { + nextEmailAddress += '"'; + } + } + } + + emailAddressArray.push(nextEmailAddress); + return emailAddressArray; } }; }(window.jQuery)); ;(function($) { $.fn.bootstrapValidator.i18n.file = $.extend($.fn.bootstrapValidator.i18n.file || {}, { @@ -2616,10 +2793,11 @@ $.fn.bootstrapValidator.validators.file = { html5Attributes: { extension: 'extension', maxsize: 'maxSize', + minsize: 'minSize', message: 'message', type: 'type' }, /** @@ -2628,10 +2806,11 @@ * @param {BootstrapValidator} validator The validator plugin instance * @param {jQuery} $field Field element * @param {Object} options Can consist of the following keys: * - extension: The allowed extensions, separated by a comma * - maxSize: The maximum size in bytes + * - minSize: the minimum size in bytes * - message: The invalid message * - type: The allowed MIME type, separated by a comma * @returns {Boolean} */ validate: function(validator, $field, options) { @@ -2648,11 +2827,16 @@ if (html5) { // Get FileList instance var files = $field.get(0).files, total = files.length; for (var i = 0; i < total; i++) { - // Check file size + // Check the minSize + if (options.minSize && files[i].size < parseInt(options.minSize, 10)) { + return false; + } + + // Check the maxSize if (options.maxSize && files[i].size > parseInt(options.maxSize, 10)) { return false; } // Check file extension @@ -2859,11 +3043,11 @@ CV: 'Cape Verde', CY: 'Cyprus', CZ: 'Czech Republic', DE: 'Germany', DK: 'Denmark', - DO: 'Dominican Republic', + DO: 'Dominica', DZ: 'Algeria', EE: 'Estonia', ES: 'Spain', FI: 'Finland', FO: 'Faroe Islands', @@ -2901,11 +3085,11 @@ MZ: 'Mozambique', NL: 'Netherlands', NO: 'Norway', PK: 'Pakistan', PL: 'Poland', - PS: 'Palestinian', + PS: 'Palestine', PT: 'Portugal', QA: 'Qatar', RO: 'Romania', RS: 'Serbia', SA: 'Saudi Arabia', @@ -3080,37 +3264,39 @@ }(window.jQuery)); ;(function($) { $.fn.bootstrapValidator.i18n.id = $.extend($.fn.bootstrapValidator.i18n.id || {}, { 'default': 'Please enter a valid identification number', countryNotSupported: 'The country code %s is not supported', - country: 'Please enter a valid %s identification number', + country: 'Please enter a valid identification number in %s', countries: { BA: 'Bosnia and Herzegovina', - BG: 'Bulgarian', - BR: 'Brazilian', - CH: 'Swiss', - CL: 'Chilean', - CZ: 'Czech', - DK: 'Danish', - EE: 'Estonian', - ES: 'Spanish', - FI: 'Finnish', - HR: 'Croatian', - IE: 'Irish', + BG: 'Bulgaria', + BR: 'Brazil', + CH: 'Switzerland', + CL: 'Chile', + CN: 'China', + CZ: 'Czech Republic', + DK: 'Denmark', + EE: 'Estonia', + ES: 'Spain', + FI: 'Finland', + HR: 'Croatia', + IE: 'Ireland', IS: 'Iceland', - LT: 'Lithuanian', - LV: 'Latvian', + LT: 'Lithuania', + LV: 'Latvia', ME: 'Montenegro', - MK: 'Macedonian', - NL: 'Dutch', - RO: 'Romanian', - RS: 'Serbian', - SE: 'Swedish', - SI: 'Slovenian', - SK: 'Slovak', + MK: 'Macedonia', + NL: 'Netherlands', + RO: 'Romania', + RS: 'Serbia', + SE: 'Sweden', + SI: 'Slovenia', + SK: 'Slovakia', SM: 'San Marino', - ZA: 'South African' + TH: 'Thailand', + ZA: 'South Africa' } }); $.fn.bootstrapValidator.validators.id = { html5Attributes: { @@ -3118,12 +3304,12 @@ country: 'country' }, // Supported country codes COUNTRY_CODES: [ - 'BA', 'BG', 'BR', 'CH', 'CL', 'CZ', 'DK', 'EE', 'ES', 'FI', 'HR', 'IE', 'IS', 'LT', 'LV', 'ME', 'MK', 'NL', - 'RO', 'RS', 'SE', 'SI', 'SK', 'SM', 'ZA' + 'BA', 'BG', 'BR', 'CH', 'CL', 'CN', 'CZ', 'DK', 'EE', 'ES', 'FI', 'HR', 'IE', 'IS', 'LT', 'LV', 'ME', 'MK', 'NL', + 'RO', 'RS', 'SE', 'SI', 'SK', 'SM', 'TH', 'ZA' ], /** * Validate identification number in different countries * @@ -3393,10 +3579,546 @@ } return sum + '' === value.charAt(8).toUpperCase(); }, /** + * Validate Chinese citizen identification number + * + * Rules: + * - For current 18-digit system (since 1st Oct 1999, defined by GB11643—1999 national standard): + * - Digit 0-5: Must be a valid administrative division code of China PR. + * - Digit 6-13: Must be a valid YYYYMMDD date of birth. A future date is tolerated. + * - Digit 14-16: Order code, any integer. + * - Digit 17: An ISO 7064:1983, MOD 11-2 checksum. + * Both upper/lower case of X are tolerated. + * - For deprecated 15-digit system: + * - Digit 0-5: Must be a valid administrative division code of China PR. + * - Digit 6-11: Must be a valid YYMMDD date of birth, indicating the year of 19XX. + * - Digit 12-14: Order code, any integer. + * Lists of valid administrative division codes of China PR can be seen here: + * <http://www.stats.gov.cn/tjsj/tjbz/xzqhdm/> + * Published and maintained by National Bureau of Statistics of China PR. + * NOTE: Current and deprecated codes MUST BOTH be considered valid. + * Many Chinese citizens born in once existed administrative divisions! + * + * @see http://en.wikipedia.org/wiki/Resident_Identity_Card#Identity_card_number + * @param {String} value The ID + * @returns {Boolean} + */ + _cn: function(value) { + // Basic format check (18 or 15 digits, considering X in checksum) + value = value.trim(); + if (!/^\d{15}$/.test(value) && !/^\d{17}[\dXx]{1}$/.test(value)) { + return false; + } + + // Check China PR Administrative division code + var adminDivisionCodes = { + 11: { + 0: [0], + 1: [[0, 9], [11, 17]], + 2: [0, 28, 29] + }, + 12: { + 0: [0], + 1: [[0, 16]], + 2: [0, 21, 23, 25] + }, + 13: { + 0: [0], + 1: [[0, 5], 7, 8, 21, [23, 33], [81, 85]], + 2: [[0, 5], [7, 9], [23, 25], 27, 29, 30, 81, 83], + 3: [[0, 4], [21, 24]], + 4: [[0, 4], 6, 21, [23, 35], 81], + 5: [[0, 3], [21, 35], 81, 82], + 6: [[0, 4], [21, 38], [81, 84]], + 7: [[0, 3], 5, 6, [21, 33]], + 8: [[0, 4], [21, 28]], + 9: [[0, 3], [21, 30], [81, 84]], + 10: [[0, 3], [22, 26], 28, 81, 82], + 11: [[0, 2], [21, 28], 81, 82] + }, + 14: { + 0: [0], + 1: [0, 1, [5, 10], [21, 23], 81], + 2: [[0, 3], 11, 12, [21, 27]], + 3: [[0, 3], 11, 21, 22], + 4: [[0, 2], 11, 21, [23, 31], 81], + 5: [[0, 2], 21, 22, 24, 25, 81], + 6: [[0, 3], [21, 24]], + 7: [[0, 2], [21, 29], 81], + 8: [[0, 2], [21, 30], 81, 82], + 9: [[0, 2], [21, 32], 81], + 10: [[0, 2], [21, 34], 81, 82], + 11: [[0, 2], [21, 30], 81, 82], + 23: [[0, 3], 22, 23, [25, 30], 32, 33] + }, + 15: { + 0: [0], + 1: [[0, 5], [21, 25]], + 2: [[0, 7], [21, 23]], + 3: [[0, 4]], + 4: [[0, 4], [21, 26], [28, 30]], + 5: [[0, 2], [21, 26], 81], + 6: [[0, 2], [21, 27]], + 7: [[0, 3], [21, 27], [81, 85]], + 8: [[0, 2], [21, 26]], + 9: [[0, 2], [21, 29], 81], + 22: [[0, 2], [21, 24]], + 25: [[0, 2], [22, 31]], + 26: [[0, 2], [24, 27], [29, 32], 34], + 28: [0, 1, [22, 27]], + 29: [0, [21, 23]] + }, + 21: { + 0: [0], + 1: [[0, 6], [11, 14], [22, 24], 81], + 2: [[0, 4], [11, 13], 24, [81, 83]], + 3: [[0, 4], 11, 21, 23, 81], + 4: [[0, 4], 11, [21, 23]], + 5: [[0, 5], 21, 22], + 6: [[0, 4], 24, 81, 82], + 7: [[0, 3], 11, 26, 27, 81, 82], + 8: [[0, 4], 11, 81, 82], + 9: [[0, 5], 11, 21, 22], + 10: [[0, 5], 11, 21, 81], + 11: [[0, 3], 21, 22], + 12: [[0, 2], 4, 21, 23, 24, 81, 82], + 13: [[0, 3], 21, 22, 24, 81, 82], + 14: [[0, 4], 21, 22, 81] + }, + 22: { + 0: [0], + 1: [[0, 6], 12, 22, [81, 83]], + 2: [[0, 4], 11, 21, [81, 84]], + 3: [[0, 3], 22, 23, 81, 82], + 4: [[0, 3], 21, 22], + 5: [[0, 3], 21, 23, 24, 81, 82], + 6: [[0, 2], 4, 5, [21, 23], 25, 81], + 7: [[0, 2], [21, 24], 81], + 8: [[0, 2], 21, 22, 81, 82], + 24: [[0, 6], 24, 26] + }, + 23: { + 0: [0], + 1: [[0, 12], 21, [23, 29], [81, 84]], + 2: [[0, 8], 21, [23, 25], 27, [29, 31], 81], + 3: [[0, 7], 21, 81, 82], + 4: [[0, 7], 21, 22], + 5: [[0, 3], 5, 6, [21, 24]], + 6: [[0, 6], [21, 24]], + 7: [[0, 16], 22, 81], + 8: [[0, 5], 11, 22, 26, 28, 33, 81, 82], + 9: [[0, 4], 21], + 10: [[0, 5], 24, 25, 81, [83, 85]], + 11: [[0, 2], 21, 23, 24, 81, 82], + 12: [[0, 2], [21, 26], [81, 83]], + 27: [[0, 4], [21, 23]] + }, + 31: { + 0: [0], + 1: [0, 1, [3, 10], [12, 20]], + 2: [0, 30] + }, + 32: { + 0: [0], + 1: [[0, 7], 11, [13, 18], 24, 25], + 2: [[0, 6], 11, 81, 82], + 3: [[0, 5], 11, 12, [21, 24], 81, 82], + 4: [[0, 2], 4, 5, 11, 12, 81, 82], + 5: [[0, 9], [81, 85]], + 6: [[0, 2], 11, 12, 21, 23, [81, 84]], + 7: [0, 1, 3, 5, 6, [21, 24]], + 8: [[0, 4], 11, 26, [29, 31]], + 9: [[0, 3], [21, 25], 28, 81, 82], + 10: [[0, 3], 11, 12, 23, 81, 84, 88], + 11: [[0, 2], 11, 12, [81, 83]], + 12: [[0, 4], [81, 84]], + 13: [[0, 2], 11, [21, 24]] + }, + 33: { + 0: [0], + 1: [[0, 6], [8, 10], 22, 27, 82, 83, 85], + 2: [0, 1, [3, 6], 11, 12, 25, 26, [81, 83]], + 3: [[0, 4], 22, 24, [26, 29], 81, 82], + 4: [[0, 2], 11, 21, 24, [81, 83]], + 5: [[0, 3], [21, 23]], + 6: [[0, 2], 21, 24, [81, 83]], + 7: [[0, 3], 23, 26, 27, [81, 84]], + 8: [[0, 3], 22, 24, 25, 81], + 9: [[0, 3], 21, 22], + 10: [[0, 4], [21, 24], 81, 82], + 11: [[0, 2], [21, 27], 81] + }, + 34: { + 0: [0], + 1: [[0, 4], 11, [21, 24], 81], + 2: [[0, 4], 7, 8, [21, 23], 25], + 3: [[0, 4], 11, [21, 23]], + 4: [[0, 6], 21], + 5: [[0, 4], 6, [21, 23]], + 6: [[0, 4], 21], + 7: [[0, 3], 11, 21], + 8: [[0, 3], 11, [22, 28], 81], + 10: [[0, 4], [21, 24]], + 11: [[0, 3], 22, [24, 26], 81, 82], + 12: [[0, 4], 21, 22, 25, 26, 82], + 13: [[0, 2], [21, 24]], + 14: [[0, 2], [21, 24]], + 15: [[0, 3], [21, 25]], + 16: [[0, 2], [21, 23]], + 17: [[0, 2], [21, 23]], + 18: [[0, 2], [21, 25], 81] + }, + 35: { + 0: [0], + 1: [[0, 5], 11, [21, 25], 28, 81, 82], + 2: [[0, 6], [11, 13]], + 3: [[0, 5], 22], + 4: [[0, 3], 21, [23, 30], 81], + 5: [[0, 5], 21, [24, 27], [81, 83]], + 6: [[0, 3], [22, 29], 81], + 7: [[0, 2], [21, 25], [81, 84]], + 8: [[0, 2], [21, 25], 81], + 9: [[0, 2], [21, 26], 81, 82] + }, + 36: { + 0: [0], + 1: [[0, 5], 11, [21, 24]], + 2: [[0, 3], 22, 81], + 3: [[0, 2], 13, [21, 23]], + 4: [[0, 3], 21, [23, 30], 81, 82], + 5: [[0, 2], 21], + 6: [[0, 2], 22, 81], + 7: [[0, 2], [21, 35], 81, 82], + 8: [[0, 3], [21, 30], 81], + 9: [[0, 2], [21, 26], [81, 83]], + 10: [[0, 2], [21, 30]], + 11: [[0, 2], [21, 30], 81] + }, + 37: { + 0: [0], + 1: [[0, 5], 12, 13, [24, 26], 81], + 2: [[0, 3], 5, [11, 14], [81, 85]], + 3: [[0, 6], [21, 23]], + 4: [[0, 6], 81], + 5: [[0, 3], [21, 23]], + 6: [[0, 2], [11, 13], 34, [81, 87]], + 7: [[0, 5], 24, 25, [81, 86]], + 8: [[0, 2], 11, [26, 32], [81, 83]], + 9: [[0, 3], 11, 21, 23, 82, 83], + 10: [[0, 2], [81, 83]], + 11: [[0, 3], 21, 22], + 12: [[0, 3]], + 13: [[0, 2], 11, 12, [21, 29]], + 14: [[0, 2], [21, 28], 81, 82], + 15: [[0, 2], [21, 26], 81], + 16: [[0, 2], [21, 26]], + 17: [[0, 2], [21, 28]] + }, + 41: { + 0: [0], + 1: [[0, 6], 8, 22, [81, 85]], + 2: [[0, 5], 11, [21, 25]], + 3: [[0, 7], 11, [22, 29], 81], + 4: [[0, 4], 11, [21, 23], 25, 81, 82], + 5: [[0, 3], 5, 6, 22, 23, 26, 27, 81], + 6: [[0, 3], 11, 21, 22], + 7: [[0, 4], 11, 21, [24, 28], 81, 82], + 8: [[0, 4], 11, [21, 23], 25, [81, 83]], + 9: [[0, 2], 22, 23, [26, 28]], + 10: [[0, 2], [23, 25], 81, 82], + 11: [[0, 4], [21, 23]], + 12: [[0, 2], 21, 22, 24, 81, 82], + 13: [[0, 3], [21, 30], 81], + 14: [[0, 3], [21, 26], 81], + 15: [[0, 3], [21, 28]], + 16: [[0, 2], [21, 28], 81], + 17: [[0, 2], [21, 29]], + 90: [0, 1] + }, + 42: { + 0: [0], + 1: [[0, 7], [11, 17]], + 2: [[0, 5], 22, 81], + 3: [[0, 3], [21, 25], 81], + 5: [[0, 6], [25, 29], [81, 83]], + 6: [[0, 2], 6, 7, [24, 26], [82, 84]], + 7: [[0, 4]], + 8: [[0, 2], 4, 21, 22, 81], + 9: [[0, 2], [21, 23], 81, 82, 84], + 10: [[0, 3], [22, 24], 81, 83, 87], + 11: [[0, 2], [21, 27], 81, 82], + 12: [[0, 2], [21, 24], 81], + 13: [[0, 3], 21, 81], + 28: [[0, 2], 22, 23, [25, 28]], + 90: [0, [4, 6], 21] + }, + 43: { + 0: [0], + 1: [[0, 5], 11, 12, 21, 22, 24, 81], + 2: [[0, 4], 11, 21, [23, 25], 81], + 3: [[0, 2], 4, 21, 81, 82], + 4: [0, 1, [5, 8], 12, [21, 24], 26, 81, 82], + 5: [[0, 3], 11, [21, 25], [27, 29], 81], + 6: [[0, 3], 11, 21, 23, 24, 26, 81, 82], + 7: [[0, 3], [21, 26], 81], + 8: [[0, 2], 11, 21, 22], + 9: [[0, 3], [21, 23], 81], + 10: [[0, 3], [21, 28], 81], + 11: [[0, 3], [21, 29]], + 12: [[0, 2], [21, 30], 81], + 13: [[0, 2], 21, 22, 81, 82], + 31: [0, 1, [22, 27], 30] + }, + 44: { + 0: [0], + 1: [[0, 7], [11, 16], 83, 84], + 2: [[0, 5], 21, 22, 24, 29, 32, 33, 81, 82], + 3: [0, 1, [3, 8]], + 4: [[0, 4]], + 5: [0, 1, [6, 15], 23, 82, 83], + 6: [0, 1, [4, 8]], + 7: [0, 1, [3, 5], 81, [83, 85]], + 8: [[0, 4], 11, 23, 25, [81, 83]], + 9: [[0, 3], 23, [81, 83]], + 12: [[0, 3], [23, 26], 83, 84], + 13: [[0, 3], [22, 24], 81], + 14: [[0, 2], [21, 24], 26, 27, 81], + 15: [[0, 2], 21, 23, 81], + 16: [[0, 2], [21, 25]], + 17: [[0, 2], 21, 23, 81], + 18: [[0, 3], 21, 23, [25, 27], 81, 82], + 19: [0], + 20: [0], + 51: [[0, 3], 21, 22], + 52: [[0, 3], 21, 22, 24, 81], + 53: [[0, 2], [21, 23], 81] + }, + 45: { + 0: [0], + 1: [[0, 9], [21, 27]], + 2: [[0, 5], [21, 26]], + 3: [[0, 5], 11, 12, [21, 32]], + 4: [0, 1, [3, 6], 11, [21, 23], 81], + 5: [[0, 3], 12, 21], + 6: [[0, 3], 21, 81], + 7: [[0, 3], 21, 22], + 8: [[0, 4], 21, 81], + 9: [[0, 3], [21, 24], 81], + 10: [[0, 2], [21, 31]], + 11: [[0, 2], [21, 23]], + 12: [[0, 2], [21, 29], 81], + 13: [[0, 2], [21, 24], 81], + 14: [[0, 2], [21, 25], 81] + }, + 46: { + 0: [0], + 1: [0, 1, [5, 8]], + 2: [0, 1], + 3: [0, [21, 23]], + 90: [[0, 3], [5, 7], [21, 39]] + }, + 50: { + 0: [0], + 1: [[0, 19]], + 2: [0, [22, 38], [40, 43]], + 3: [0, [81, 84]] + }, + 51: { + 0: [0], + 1: [0, 1, [4, 8], [12, 15], [21, 24], 29, 31, 32, [81, 84]], + 3: [[0, 4], 11, 21, 22], + 4: [[0, 3], 11, 21, 22], + 5: [[0, 4], 21, 22, 24, 25], + 6: [0, 1, 3, 23, 26, [81, 83]], + 7: [0, 1, 3, 4, [22, 27], 81], + 8: [[0, 2], 11, 12, [21, 24]], + 9: [[0, 4], [21, 23]], + 10: [[0, 2], 11, 24, 25, 28], + 11: [[0, 2], [11, 13], 23, 24, 26, 29, 32, 33, 81], + 13: [[0, 4], [21, 25], 81], + 14: [[0, 2], [21, 25]], + 15: [[0, 3], [21, 29]], + 16: [[0, 3], [21, 23], 81], + 17: [[0, 3], [21, 25], 81], + 18: [[0, 3], [21, 27]], + 19: [[0, 3], [21, 23]], + 20: [[0, 2], 21, 22, 81], + 32: [0, [21, 33]], + 33: [0, [21, 38]], + 34: [0, 1, [22, 37]] + }, + 52: { + 0: [0], + 1: [[0, 3], [11, 15], [21, 23], 81], + 2: [0, 1, 3, 21, 22], + 3: [[0, 3], [21, 30], 81, 82], + 4: [[0, 2], [21, 25]], + 5: [[0, 2], [21, 27]], + 6: [[0, 3], [21, 28]], + 22: [0, 1, [22, 30]], + 23: [0, 1, [22, 28]], + 24: [0, 1, [22, 28]], + 26: [0, 1, [22, 36]], + 27: [[0, 2], 22, 23, [25, 32]] + }, + 53: { + 0: [0], + 1: [[0, 3], [11, 14], 21, 22, [24, 29], 81], + 3: [[0, 2], [21, 26], 28, 81], + 4: [[0, 2], [21, 28]], + 5: [[0, 2], [21, 24]], + 6: [[0, 2], [21, 30]], + 7: [[0, 2], [21, 24]], + 8: [[0, 2], [21, 29]], + 9: [[0, 2], [21, 27]], + 23: [0, 1, [22, 29], 31], + 25: [[0, 4], [22, 32]], + 26: [0, 1, [21, 28]], + 27: [0, 1, [22, 30]], 28: [0, 1, 22, 23], + 29: [0, 1, [22, 32]], + 31: [0, 2, 3, [22, 24]], + 34: [0, [21, 23]], + 33: [0, 21, [23, 25]], + 35: [0, [21, 28]] + }, + 54: { + 0: [0], + 1: [[0, 2], [21, 27]], + 21: [0, [21, 29], 32, 33], + 22: [0, [21, 29], [31, 33]], + 23: [0, 1, [22, 38]], + 24: [0, [21, 31]], + 25: [0, [21, 27]], + 26: [0, [21, 27]] + }, + 61: { + 0: [0], + 1: [[0, 4], [11, 16], 22, [24, 26]], + 2: [[0, 4], 22], + 3: [[0, 4], [21, 24], [26, 31]], + 4: [[0, 4], [22, 31], 81], + 5: [[0, 2], [21, 28], 81, 82], + 6: [[0, 2], [21, 32]], + 7: [[0, 2], [21, 30]], + 8: [[0, 2], [21, 31]], + 9: [[0, 2], [21, 29]], + 10: [[0, 2], [21, 26]] + }, + 62: { + 0: [0], + 1: [[0, 5], 11, [21, 23]], + 2: [0, 1], + 3: [[0, 2], 21], + 4: [[0, 3], [21, 23]], + 5: [[0, 3], [21, 25]], + 6: [[0, 2], [21, 23]], + 7: [[0, 2], [21, 25]], + 8: [[0, 2], [21, 26]], + 9: [[0, 2], [21, 24], 81, 82], + 10: [[0, 2], [21, 27]], + 11: [[0, 2], [21, 26]], + 12: [[0, 2], [21, 28]], + 24: [0, 21, [24, 29]], + 26: [0, 21, [23, 30]], + 29: [0, 1, [21, 27]], + 30: [0, 1, [21, 27]] + }, + 63: { + 0: [0], + 1: [[0, 5], [21, 23]], + 2: [0, 2, [21, 25]], + 21: [0, [21, 23], [26, 28]], + 22: [0, [21, 24]], + 23: [0, [21, 24]], + 25: [0, [21, 25]], + 26: [0, [21, 26]], + 27: [0, 1, [21, 26]], + 28: [[0, 2], [21, 23]] + }, + 64: { + 0: [0], + 1: [0, 1, [4, 6], 21, 22, 81], + 2: [[0, 3], 5, [21, 23]], + 3: [[0, 3], [21, 24], 81], + 4: [[0, 2], [21, 25]], + 5: [[0, 2], 21, 22] + }, + 65: { + 0: [0], + 1: [[0, 9], 21], + 2: [[0, 5]], + 21: [0, 1, 22, 23], + 22: [0, 1, 22, 23], + 23: [[0, 3], [23, 25], 27, 28], + 28: [0, 1, [22, 29]], + 29: [0, 1, [22, 29]], + 30: [0, 1, [22, 24]], 31: [0, 1, [21, 31]], + 32: [0, 1, [21, 27]], + 40: [0, 2, 3, [21, 28]], + 42: [[0, 2], 21, [23, 26]], + 43: [0, 1, [21, 26]], + 90: [[0, 4]], 27: [[0, 2], 22, 23] + }, + 71: { 0: [0] }, + 81: { 0: [0] }, + 82: { 0: [0] } + }; + + var provincial = parseInt(value.substr(0, 2), 10), + prefectural = parseInt(value.substr(2, 2), 10), + county = parseInt(value.substr(4, 2), 10); + + if (!adminDivisionCodes[provincial] || !adminDivisionCodes[provincial][prefectural]) { + return false; + } + var inRange = false, + rangeDef = adminDivisionCodes[provincial][prefectural]; + for (var i = 0; i < rangeDef.length; i++) { + if (($.isArray(rangeDef[i]) && rangeDef[i][0] <= county && county <= rangeDef[i][1]) + || (!$.isArray(rangeDef[i]) && county === rangeDef[i])) + { + inRange = true; + break; + } + } + + if (!inRange) { + return false; + } + + // Check date of birth + var dob; + if (value.length === 18) { + dob = value.substr(6, 8); + } else /* length == 15 */ { + dob = '19' + value.substr(6, 6); + } + var year = parseInt(dob.substr(0, 4), 10), + month = parseInt(dob.substr(4, 2), 10), + day = parseInt(dob.substr(6, 2), 10); + if (!$.fn.bootstrapValidator.helpers.date(year, month, day)) { + return false; + } + + // Check checksum (18-digit system only) + if (value.length === 18) { + var sum = 0, + weight = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; + for (i = 0; i < 17; i++) { + sum += parseInt(value.charAt(i), 10) * weight[i]; + } + sum = (12 - (sum % 11)) % 11; + var checksum = (value.charAt(17).toUpperCase() !== 'X') ? parseInt(value.charAt(17), 10) : 10; + return checksum === sum; + } + + return true; + }, + + /** * Validate Czech national identification number (RC) * Examples: * - Valid: 7103192745, 991231123 * - Invalid: 1103492745, 590312123 * @@ -3863,10 +4585,33 @@ _sm: function(value) { return /^\d{5}$/.test(value); }, /** + * Validate Thailand citizen number + * Examples: + * - Valid: 7145620509547, 3688699975685, 2368719339716 + * - Invalid: 1100800092310 + * + * @see http://en.wikipedia.org/wiki/National_identification_number#Thailand + * @param {String} value The ID + * @returns {Boolean} + */ + _th: function(value) { + if (value.length !== 13) { + return false; + } + + var sum = 0; + for (var i = 0; i < 12; i++) { + sum += parseInt(value.charAt(i), 10) * (13 - i); + } + + return (11 - sum % 11) % 10 === parseInt(value.charAt(12), 10); + }, + + /** * Validate South African ID * Example: * - Valid: 8001015009087 * - Invalid: 8001015009287, 8001015009086 * @@ -4103,11 +4848,11 @@ break; case (options.ipv4 && options.ipv6): /* falls through */ default: - valid = ipv4Regex.test(value) && ipv6Regex.test(value); + valid = ipv4Regex.test(value) || ipv6Regex.test(value); message = options.message || $.fn.bootstrapValidator.i18n.ip['default']; break; } return { @@ -4564,10 +5309,14 @@ .getFieldElements($field.attr('data-bv-field')) .filter(':checked') .length > 0; } + if ('number' === type && $field.get(0).validity && $field.get(0).validity.badInput === true) { + return true; + } + return $.trim($field.val()) !== ''; } }; }(window.jQuery)); ;(function($) { @@ -4618,27 +5367,35 @@ 'default': 'Please enter a valid phone number', countryNotSupported: 'The country code %s is not supported', country: 'Please enter a valid phone number in %s', countries: { BR: 'Brazil', + CN: 'China', + CZ: 'Czech Republic', + DK: 'Denmark', ES: 'Spain', FR: 'France', GB: 'United Kingdom', MA: 'Morocco', PK: 'Pakistan', - US: 'USA' + RO: 'Romania', + RU: 'Russia', + SK: 'Slovakia', + TH: 'Thailand', + US: 'USA', + VE: 'Venezuela' } }); $.fn.bootstrapValidator.validators.phone = { html5Attributes: { message: 'message', country: 'country' }, // The supported countries - COUNTRY_CODES: ['BR', 'ES', 'FR', 'GB', 'MA', 'PK', 'US'], + COUNTRY_CODES: ['BR', 'CN', 'CZ', 'DK', 'ES', 'FR', 'GB', 'MA', 'PK', 'RO', 'RU', 'SK', 'TH', 'US', 'VE'], /** * Return true if the input value contains a valid phone number for the country * selected in the options * @@ -4679,10 +5436,30 @@ // Test: http://regexr.com/399m1 value = $.trim(value); isValid = (/^(([\d]{4}[-.\s]{1}[\d]{2,3}[-.\s]{1}[\d]{2}[-.\s]{1}[\d]{2})|([\d]{4}[-.\s]{1}[\d]{3}[-.\s]{1}[\d]{4})|((\(?\+?[0-9]{2}\)?\s?)?(\(?\d{2}\)?\s?)?\d{4,5}[-.\s]?\d{4}))$/).test(value); break; + case 'CN': + // http://regexr.com/39dq4 + value = $.trim(value); + isValid = (/^((00|\+)?(86(?:-| )))?((\d{11})|(\d{3}[- ]{1}\d{4}[- ]{1}\d{4})|((\d{2,4}[- ]){1}(\d{7,8}|(\d{3,4}[- ]{1}\d{4}))([- ]{1}\d{1,4})?))$/).test(value); + break; + + case 'CZ': + // Test: http://regexr.com/39hhl + isValid = /^(((00)([- ]?)|\+)(420)([- ]?))?((\d{3})([- ]?)){2}(\d{3})$/.test(value); + break; + + case 'DK': + // Mathing DK phone numbers with country code in 1 of 3 formats and an + // 8 digit phone number not starting with a 0 or 1. Can have 1 space + // between each character except inside the country code. + // Test: http://regex101.com/r/sS8fO4/1 + value = $.trim(value); + isValid = (/^(\+45|0045|\(45\))?\s?[2-9](\s?\d){7}$/).test(value); + break; + case 'ES': // http://regex101.com/r/rB9mA9/1 value = $.trim(value); isValid = (/^(?:(?:(?:\+|00)34\D?))?(?:9|6)(?:\d\D?){8}$/).test(value); break; @@ -4704,17 +5481,44 @@ // http://en.wikipedia.org/wiki/Telephone_numbers_in_Morocco // Test: http://regexr.com/399n8 value = $.trim(value); isValid = (/^(?:(?:(?:\+|00)212[\s]?(?:[\s]?\(0\)[\s]?)?)|0){1}(?:5[\s.-]?[2-3]|6[\s.-]?[13-9]){1}[0-9]{1}(?:[\s.-]?\d{2}){3}$/).test(value); break; - + case 'PK': // http://regex101.com/r/yH8aV9/2 value = $.trim(value); isValid = (/^0?3[0-9]{2}[0-9]{7}$/).test(value); break; - + + case 'RO': + // All mobile network and land line + // http://regexr.com/39fv1 + isValid = (/^(\+4|)?(07[0-8]{1}[0-9]{1}|02[0-9]{2}|03[0-9]{2}){1}?(\s|\.|\-)?([0-9]{3}(\s|\.|\-|)){2}$/g).test(value); + break; + + case 'RU': + // http://regex101.com/r/gW7yT5/5 + isValid = (/^((8|\+7|007)[\-\.\/ ]?)?([\(\/\.]?\d{3}[\)\/\.]?[\-\.\/ ]?)?[\d\-\.\/ ]{7,10}$/g).test(value); + break; + + case 'SK': + // Test: http://regexr.com/39hhl + isValid = /^(((00)([- ]?)|\+)(420)([- ]?))?((\d{3})([- ]?)){2}(\d{3})$/.test(value); + break; + + case 'TH': + // http://regex101.com/r/vM5mZ4/2 + isValid = (/^0\(?([6|8-9]{2})*-([0-9]{3})*-([0-9]{4})$/).test(value); + break; + + case 'VE': + // http://regex101.com/r/eM2yY0/6 + value = $.trim(value); + isValid = (/^0(?:2(?:12|4[0-9]|5[1-9]|6[0-9]|7[0-8]|8[1-35-8]|9[1-5]|3[45789])|4(?:1[246]|2[46]))\d{7}$/).test(value); + break; + case 'US': /* falls through */ default: // Make sure US phone numbers have 10 digits // May start with 1, +1, or 1-; should discard @@ -4782,14 +5586,25 @@ $.fn.bootstrapValidator.validators.remote = { html5Attributes: { message: 'message', name: 'name', type: 'type', - url: 'url' + url: 'url', + delay: 'delay' }, /** + * Destroy the timer when destroying the bootstrapValidator (using validator.destroy() method) + */ + destroy: function(validator, $field, options) { + if ($field.data('bv.remote.timer')) { + clearTimeout($field.data('bv.remote.timer')); + $field.removeData('bv.remote.timer'); + } + }, + + /** * Request a remote server to check the input value * * @param {BootstrapValidator} validator Plugin instance * @param {jQuery} $field Field element * @param {Object} options Can consist of the following keys: @@ -4797,25 +5612,28 @@ * - type {String} [optional] Can be GET or POST (default) * - data {Object|Function} [optional]: By default, it will take the value * { * <fieldName>: <fieldValue> * } + * - delay * - name {String} [optional]: Override the field name for the request. * - message: The invalid message * - headers: Additional headers - * @returns {Boolean|Deferred} + * @returns {Deferred} */ validate: function(validator, $field, options) { - var value = $field.val(); + var value = $field.val(), + dfd = new $.Deferred(); if (value === '') { - return true; + dfd.resolve($field, 'remote', { valid: true }); + return dfd; } var name = $field.attr('data-bv-field'), data = options.data || {}, url = options.url, - type = options.type || 'POST', + type = options.type || 'GET', headers = options.headers || {}; // Support dynamic data if ('function' === typeof data) { data = data.call(this, validator); @@ -4825,28 +5643,42 @@ if ('function' === typeof url) { url = url.call(this, validator); } data[options.name || name] = value; + function runCallback() { + var xhr = $.ajax({ + type: type, + headers: headers, + url: url, + dataType: 'json', + data: data + }); + xhr.then(function(response) { + response.valid = response.valid === true || response.valid === 'true'; + dfd.resolve($field, 'remote', response); + }); - var dfd = new $.Deferred(); - var xhr = $.ajax({ - type: type, - headers: headers, - url: url, - dataType: 'json', - data: data - }); - xhr.then(function(response) { - dfd.resolve($field, 'remote', response.valid === true || response.valid === 'true', response.message ? response.message : null); - }); + dfd.fail(function() { + xhr.abort(); + }); - dfd.fail(function() { - xhr.abort(); - }); + return dfd; + } + + if (options.delay) { + // Since the form might have multiple fields with the same name + // I have to attach the timer to the field element + if ($field.data('bv.remote.timer')) { + clearTimeout($field.data('bv.remote.timer')); + } - return dfd; + $field.data('bv.remote.timer', setTimeout(runCallback, options.delay)); + return dfd; + } else { + return runCallback(); + } } }; }(window.jQuery)); ;(function($) { $.fn.bootstrapValidator.i18n.rtn = $.extend($.fn.bootstrapValidator.i18n.rtn || {}, { @@ -5106,18 +5938,21 @@ min: 'min', max: 'max' }, enableByHtml5: function($field) { - var maxLength = $field.attr('maxlength'); + var options = {}, + maxLength = $field.attr('maxlength'), + minLength = $field.attr('minlength'); if (maxLength) { - return { - max: parseInt(maxLength, 10) - }; + options.max = parseInt(maxLength, 10); } + if (minLength) { + options.min = parseInt(minLength, 10); + } - return false; + return $.isEmptyObject(options) ? false : options; }, /** * Check if the length of element value is less or more than given number * @@ -5179,11 +6014,12 @@ }); $.fn.bootstrapValidator.validators.uri = { html5Attributes: { message: 'message', - allowlocal: 'allowLocal' + allowlocal: 'allowLocal', + protocol: 'protocol' }, enableByHtml5: function($field) { return ('url' === $field.attr('type')); }, @@ -5194,10 +6030,11 @@ * @param {BootstrapValidator} validator The validator plugin instance * @param {jQuery} $field Field element * @param {Object} options * - message: The error message * - allowLocal: Allow the private and local network IP. Default to false + * - protocol: The protocols, separated by a comma. Default to "http, https, ftp" * @returns {Boolean} */ validate: function(validator, $field, options) { var value = $field.val(); if (value === '') { @@ -5231,15 +6068,18 @@ // (since they are broadcast/network addresses) // // - Added exclusion of private, reserved and/or local networks ranges // unless `allowLocal` is true // + // - Added possibility of choosing a custom protocol + // var allowLocal = options.allowLocal === true || options.allowLocal === 'true', + protocol = (options.protocol || 'http, https, ftp').split(',').join('|').replace(/\s/g, ''), urlExp = new RegExp( "^" + // protocol identifier - "(?:(?:https?|ftp)://)" + + "(?:(?:" + protocol + ")://)" + // user:pass authentication "(?:\\S+(?::\\S*)?@)?" + "(?:" + // IP address exclusion // private & local networks @@ -5269,11 +6109,11 @@ // port number "(?::\\d{2,5})?" + // resource path "(?:/[^\\s]*)?" + "$", "i" - ); + ); return urlExp.test(value); } }; }(window.jQuery)); @@ -5325,48 +6165,49 @@ }(window.jQuery)); ;(function($) { $.fn.bootstrapValidator.i18n.vat = $.extend($.fn.bootstrapValidator.i18n.vat || {}, { 'default': 'Please enter a valid VAT number', countryNotSupported: 'The country code %s is not supported', - country: 'Please enter a valid %s VAT number', + country: 'Please enter a valid VAT number in %s', countries: { - AT: 'Austrian', - BE: 'Belgian', - BG: 'Bulgarian', - BR: 'Brazilian', - CH: 'Swiss', - CY: 'Cypriot', - CZ: 'Czech', - DE: 'German', - DK: 'Danish', - EE: 'Estonian', - ES: 'Spanish', - FI: 'Finnish', - FR: 'French', + AT: 'Austria', + BE: 'Belgium', + BG: 'Bulgaria', + BR: 'Brazil', + CH: 'Switzerland', + CY: 'Cyprus', + CZ: 'Czech Republic', + DE: 'Germany', + DK: 'Denmark', + EE: 'Estonia', + ES: 'Spain', + FI: 'Finland', + FR: 'France', GB: 'United Kingdom', GR: 'Greek', EL: 'Greek', - HU: 'Hungarian', - HR: 'Croatian', - IE: 'Irish', + HU: 'Hungary', + HR: 'Croatia', + IE: 'Ireland', IS: 'Iceland', - IT: 'Italian', - LT: 'Lithuanian', + IT: 'Italy', + LT: 'Lithuania', LU: 'Luxembourg', - LV: 'Latvian', - MT: 'Maltese', - NL: 'Dutch', - NO: 'Norwegian', - PL: 'Polish', - PT: 'Portuguese', - RO: 'Romanian', - RU: 'Russian', - RS: 'Serbian', - SE: 'Swedish', - SI: 'Slovenian', - SK: 'Slovak', - ZA: 'South African' + LV: 'Latvia', + MT: 'Malta', + NL: 'Netherlands', + NO: 'Norway', + PL: 'Poland', + PT: 'Portugal', + RO: 'Romania', + RU: 'Russia', + RS: 'Serbia', + SE: 'Sweden', + SI: 'Slovenia', + SK: 'Slovakia', + VE: 'Venezuela', + ZA: 'South Africa' } }); $.fn.bootstrapValidator.validators.vat = { html5Attributes: { @@ -5375,11 +6216,12 @@ }, // Supported country codes COUNTRY_CODES: [ 'AT', 'BE', 'BG', 'BR', 'CH', 'CY', 'CZ', 'DE', 'DK', 'EE', 'EL', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', - 'IE', 'IS', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'RU', 'RS', 'SE', 'SK', 'SI', 'ZA' + 'IE', 'IS', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'RU', 'RS', 'SE', 'SK', 'SI', 'VE', + 'ZA' ], /** * Validate an European VAT number * @@ -5434,19 +6276,21 @@ * * @param {String} value VAT number * @returns {Boolean} */ _at: function(value) { - if (!/^ATU[0-9]{8}$/.test(value)) { + if (/^ATU[0-9]{8}$/.test(value)) { + value = value.substr(2); + } + if (!/^U[0-9]{8}$/.test(value)) { return false; } - value = value.substr(3); + value = value.substr(1); var sum = 0, weight = [1, 2, 1, 2, 1, 2, 1], temp = 0; - for (var i = 0; i < 7; i++) { temp = parseInt(value.charAt(i), 10) * weight[i]; if (temp > 9) { temp = Math.floor(temp / 10) + temp % 10; } @@ -5469,19 +6313,20 @@ * * @param {String} value VAT number * @returns {Boolean} */ _be: function(value) { - if (!/^BE[0]{0,1}[0-9]{9}$/.test(value)) { + if (/^BE[0]{0,1}[0-9]{9}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0]{0,1}[0-9]{9}$/.test(value)) { return false; } - value = value.substr(2); if (value.length === 9) { value = '0' + value; } - if (value.substr(1, 1) === '0') { return false; } var sum = parseInt(value.substr(0, 8), 10) + parseInt(value.substr(8, 2), 10); @@ -5499,15 +6344,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _bg: function(value) { - if (!/^BG[0-9]{9,10}$/.test(value)) { + if (/^BG[0-9]{9,10}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{9,10}$/.test(value)) { return false; } - value = value.substr(2); var sum = 0, i = 0; // Legal entities if (value.length === 9) { for (i = 0; i < 8; i++) { @@ -5646,15 +6493,18 @@ * * @param {String} value VAT number * @returns {Boolean} */ _ch: function(value) { - if (!/^CHE[0-9]{9}(MWST)?$/.test(value)) { + if (/^CHE[0-9]{9}(MWST)?$/.test(value)) { + value = value.substr(2); + } + if (!/^E[0-9]{9}(MWST)?$/.test(value)) { return false; } - value = value.substr(3); + value = value.substr(1); var sum = 0, weight = [5, 4, 3, 2, 7, 6, 5, 4]; for (var i = 0; i < 8; i++) { sum += parseInt(value.charAt(i), 10) * weight[i]; } @@ -5678,16 +6528,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _cy: function(value) { - if (!/^CY[0-5|9]{1}[0-9]{7}[A-Z]{1}$/.test(value)) { + if (/^CY[0-5|9]{1}[0-9]{7}[A-Z]{1}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-5|9]{1}[0-9]{7}[A-Z]{1}$/.test(value)) { return false; } - value = value.substr(2); - // Do not allow to start with "12" if (value.substr(0, 2) === '12') { return false; } @@ -5722,16 +6573,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _cz: function(value) { - if (!/^CZ[0-9]{8,10}$/.test(value)) { + if (/^CZ[0-9]{8,10}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{8,10}$/.test(value)) { return false; } - value = value.substr(2); - var sum = 0, i = 0; if (value.length === 8) { // Do not allow to start with '9' if (value.charAt(0) + '' === '9') { @@ -5809,15 +6661,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _de: function(value) { - if (!/^DE[0-9]{9}$/.test(value)) { + if (/^DE[0-9]{9}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{9}$/.test(value)) { return false; } - value = value.substr(2); return $.fn.bootstrapValidator.helpers.mod11And10(value); }, /** * Validate Danish VAT number @@ -5827,15 +6681,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _dk: function(value) { - if (!/^DK[0-9]{8}$/.test(value)) { + if (/^DK[0-9]{8}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{8}$/.test(value)) { return false; } - value = value.substr(2); var sum = 0, weight = [2, 7, 6, 5, 4, 3, 2, 1]; for (var i = 0; i < 8; i++) { sum += parseInt(value.charAt(i), 10) * weight[i]; } @@ -5851,18 +6707,19 @@ * * @param {String} value VAT number * @returns {Boolean} */ _ee: function(value) { - if (!/^EE[0-9]{9}$/.test(value)) { + if (/^EE[0-9]{9}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{9}$/.test(value)) { return false; } - value = value.substr(2); var sum = 0, weight = [3, 7, 1, 3, 7, 1, 3, 7, 1]; - for (var i = 0; i < 9; i++) { sum += parseInt(value.charAt(i), 10) * weight[i]; } return (sum % 10 === 0); @@ -5881,15 +6738,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _es: function(value) { - if (!/^ES[0-9A-Z][0-9]{7}[0-9A-Z]$/.test(value)) { + if (/^ES[0-9A-Z][0-9]{7}[0-9A-Z]$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9A-Z][0-9]{7}[0-9A-Z]$/.test(value)) { return false; } - value = value.substr(2); var dni = function(value) { var check = parseInt(value.substr(0, 8), 10); check = 'TRWAGMYFPDXBNJZSQVHLCKE'[check % 23]; return (check + '' === value.substr(8, 1)); }, @@ -5945,18 +6804,19 @@ * * @param {String} value VAT number * @returns {Boolean} */ _fi: function(value) { - if (!/^FI[0-9]{8}$/.test(value)) { + if (/^FI[0-9]{8}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{8}$/.test(value)) { return false; } - value = value.substr(2); var sum = 0, weight = [7, 9, 10, 5, 8, 4, 2, 1]; - for (var i = 0; i < 8; i++) { sum += parseInt(value.charAt(i), 10) * weight[i]; } return (sum % 11 === 0); @@ -5972,16 +6832,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _fr: function(value) { - if (!/^FR[0-9A-Z]{2}[0-9]{9}$/.test(value)) { + if (/^FR[0-9A-Z]{2}[0-9]{9}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9A-Z]{2}[0-9]{9}$/.test(value)) { return false; } - value = value.substr(2); - if (!$.fn.bootstrapValidator.helpers.luhn(value.substr(2))) { return false; } if (/^[0-9]{2}$/.test(value.substr(0, 2))) { @@ -6009,20 +6870,27 @@ * * @param {String} value VAT number * @returns {Boolean} */ _gb: function(value) { - if (!/^GB[0-9]{9}$/.test(value) /* Standard */ - && !/^GB[0-9]{12}$/.test(value) /* Branches */ - && !/^GBGD[0-9]{3}$/.test(value) /* Government department */ - && !/^GBHA[0-9]{3}$/.test(value) /* Health authority */ - && !/^GB(GD|HA)8888[0-9]{5}$/.test(value)) + if (/^GB[0-9]{9}$/.test(value) /* Standard */ + || /^GB[0-9]{12}$/.test(value) /* Branches */ + || /^GBGD[0-9]{3}$/.test(value) /* Government department */ + || /^GBHA[0-9]{3}$/.test(value) /* Health authority */ + || /^GB(GD|HA)8888[0-9]{5}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{9}$/.test(value) + && !/^[0-9]{12}$/.test(value) + && !/^GD[0-9]{3}$/.test(value) + && !/^HA[0-9]{3}$/.test(value) + && !/^(GD|HA)8888[0-9]{5}$/.test(value)) + { return false; } - value = value.substr(2); var length = value.length; if (length === 5) { var firstTwo = value.substr(0, 2), lastThree = parseInt(value.substr(2), 10); return ('GD' === firstTwo && lastThree < 500) || ('HA' === firstTwo && lastThree >= 500); @@ -6059,15 +6927,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _gr: function(value) { - if (!/^GR[0-9]{9}$/.test(value)) { + if (/^(GR|EL)[0-9]{9}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{9}$/.test(value)) { return false; } - value = value.substr(2); if (value.length === 8) { value = '0' + value; } var sum = 0, @@ -6080,15 +6950,10 @@ return (sum + '' === value.substr(8, 1)); }, // EL is traditionally prefix of Greek VAT numbers _el: function(value) { - if (!/^EL[0-9]{9}$/.test(value)) { - return false; - } - - value = 'GR' + value.substr(2); return this._gr(value); }, /** * Validate Hungarian VAT number @@ -6098,15 +6963,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _hu: function(value) { - if (!/^HU[0-9]{8}$/.test(value)) { + if (/^HU[0-9]{8}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{8}$/.test(value)) { return false; } - value = value.substr(2); var sum = 0, weight = [9, 7, 3, 1, 9, 7, 3, 1]; for (var i = 0; i < 8; i++) { sum += parseInt(value.charAt(i), 10) * weight[i]; @@ -6123,15 +6990,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _hr: function(value) { - if (!/^HR[0-9]{11}$/.test(value)) { + if (/^HR[0-9]{11}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{11}$/.test(value)) { return false; } - value = value.substr(2); return $.fn.bootstrapValidator.helpers.mod11And10(value); }, /** * Validate Irish VAT number @@ -6141,15 +7010,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _ie: function(value) { - if (!/^IE[0-9]{1}[0-9A-Z\*\+]{1}[0-9]{5}[A-Z]{1,2}$/.test(value)) { + if (/^IE[0-9]{1}[0-9A-Z\*\+]{1}[0-9]{5}[A-Z]{1,2}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{1}[0-9A-Z\*\+]{1}[0-9]{5}[A-Z]{1,2}$/.test(value)) { return false; } - value = value.substr(2); var getCheckDigit = function(value) { while (value.length < 7) { value = '0' + value; } var alphabet = 'WABCDEFGHIJKLMNOPQRSTUV', @@ -6181,11 +7052,14 @@ * * @params {String} value VAT number * @returns {Boolean} */ _is: function(value) { - return /^IS\d{5,6}$/.test(value); + if (/^IS[0-9]{5,6}$/.test(value)) { + value = value.substr(2); + } + return /^[0-9]{5,6}$/.test(value); }, /** * Validate Italian VAT number, which consists of 11 digits. * - First 7 digits are a company identifier @@ -6198,15 +7072,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _it: function(value) { - if (!/^IT[0-9]{11}$/.test(value)) { + if (/^IT[0-9]{11}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{11}$/.test(value)) { return false; } - value = value.substr(2); if (parseInt(value.substr(0, 7), 10) === 0) { return false; } var lastThree = parseInt(value.substr(7, 3), 10); @@ -6229,15 +7105,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _lt: function(value) { - if (!/^LT([0-9]{7}1[0-9]{1}|[0-9]{10}1[0-9]{1})$/.test(value)) { + if (/^LT([0-9]{7}1[0-9]{1}|[0-9]{10}1[0-9]{1})$/.test(value)) { + value = value.substr(2); + } + if (!/^([0-9]{7}1[0-9]{1}|[0-9]{10}1[0-9]{1})$/.test(value)) { return false; } - value = value.substr(2); var length = value.length, sum = 0, i; for (i = 0; i < length - 1; i++) { sum += parseInt(value.charAt(i), 10) * (1 + i % 9); @@ -6261,15 +7139,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _lu: function(value) { - if (!/^LU[0-9]{8}$/.test(value)) { + if (/^LU[0-9]{8}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{8}$/.test(value)) { return false; } - value = value.substr(2); return ((parseInt(value.substr(0, 6), 10) % 89) + '' === value.substr(6, 2)); }, /** * Validate Latvian VAT number @@ -6279,15 +7159,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _lv: function(value) { - if (!/^LV[0-9]{11}$/.test(value)) { + if (/^LV[0-9]{11}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{11}$/.test(value)) { return false; } - value = value.substr(2); var first = parseInt(value.charAt(0), 10), sum = 0, weight = [], i, length = value.length; @@ -6330,15 +7212,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _mt: function(value) { - if (!/^MT[0-9]{8}$/.test(value)) { + if (/^MT[0-9]{8}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{8}$/.test(value)) { return false; } - value = value.substr(2); var sum = 0, weight = [3, 4, 6, 7, 8, 9, 10, 1]; for (var i = 0; i < 8; i++) { sum += parseInt(value.charAt(i), 10) * weight[i]; @@ -6355,14 +7239,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _nl: function(value) { - if (!/^NL[0-9]{9}B[0-9]{2}$/.test(value)) { + if (/^NL[0-9]{9}B[0-9]{2}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{9}B[0-9]{2}$/.test(value)) { return false; } - value = value.substr(2); + var sum = 0, weight = [9, 8, 7, 6, 5, 4, 3, 2]; for (var i = 0; i < 8; i++) { sum += parseInt(value.charAt(i), 10) * weight[i]; } @@ -6380,14 +7267,17 @@ * @see http://www.brreg.no/english/coordination/number.html * @param {String} value VAT number * @returns {Boolean} */ _no: function(value) { - if (!/^NO[0-9]{9}$/.test(value)) { + if (/^NO[0-9]{9}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{9}$/.test(value)) { return false; } - value = value.substr(2); + var sum = 0, weight = [3, 2, 7, 6, 5, 4, 3, 2]; for (var i = 0; i < 8; i++) { sum += parseInt(value.charAt(i), 10) * weight[i]; } @@ -6407,15 +7297,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _pl: function(value) { - if (!/^PL[0-9]{10}$/.test(value)) { + if (/^PL[0-9]{10}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{10}$/.test(value)) { return false; } - value = value.substr(2); var sum = 0, weight = [6, 5, 7, 2, 3, 4, 5, 6, 7, -1]; for (var i = 0; i < 10; i++) { sum += parseInt(value.charAt(i), 10) * weight[i]; @@ -6432,15 +7324,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _pt: function(value) { - if (!/^PT[0-9]{9}$/.test(value)) { + if (/^PT[0-9]{9}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{9}$/.test(value)) { return false; } - value = value.substr(2); var sum = 0, weight = [9, 8, 7, 6, 5, 4, 3, 2]; for (var i = 0; i < 8; i++) { sum += parseInt(value.charAt(i), 10) * weight[i]; @@ -6460,14 +7354,16 @@ * * @param {String} value VAT number * @returns {Boolean} */ _ro: function(value) { - if (!/^RO[1-9][0-9]{1,9}$/.test(value)) { + if (/^RO[1-9][0-9]{1,9}$/.test(value)) { + value = value.substr(2); + } + if (!/^[1-9][0-9]{1,9}$/.test(value)) { return false; } - value = value.substr(2); var length = value.length, weight = [7, 5, 3, 2, 1, 7, 5, 3, 2].slice(10 - length), sum = 0; for (var i = 0; i < length - 1; i++) { @@ -6483,15 +7379,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _ru: function(value) { - if (!/^RU([0-9]{9}|[0-9]{12})$/.test(value)) { + if (/^RU([0-9]{10}|[0-9]{12})$/.test(value)) { + value = value.substr(2); + } + if (!/^([0-9]{10}|[0-9]{12})$/.test(value)) { return false; } - value = value.substr(2); var i = 0; if (value.length === 10) { var sum = 0, weight = [2, 4, 10, 3, 5, 9, 4, 6, 8, 0]; for (i = 0; i < 10; i++) { @@ -6533,15 +7431,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _rs: function(value) { - if (!/^RS[0-9]{9}$/.test(value)) { + if (/^RS[0-9]{9}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{9}$/.test(value)) { return false; } - value = value.substr(2); var sum = 10, temp = 0; for (var i = 0; i < 8; i++) { temp = (parseInt(value.charAt(i), 10) + sum) % 10; if (temp === 0) { @@ -6561,15 +7461,18 @@ * * @param {String} value VAT number * @returns {Boolean} */ _se: function(value) { - if (!/^SE[0-9]{10}01$/.test(value)) { + if (/^SE[0-9]{10}01$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{10}01$/.test(value)) { return false; } - value = value.substr(2, 10); + value = value.substr(0, 10); return $.fn.bootstrapValidator.helpers.luhn(value); }, /** * Validate Slovenian VAT number @@ -6579,15 +7482,17 @@ * * @param {String} value VAT number * @returns {Boolean} */ _si: function(value) { - if (!/^SI[0-9]{8}$/.test(value)) { + if (/^SI[0-9]{8}$/.test(value)) { + value = value.substr(2); + } + if (!/^[0-9]{8}$/.test(value)) { return false; } - value = value.substr(2); var sum = 0, weight = [8, 7, 6, 5, 4, 3, 2]; for (var i = 0; i < 7; i++) { sum += parseInt(value.charAt(i), 10) * weight[i]; @@ -6607,28 +7512,73 @@ * * @param {String} value VAT number * @returns {Boolean} */ _sk: function(value) { - if (!/^SK[1-9][0-9][(2-4)|(6-9)][0-9]{7}$/.test(value)) { + if (/^SK[1-9][0-9][(2-4)|(6-9)][0-9]{7}$/.test(value)) { + value = value.substr(2); + } + if (!/^[1-9][0-9][(2-4)|(6-9)][0-9]{7}$/.test(value)) { return false; } - return (parseInt(value.substr(2), 10) % 11 === 0); + return (parseInt(value, 10) % 11 === 0); }, /** + * Validate Venezuelan VAT number (RIF) + * Examples: + * - Valid: VEJ309272292, VEV242818101, VEJ000126518, VEJ000458324, J309272292, V242818101, J000126518, J000458324 + * - Invalid: VEJ309272293, VEV242818100, J000126519, J000458323 + * + * @param {String} value VAT number + * @returns {Boolean} + */ + _ve: function(value) { + if (/^VE[VEJPG][0-9]{9}$/.test(value)) { + value = value.substr(2); + } + if (!/^[VEJPG][0-9]{9}$/.test(value)) { + return false; + } + + var types = { + 'V': 4, + 'E': 8, + 'J': 12, + 'P': 16, + 'G': 20 + }, + sum = types[value.charAt(0)], + weight = [3, 2, 7, 6, 5, 4, 3, 2]; + + for (var i = 0; i < 8; i++) { + sum += parseInt(value.charAt(i + 1), 10) * weight[i]; + } + + sum = 11 - sum % 11; + if (sum === 11 || sum === 10) { + sum = 0; + } + return (sum + '' === value.substr(9, 1)); + }, + + /** * Validate South African VAT number * Examples: * - Valid: 4012345678 * - Invalid: 40123456789, 3012345678 * * @params {String} value VAT number * @returns {Boolean} */ _za: function(value) { - return /^ZA4\d{9}$/.test(value); + if (/^ZA4[0-9]{9}$/.test(value)) { + value = value.substr(2); + } + + return /^4[0-9]{9}$/.test(value); } }; }(window.jQuery)); ;(function($) { $.fn.bootstrapValidator.i18n.vin = $.extend($.fn.bootstrapValidator.i18n.vin || {}, { @@ -6679,34 +7629,38 @@ } }; }(window.jQuery)); ;(function($) { $.fn.bootstrapValidator.i18n.zipCode = $.extend($.fn.bootstrapValidator.i18n.zipCode || {}, { - 'default': 'Please enter a valid zip code', + 'default': 'Please enter a valid postal code', countryNotSupported: 'The country code %s is not supported', - country: 'Please enter a valid %s', + country: 'Please enter a valid postal code in %s', countries: { - BR: 'Brazilian postal code', - CA: 'Canadian postal code', - DK: 'Danish postal code', - GB: 'United Kingdom postal code', - IT: 'Italian postal code', - MA: 'Moroccan postal code', - NL: 'Dutch postal code', - SE: 'Swiss postal code', - SG: 'Singapore postal code', - US: 'US zip code' + BR: 'Brazil', + CA: 'Canada', + CZ: 'Czech Republic', + DK: 'Denmark', + GB: 'United Kingdom', + IT: 'Italy', + MA: 'Morocco', + NL: 'Netherlands', + RO: 'Romania', + RU: 'Russia', + SE: 'Sweden', + SG: 'Singapore', + SK: 'Slovakia', + US: 'USA' } }); $.fn.bootstrapValidator.validators.zipCode = { html5Attributes: { message: 'message', country: 'country' }, - COUNTRY_CODES: ['BR', 'CA', 'DK', 'GB', 'IT', 'MA', 'NL', 'SE', 'SG', 'US'], + COUNTRY_CODES: ['BR', 'CA', 'CZ', 'DK', 'GB', 'IT', 'MA', 'NL', 'RO', 'RU', 'SE', 'SG', 'SK', 'US'], /** * Return true if and only if the input value is a valid country zip code * * @param {BootstrapValidator} validator The validator plugin instance @@ -6754,10 +7708,15 @@ case 'CA': isValid = /^(?:A|B|C|E|G|H|J|K|L|M|N|P|R|S|T|V|X|Y){1}[0-9]{1}(?:A|B|C|E|G|H|J|K|L|M|N|P|R|S|T|V|W|X|Y|Z){1}\s?[0-9]{1}(?:A|B|C|E|G|H|J|K|L|M|N|P|R|S|T|V|W|X|Y|Z){1}[0-9]{1}$/i.test(value); break; + case 'CZ': + // Test: http://regexr.com/39hhr + isValid = /^(\d{3})([ ]?)(\d{2})$/.test(value); + break; + case 'DK': isValid = /^(DK(-|\s)?)?\d{4}$/i.test(value); break; case 'GB': @@ -6776,18 +7735,31 @@ // http://en.wikipedia.org/wiki/Postal_codes_in_the_Netherlands case 'NL': isValid = /^[1-9][0-9]{3} ?(?!sa|sd|ss)[a-z]{2}$/i.test(value); break; + + case 'RO': + isValid = /^(0[1-8]{1}|[1-9]{1}[0-5]{1})?[0-9]{4}$/i.test(value); + break; + case 'RU': + isValid = /^[0-9]{6}$/i.test(value); + break; + case 'SE': isValid = /^(S-)?\d{3}\s?\d{2}$/i.test(value); break; case 'SG': isValid = /^([0][1-9]|[1-6][0-9]|[7]([0-3]|[5-9])|[8][0-2])(\d{4})$/i.test(value); break; - + + case 'SK': + // Test: http://regexr.com/39hhr + isValid = /^(\d{3})([ ]?)(\d{2})$/.test(value); + break; + case 'US': /* falls through */ default: isValid = /^\d{4,5}([\-]?\d{4})?$/.test(value); break;