/*! * 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 * @author https://twitter.com/nghuuphuoc * @copyright (c) 2013 - 2014 Nguyen Huu Phuoc * @license MIT */ (function ($) { const BootstrapValidator = function (form, options) { this.$form = $(form) this.options = $.extend({}, $.fn.bootstrapValidator.DEFAULT_OPTIONS, options) this.$invalidFields = $([]) // Array of invalid fields this.$submitButton = null // The submit button which is clicked to submit form this.$hiddenButton = null // Validating status this.STATUS_NOT_VALIDATED = 'NOT_VALIDATED' this.STATUS_VALIDATING = 'VALIDATING' this.STATUS_INVALID = 'INVALID' this.STATUS_VALID = 'VALID' // Determine the event that is fired when user change the field value // Most modern browsers supports input event except IE 7, 8. // IE 9 supports input event but the event is still not fired if I press the backspace key. // Get IE version // https://gist.github.com/padolsey/527683/#comment-7595 const ieVersion = (function () { let v = 3; const div = document.createElement('div'); const a = div.all || [] while (div.innerHTML = '', a[0]) {} return v > 4 ? v : !v }()) const el = document.createElement('div') this._changeEvent = (ieVersion === 9 || !('oninput' in el)) ? 'keyup' : 'input' // 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() } BootstrapValidator.prototype = { constructor: BootstrapValidator, /** * Init form */ _init: function () { const that = this const 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'), fieldRemoved: this.$form.attr('data-bv-events-field-removed'), fieldInit: this.$form.attr('data-bv-events-field-init'), 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') } } this.$form // Disable client side validation in HTML 5 .attr('novalidate', 'novalidate') .addClass(this.options.elementClass) // Disable the default submission first .on('submit.bv', function (e) { e.preventDefault() that.validate() }) .on('click.bv', this.options.submitButtons, function () { that.$submitButton = $(this) // The user just click the submit button that._submitIfValid = true }) // Find all fields which have either "name" or "data-bv-field" attribute .find('[name], [data-bv-field]') .each(function () { const $field = $(this) const field = $field.attr('name') || $field.attr('data-bv-field') const opts = that._parseOptions($field) if (opts) { $field.attr('data-bv-field', field) options.fields[field] = $.extend({}, opts, options.fields[field]) } }) this.options = $.extend(true, this.options, options) // When pressing Enter on any field in the form, the first submit button will do its job. // The form then will be submitted. // I create a first hidden submit button this.$hiddenButton = $('') .attr('type', 'submit') .prependTo(this.$form) .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 const $button = $(e.target).eq(0) if (that.options.submitButtons && !$button.is(that.options.submitButtons) && !$button.is(that.$hiddenButton)) { that.$form.off('submit.bv').submit() } }) for (let field in this.options.fields) { this._initField(field) } this.$form.trigger($.Event(this.options.events.formInit), { bv: this, options: this.options }) // Prepare the events if (this.options.onSuccess) { this.$form.on(this.options.events.formSuccess, function (e) { $.fn.bootstrapValidator.helpers.call(that.options.onSuccess, [e]) }) } if (this.options.onError) { this.$form.on(this.options.events.formError, function (e) { $.fn.bootstrapValidator.helpers.call(that.options.onError, [e]) }) } }, /** * Parse the validator options from HTML attributes * * @param {jQuery} $field The field element * @returns {Object} */ _parseOptions: function ($field) { const field = $field.attr('name') || $field.attr('data-bv-field') const validators = {} let validator let v // Validator name let enabled let optionName let optionValue let html5AttrName let html5AttrMap for (v in $.fn.bootstrapValidator.validators) { validator = $.fn.bootstrapValidator.validators[v] enabled = $field.attr('data-bv-' + v.toLowerCase()) + '' html5AttrMap = (typeof validator.enableByHtml5 === 'function') ? validator.enableByHtml5($field) : null if ((html5AttrMap && enabled !== 'false') || (html5AttrMap !== true && (enabled === '' || enabled === 'true'))) { // Try to parse the options via attributes validator.html5Attributes = $.extend({}, { message: 'message', onerror: 'onError', onsuccess: 'onSuccess' }, validator.html5Attributes) validators[v] = $.extend({}, html5AttrMap === true ? {} : html5AttrMap, validators[v]) for (html5AttrName in validator.html5Attributes) { optionName = validator.html5Attributes[html5AttrName] optionValue = $field.attr('data-bv-' + v.toLowerCase() + '-' + html5AttrName) if (optionValue) { if (optionValue === 'true') { optionValue = true } else if (optionValue === 'false') { optionValue = false } validators[v][optionName] = optionValue } } } } const opts = { 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'), onStatus: $field.attr('data-bv-onstatus'), onSuccess: $field.attr('data-bv-onsuccess'), onError: $field.attr('data-bv-onerror'), validators: validators } const emptyOptions = $.isEmptyObject(opts) // Check if the field options are set using HTML attributes const emptyValidators = $.isEmptyObject(validators) // Check if the field validators are set using HTML attributes if (!emptyValidators || (!emptyOptions && this.options.fields && this.options.fields[field])) { opts.validators = validators return opts } else { return null } }, /** * Init field * * @param {String|jQuery} field The field name or field element */ _initField: function (field) { let fields = $([]) switch (typeof field) { case 'object': fields = field field = field.attr('data-bv-field') break case 'string': fields = this.getFieldElements(field) fields.attr('data-bv-field', field) break default: break } if (this.options.fields[field] === null || this.options.fields[field].validators === null) { return } // We don't need to validate non-existing fields if (fields.length === 0) { delete this.options.fields[field] return } let validatorName for (validatorName in this.options.fields[field].validators) { if (!$.fn.bootstrapValidator.validators[validatorName]) { delete this.options.fields[field].validators[validatorName] } } if (this.options.fields[field].enabled === null) { this.options.fields[field].enabled = true } const that = this const total = fields.length const type = fields.attr('type') const updateAll = (total === 1) || (type === 'radio') || (type === 'checkbox') const event = (type === 'radio' || type === 'checkbox' || type === 'file' || fields.eq(0).get(0).tagName === 'SELECT') ? 'change' : this._changeEvent const trigger = (this.options.fields[field].trigger || this.options.trigger || event).split(' ') let events = $.map(trigger, function (item) { return item + '.update.bv' }).join(' ') for (let i = 0; i < total; i++) { const $field = fields.eq(i) const group = this.options.fields[field].group || this.options.group const $parent = $field.parents(group) // Allow user to indicate where the error messages are shown const container = this.options.fields[field].container || this.options.container const $message = (container && container !== 'tooltip' && container !== 'popover') ? $(container) : this._getMessageContainer($field, group) if (container && container !== 'tooltip' && container !== 'popover') { $message.addClass('has-error') } // Remove all error messages and feedback icons $message.find('.help-block[data-bv-validator][data-bv-for="' + field + '"]').remove() $parent.find('i[data-bv-icon-for="' + field + '"]').remove() // 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) if (!updateAll || i === total - 1) { $('') .css('display', 'none') .addClass('help-block') .attr('data-bv-validator', validatorName) .attr('data-bv-for', field) .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]) }) } 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') const $icon = $('') .css('display', 'none') .addClass('form-control-feedback') .attr('data-bv-icon-for', field) .insertAfter($field) // Place it after the container of checkbox/radio // so when clicking the icon, it doesn't effect to the checkbox/radio element if (type === 'checkbox' || type === 'radio') { const $fieldParent = $field.parent() if ($fieldParent.hasClass(type)) { $icon.insertAfter($fieldParent) } else if ($fieldParent.parent().hasClass(type)) { $icon.insertAfter($fieldParent.parent()) } } // 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) } // 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)) } } } // 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]) }) } 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(' ') switch (this.options.live) { case 'submitted': break case 'disabled': fields.off(events) break case 'enabled': /* falls through */ default: fields.off(events).on(events, function () { if (that._exceedThreshold($(this))) { that.validateField($(this)) } }) break } fields.trigger($.Event(this.options.events.fieldInit), { bv: this, field: field, element: fields }) }, /** * Get the error message for given field and validator * * @param {String} field The field name * @param {String} validatorName The validator name * @returns {String} */ _getMessage: function (field, validatorName) { if (!this.options.fields[field] || !$.fn.bootstrapValidator.validators[validatorName] || !this.options.fields[field].validators || !this.options.fields[field].validators[validatorName]) { return '' } const options = this.options.fields[field].validators[validatorName] switch (true) { case (!!options.message): return options.message case (!!this.options.fields[field].message): return this.options.fields[field].message case (!!$.fn.bootstrapValidator.i18n[validatorName]): return $.fn.bootstrapValidator.i18n[validatorName].default default: return this.options.message } }, /** * Get the element to place the error messages * * @param {jQuery} $field The field element * @param {String} group * @returns {jQuery} */ _getMessageContainer: function ($field, group) { const $parent = $field.parent() if ($parent.is(group)) { return $parent } let cssClasses = $parent.attr('class') if (!cssClasses) { return this._getMessageContainer($parent, group) } cssClasses = cssClasses.split(' ') const n = cssClasses.length for (let i = 0; i < n; i++) { if (/^col-(xs|sm|md|lg)-\d+$/.test(cssClasses[i]) || /^col-(xs|sm|md|lg)-offset-\d+$/.test(cssClasses[i])) { return $parent } } return this._getMessageContainer($parent, group) }, /** * Called when all validations are completed */ _submit: function () { const isValid = this.isValid() const eventType = isValid ? this.options.events.formSuccess : this.options.events.formError const e = $.Event(eventType) this.$form.trigger(e) // Call default handler // Check if whether the submit button is clicked if (this.$submitButton) { isValid ? this._onSuccess(e) : this._onError(e) } }, /** * Check if the field is excluded. * Returning true means that the field will not be validated * * @param {jQuery} $field The field element * @returns {Boolean} */ _isExcluded: function ($field) { const excludedAttr = $field.attr('data-bv-excluded') // I still need to check the 'name' attribute while initializing the field const field = $field.attr('data-bv-field') || $field.attr('name') switch (true) { case (!!field && this.options.fields && this.options.fields[field] && (this.options.fields[field].excluded === 'true' || this.options.fields[field].excluded === true)): case (excludedAttr === 'true'): case (excludedAttr === ''): return true case (!!field && this.options.fields && this.options.fields[field] && (this.options.fields[field].excluded === 'false' || this.options.fields[field].excluded === false)): case (excludedAttr === 'false'): return false default: if (this.options.excluded) { // Convert to array first if (typeof this.options.excluded === 'string') { this.options.excluded = $.map(this.options.excluded.split(','), function (item) { // Trim the spaces return $.trim(item) }) } const length = this.options.excluded.length for (let i = 0; i < length; i++) { if ((typeof this.options.excluded[i] === 'string' && $field.is(this.options.excluded[i])) || (typeof this.options.excluded[i] === 'function' && this.options.excluded[i].call(this, $field, this) === true)) { return true } } } return false } }, /** * Check if the number of characters of field value exceed the threshold or not * * @param {jQuery} $field The field element * @returns {Boolean} */ _exceedThreshold: function ($field) { const field = $field.attr('data-bv-field') const threshold = this.options.fields[field].threshold || this.options.threshold if (!threshold) { return true } const cannotType = $.inArray($field.attr('type'), ['button', 'checkbox', 'file', 'hidden', 'image', 'radio', 'reset', 'submit']) !== -1 return (cannotType || $field.val().length >= threshold) }, // --- // Events // --- /** * The default handler of error.form.bv event. * It will be called when there is a invalid field * * @param {jQuery.Event} e The jQuery event object */ _onError: function (e) { if (e.isDefaultPrevented()) { return } if (this.options.live === 'submitted') { // Enable live mode this.options.live = 'enabled' const that = this for (var field in this.options.fields) { (function (f) { const fields = that.getFieldElements(f) if (fields.length) { const type = $(fields[0]).attr('type') const event = (type === 'radio' || type === 'checkbox' || type === 'file' || $(fields[0]).get(0).tagName === 'SELECT') ? 'change' : that._changeEvent const trigger = that.options.fields[field].trigger || that.options.trigger || event const events = $.map(trigger.split(' '), function (item) { return item + '.live.bv' }).join(' ') fields.off(events).on(events, function () { if (that._exceedThreshold($(this))) { that.validateField($(this)) } }) } })(field) } } const $invalidField = this.$invalidFields.eq(0) if ($invalidField) { // Activate the tab containing the invalid field if exists const $tabPane = $invalidField.parents('.tab-pane'); let tabId if ($tabPane && (tabId = $tabPane.attr('id'))) { $('a[href="#' + tabId + '"][data-toggle="tab"]').tab('show') } // Focus to the first invalid field $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 } // Submit the form this.disableSubmitButtons(true).defaultSubmit() }, /** * Called after validating a field element * * @param {jQuery} $field The field element * @param {String} [validatorName] The validator name */ _onFieldValidated: function ($field, validatorName) { const field = $field.attr('data-bv-field') const validators = this.options.fields[field].validators const counter = {} let numValidators = 0 const data = { bv: this, field: field, element: $field, validator: validatorName } // Trigger an event after given validator completes if (validatorName) { switch ($field.data('bv.result.' + validatorName)) { case this.STATUS_INVALID: $field.trigger($.Event(this.options.events.validatorError), data) break case this.STATUS_VALID: $field.trigger($.Event(this.options.events.validatorSuccess), data) break default: break } } counter[this.STATUS_NOT_VALIDATED] = 0 counter[this.STATUS_VALIDATING] = 0 counter[this.STATUS_INVALID] = 0 counter[this.STATUS_VALID] = 0 for (var v in validators) { if (validators[v].enabled === false) { continue } numValidators++ const result = $field.data('bv.result.' + v) if (result) { counter[result]++ } } if (counter[this.STATUS_VALID] === numValidators) { // Remove from the list of invalid fields this.$invalidFields = this.$invalidFields.not($field) $field.trigger($.Event(this.options.events.fieldSuccess), data) } // If all validators are completed and there is at least one validator which doesn't pass else if (counter[this.STATUS_NOT_VALIDATED] === 0 && counter[this.STATUS_VALIDATING] === 0 && counter[this.STATUS_INVALID] > 0) { // Add to the list of invalid fields this.$invalidFields = this.$invalidFields.add($field) $field.trigger($.Event(this.options.events.fieldError), data) } }, // --- // Public methods // --- /** * Retrieve the field elements by given name * * @param {String} field The field name * @returns {null|jQuery[]} */ getFieldElements: function (field) { if (!this._cacheFields[field]) { this._cacheFields[field] = (this.options.fields[field] && this.options.fields[field].selector) ? $(this.options.fields[field].selector) : this.$form.find('[name="' + field + '"]') } return this._cacheFields[field] }, /** * Disable/enable submit buttons * * @param {Boolean} disabled Can be true or false * @returns {BootstrapValidator} */ disableSubmitButtons: function (disabled) { if (!disabled) { this.$form.find(this.options.submitButtons).removeAttr('disabled') } else if (this.options.live !== 'disabled') { // Don't disable if the live validating mode is disabled this.$form.find(this.options.submitButtons).attr('disabled', 'disabled') } return this }, /** * Validate the form * * @returns {BootstrapValidator} */ validate: function () { if (!this.options.fields) { return this } this.disableSubmitButtons(true) for (var field in this.options.fields) { this.validateField(field) } this._submit() return this }, /** * Validate given field * * @param {String|jQuery} field The field name or field element * @returns {BootstrapValidator} */ validateField: function (field) { let fields = $([]) switch (typeof field) { case 'object': fields = field field = field.attr('data-bv-field') break case 'string': fields = this.getFieldElements(field) break default: break } if (this.options.fields[field] && this.options.fields[field].enabled === false) { return this } const that = this const type = fields.attr('type') const total = (type === 'radio' || type === 'checkbox') ? 1 : fields.length const updateAll = (type === 'radio' || type === 'checkbox') const validators = this.options.fields[field].validators let validatorName let validateResult for (let i = 0; i < total; i++) { const $field = fields.eq(i) if (this._isExcluded($field)) { continue } for (validatorName in validators) { if ($field.data('bv.dfs.' + validatorName)) { $field.data('bv.dfs.' + validatorName).reject() } // Don't validate field if it is already done const result = $field.data('bv.result.' + validatorName) if (result === this.STATUS_VALID || result === this.STATUS_INVALID || validators[validatorName].enabled === false) { this._onFieldValidated($field, 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 (typeof validateResult === 'object' && validateResult.resolve) { this.updateStatus(updateAll ? field : $field, this.STATUS_VALIDATING, validatorName) $field.data('bv.dfs.' + validatorName, validateResult) validateResult.done(function ($f, v, isValid, message) { // v is validator name $f.removeData('bv.dfs.' + v) if (message) { that.updateMessage($f, v, message) } that.updateStatus(updateAll ? $f.attr('data-bv-field') : $f, isValid ? that.STATUS_VALID : that.STATUS_INVALID, v) if (isValid && that._submitIfValid === true) { // If a remote validator returns true and the form is ready to submit, then do it that._submit() } }) } // ... or object { valid: true/false, message: 'dynamic message' } else if (typeof validateResult === 'object' && validateResult.valid !== undefined && validateResult.message !== undefined) { this.updateMessage(updateAll ? field : $field, validatorName, validateResult.message) this.updateStatus(updateAll ? field : $field, validateResult.valid ? this.STATUS_VALID : this.STATUS_INVALID, validatorName) } // ... or a boolean value else if (typeof validateResult === 'boolean') { this.updateStatus(updateAll ? field : $field, validateResult ? this.STATUS_VALID : this.STATUS_INVALID, validatorName) } } } return this }, /** * Update the error message * * @param {String|jQuery} field The field name or field element * @param {String} validator The validator name * @param {String} message The message * @returns {BootstrapValidator} */ updateMessage: function (field, validator, message) { let $fields = $([]) switch (typeof field) { case 'object': $fields = field field = field.attr('data-bv-field') break case 'string': $fields = this.getFieldElements(field) break default: break } $fields.each(function () { $(this).data('bv.messages').find('.help-block[data-bv-validator="' + validator + '"][data-bv-for="' + field + '"]').html(message) }) }, /** * Update all validating results of field * * @param {String|jQuery} field The field name or field element * @param {String} status The status. Can be 'NOT_VALIDATED', 'VALIDATING', 'INVALID' or 'VALID' * @param {String} [validatorName] The validator name. If null, the method updates validity result for all validators * @returns {BootstrapValidator} */ updateStatus: function (field, status, validatorName) { let fields = $([]) switch (typeof field) { case 'object': fields = field field = field.attr('data-bv-field') break case 'string': fields = this.getFieldElements(field) break default: break } if (status === this.STATUS_NOT_VALIDATED) { // Reset the flag this._submitIfValid = false } const that = this const type = fields.attr('type') const group = this.options.fields[field].group || this.options.group const total = (type === 'radio' || type === 'checkbox') ? 1 : fields.length for (let i = 0; i < total; i++) { const $field = fields.eq(i) if (this._isExcluded($field)) { continue } const $parent = $field.parents(group) const $message = $field.data('bv.messages') const $allErrors = $message.find('.help-block[data-bv-validator][data-bv-for="' + field + '"]') const $errors = validatorName ? $allErrors.filter('[data-bv-validator="' + validatorName + '"]') : $allErrors const $icon = $parent.find('.form-control-feedback[data-bv-icon-for="' + field + '"]') const container = this.options.fields[field].container || this.options.container let isValidField = null // Update status if (validatorName) { $field.data('bv.result.' + validatorName, status) } else { for (var v in this.options.fields[field].validators) { $field.data('bv.result.' + v, status) } } // Show/hide error elements and feedback icons $errors.attr('data-bv-result', status) // Determine the tab containing the element const $tabPane = $field.parents('.tab-pane') var tabId; var $tab if ($tabPane && (tabId = $tabPane.attr('id'))) { $tab = $('a[href="#' + tabId + '"][data-toggle="tab"]').parent() } switch (status) { case this.STATUS_VALIDATING: isValidField = null this.disableSubmitButtons(true) $parent.removeClass('has-success').removeClass('has-error') if ($icon) { $icon.removeClass(this.options.feedbackIcons.valid).removeClass(this.options.feedbackIcons.invalid).addClass(this.options.feedbackIcons.validating).show() } if ($tab) { $tab.removeClass('bv-tab-success').removeClass('bv-tab-error') } break case this.STATUS_INVALID: isValidField = false this.disableSubmitButtons(true) $parent.removeClass('has-success').addClass('has-error') if ($icon) { $icon.removeClass(this.options.feedbackIcons.valid).removeClass(this.options.feedbackIcons.validating).addClass(this.options.feedbackIcons.invalid).show() } if ($tab) { $tab.removeClass('bv-tab-success').addClass('bv-tab-error') } break case this.STATUS_VALID: // If the field is valid (passes all validators) isValidField = ($allErrors.filter('[data-bv-result="' + this.STATUS_NOT_VALIDATED + '"]').length === 0) ? ($allErrors.filter('[data-bv-result="' + this.STATUS_VALID + '"]').length === $allErrors.length) // All validators are completed : null // There are some validators that have not done if (isValidField !== null) { this.disableSubmitButtons(this.$submitButton ? !this.isValid() : !isValidField) if ($icon) { $icon .removeClass(this.options.feedbackIcons.invalid).removeClass(this.options.feedbackIcons.validating).removeClass(this.options.feedbackIcons.valid) .addClass(isValidField ? this.options.feedbackIcons.valid : this.options.feedbackIcons.invalid) .show() } } $parent.removeClass('has-error has-success').addClass(this.isValidContainer($parent) ? 'has-success' : 'has-error') if ($tab) { $tab.removeClass('bv-tab-success').removeClass('bv-tab-error').addClass(this.isValidContainer($tabPane) ? 'bv-tab-success' : 'bv-tab-error') } break case this.STATUS_NOT_VALIDATED: /* falls through */ default: isValidField = null this.disableSubmitButtons(false) $parent.removeClass('has-success').removeClass('has-error') if ($icon) { $icon.removeClass(this.options.feedbackIcons.valid).removeClass(this.options.feedbackIcons.invalid).removeClass(this.options.feedbackIcons.validating).hide() } if ($tab) { $tab.removeClass('bv-tab-success').removeClass('bv-tab-error') } break } switch (true) { // Only show the first error message if it is placed inside a tooltip ... case ($icon && container === 'tooltip'): (isValidField === false) ? $icon.css('cursor', 'pointer').tooltip('destroy').tooltip({ html: true, placement: 'top', title: $allErrors.filter('[data-bv-result="' + that.STATUS_INVALID + '"]').eq(0).html() }) : $icon.css('cursor', '').tooltip('destroy') break // ... or popover case ($icon && container === 'popover'): (isValidField === false) ? $icon.css('cursor', 'pointer').popover('destroy').popover({ content: $allErrors.filter('[data-bv-result="' + that.STATUS_INVALID + '"]').eq(0).html(), html: true, placement: 'top', trigger: 'hover click' }) : $icon.css('cursor', '').popover('destroy') break default: (status === this.STATUS_INVALID) ? $errors.show() : $errors.hide() break } // Trigger an event $field.trigger($.Event(this.options.events.fieldStatus), { bv: this, field: field, element: $field, status: status }) this._onFieldValidated($field, validatorName) } return this }, /** * Check the form validity * * @returns {Boolean} */ isValid: function () { for (var field in this.options.fields) { if (!this.isValidField(field)) { return false } } return true }, /** * Check if the field is valid or not * * @param {String|jQuery} field The field name or field element * @returns {Boolean} */ isValidField: function (field) { let fields = $([]) switch (typeof field) { case 'object': fields = field field = field.attr('data-bv-field') break case 'string': fields = this.getFieldElements(field) break default: break } if (fields.length === 0 || this.options.fields[field] === null || this.options.fields[field].enabled === false) { return true } const type = fields.attr('type') const total = (type === 'radio' || type === 'checkbox') ? 1 : fields.length let $field; let validatorName; let status for (let i = 0; i < total; i++) { $field = fields.eq(i) if (this._isExcluded($field)) { continue } for (validatorName in this.options.fields[field].validators) { if (this.options.fields[field].validators[validatorName].enabled === false) { continue } status = $field.data('bv.result.' + validatorName) if (status !== this.STATUS_VALID) { return false } } } return true }, /** * Check if all fields inside a given container are valid. * It's useful when working with a wizard-like such as tab, collapse * * @param {String|jQuery} container The container selector or element * @returns {Boolean} */ isValidContainer: function (container) { const that = this const map = {} const $container = (typeof container === 'string') ? $(container) : container if ($container.length === 0) { return true } $container.find('[data-bv-field]').each(function () { const $field = $(this) const field = $field.attr('data-bv-field') if (!that._isExcluded($field) && !map[field]) { map[field] = $field } }) 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 () { const v = $(this).attr('data-bv-validator') const 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) { // The field is not valid return false } } return true }, /** * Submit the form using default submission. * It also does not perform any validations when submitting the form */ defaultSubmit: function () { if (this.$submitButton) { // Create hidden input to send the submit buttons $('') .attr('type', 'hidden') .attr('data-bv-submit-hidden', '') .attr('name', this.$submitButton.attr('name')) .val(this.$submitButton.val()) .appendTo(this.$form) } // Submit form this.$form.off('submit.bv').submit() }, // --- // Useful APIs which aren't used internally // --- /** * Get the list of invalid fields * * @returns {jQuery[]} */ getInvalidFields: function () { return this.$invalidFields }, /** * Returns the clicked submit button * * @returns {jQuery} */ getSubmitButton: function () { return this.$submitButton }, /** * Get the error messages * * @param {String|jQuery} [field] The field name or field element * If the field is not defined, the method returns all error messages of all fields * @param {String} [validator] The name of validator * If the validator is not defined, the method returns error messages of all validators * @returns {String[]} */ getMessages: function (field, validator) { const that = this let messages = [] let $fields = $([]) switch (true) { case (field && typeof field === 'object'): $fields = field break case (field && typeof field === 'string'): var f = this.getFieldElements(field) if (f.length > 0) { const type = f.attr('type') $fields = (type === 'radio' || type === 'checkbox') ? f.eq(0) : f } break default: $fields = this.$invalidFields break } const filter = validator ? '[data-bv-validator="' + validator + '"]' : '' $fields.each(function () { messages = messages.concat( $(this) .data('bv.messages') .find('.help-block[data-bv-for="' + $(this).attr('data-bv-field') + '"][data-bv-result="' + that.STATUS_INVALID + '"]' + filter) .map(function () { const v = $(this).attr('data-bv-validator') const f = $(this).attr('data-bv-for') return (that.options.fields[f].validators[v].enabled === false) ? '' : $(this).html() }) .get() ) }) 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 (typeof field === 'object') { field = field.attr('data-bv-field') } if (!this.options.fields[field]) { return null } const 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 * @param {String} value The value to set * @returns {BootstrapValidator} */ updateOption: function (field, validator, option, value) { if (typeof field === 'object') { field = field.attr('data-bv-field') } if (this.options.fields[field] && this.options.fields[field].validators[validator]) { this.options.fields[field].validators[validator][option] = value this.updateStatus(field, this.STATUS_NOT_VALIDATED, validator) } return this }, /** * Add a new field * * @param {String|jQuery} field The field name or field element * @param {Object} [options] The validator rules * @returns {BootstrapValidator} */ addField: function (field, options) { let fields = $([]) switch (typeof field) { case 'object': fields = field field = field.attr('data-bv-field') || field.attr('name') break case 'string': delete this._cacheFields[field] fields = this.getFieldElements(field) break default: break } fields.attr('data-bv-field', field) const type = fields.attr('type') const total = (type === 'radio' || type === 'checkbox') ? 1 : fields.length for (let i = 0; i < total; i++) { const $field = fields.eq(i) // Try to parse the options from HTML attributes let opts = this._parseOptions($field) opts = (opts === null) ? options : $.extend(true, options, opts) this.options.fields[field] = $.extend(true, this.options.fields[field], opts) // Update the cache this._cacheFields[field] = this._cacheFields[field] ? this._cacheFields[field].add($field) : $field // Init the element this._initField((type === 'checkbox' || type === 'radio') ? field : $field) } this.disableSubmitButtons(false) // Trigger an event this.$form.trigger($.Event(this.options.events.fieldAdded), { field: field, element: fields, options: this.options.fields[field] }) return this }, /** * Remove a given field * * @param {String|jQuery} field The field name or field element * @returns {BootstrapValidator} */ removeField: function (field) { let fields = $([]) switch (typeof field) { case 'object': fields = field field = field.attr('data-bv-field') || field.attr('name') fields.attr('data-bv-field', field) break case 'string': fields = this.getFieldElements(field) break default: break } if (fields.length === 0) { return this } const type = fields.attr('type') const total = (type === 'radio' || type === 'checkbox') ? 1 : fields.length for (let i = 0; i < total; i++) { const $field = fields.eq(i) // Remove from the list of invalid fields this.$invalidFields = this.$invalidFields.not($field) // Update the cache this._cacheFields[field] = this._cacheFields[field].not($field) } if (!this._cacheFields[field] || this._cacheFields[field].length === 0) { delete this.options.fields[field] } if (type === 'checkbox' || type === 'radio') { this._initField(field) } this.disableSubmitButtons(false) // Trigger an event this.$form.trigger($.Event(this.options.events.fieldRemoved), { field: field, element: fields }) return this }, /** * Reset given field * * @param {String|jQuery} field The field name or field element * @param {Boolean} [resetValue] If true, the method resets field value to empty or remove checked/selected attribute (for radio/checkbox) * @returns {BootstrapValidator} */ resetField: function (field, resetValue) { let $fields = $([]) switch (typeof field) { case 'object': $fields = field field = field.attr('data-bv-field') break case 'string': $fields = this.getFieldElements(field) break default: break } const total = $fields.length if (this.options.fields[field]) { for (let i = 0; i < total; i++) { for (var validator in this.options.fields[field].validators) { $fields.eq(i).removeData('bv.dfs.' + validator) } } } // Mark field as not validated yet this.updateStatus(field, this.STATUS_NOT_VALIDATED) if (resetValue) { const type = $fields.attr('type'); (type === 'radio' || type === 'checkbox') ? $fields.removeAttr('checked').removeAttr('selected') : $fields.val('') } return this }, /** * Reset the form * * @param {Boolean} [resetValue] If true, the method resets field value to empty or remove checked/selected attribute (for radio/checkbox) * @returns {BootstrapValidator} */ resetForm: function (resetValue) { for (var field in this.options.fields) { this.resetField(field, resetValue) } this.$invalidFields = $([]) this.$submitButton = null // Enable submit buttons this.disableSubmitButtons(false) return this }, /** * Revalidate given field * It's used when you need to revalidate the field which its value is updated by other plugin * * @param {String|jQuery} field The field name of field element * @returns {BootstrapValidator} */ revalidateField: function (field) { this.updateStatus(field, this.STATUS_NOT_VALIDATED) .validateField(field) return this }, /** * Enable/Disable all validators to given field * * @param {String} field The field name * @param {Boolean} enabled Enable/Disable field validators * @param {String} [validatorName] The validator name. If null, all validators will be enabled/disabled * @returns {BootstrapValidator} */ enableFieldValidators: function (field, enabled, validatorName) { const validators = this.options.fields[field].validators // Enable/disable particular validator if (validatorName && validators && validators[validatorName] && validators[validatorName].enabled !== enabled) { this.options.fields[field].validators[validatorName].enabled = enabled this.updateStatus(field, this.STATUS_NOT_VALIDATED, validatorName) } // Enable/disable all validators else if (!validatorName && this.options.fields[field].enabled !== enabled) { this.options.fields[field].enabled = enabled for (var v in validators) { this.enableFieldValidators(field, enabled, v) } } return this }, /** * Some validators have option which its value is dynamic. * For example, the zipCode validator has the country option which might be changed dynamically by a select element. * * @param {jQuery|String} field The field name or element * @param {String|Function} option The option which can be determined by: * - a string * - name of field which defines the value * - name of function which returns the value * - a function returns the value * * The callback function has the format of * callback: function(value, validator, $field) { * // value is the value of field * // validator is the BootstrapValidator instance * // $field is the field element * } * * @returns {String} */ getDynamicOption: function (field, option) { const $field = (typeof field === 'string') ? this.getFieldElements(field) : field const value = $field.val() // Option can be determined by // ... a function if (typeof option === 'function') { return $.fn.bootstrapValidator.helpers.call(option, [value, this, $field]) } // ... value of other field else if (typeof option === 'string') { const $f = this.getFieldElements(option) if ($f.length) { return $f.val() } // ... return value of callback else { return $.fn.bootstrapValidator.helpers.call(option, [value, this, $field]) || option } } return null }, /** * Destroy the plugin * It will remove all error messages, feedback icons and turn off the events */ destroy: function () { let field, fields, $field, validator, $icon, container, 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 (let i = 0; i < fields.length; i++) { $field = fields.eq(i) $field // Remove all error messages .data('bv.messages') .find('.help-block[data-bv-validator][data-bv-for="' + field + '"]').remove().end() .end() .removeData('bv.messages') // Remove feedback classes .parents(group) .removeClass('has-feedback has-error has-success') .end() // Turn off events .off('.bv') .removeAttr('data-bv-field') // Remove feedback icons, tooltip/popover container $icon = $field.parents(group).find('i[data-bv-icon-for="' + field + '"]') if ($icon) { switch (container) { case 'tooltip': $icon.tooltip('destroy').remove() break case 'popover': $icon.popover('destroy').remove() break default: $icon.remove() break } } 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) } } } this.disableSubmitButtons(false) // Enable submit buttons this.$hiddenButton.remove() // Remove the hidden button this.$form .removeClass(this.options.elementClass) .off('.bv') .removeData('bootstrapValidator') // Remove generated hidden elements .find('[data-bv-submit-hidden]').remove().end() .find('[type="submit"]').off('click.bv') } } // Plugin definition $.fn.bootstrapValidator = function (option) { const params = arguments return this.each(function () { const $this = $(this) let data = $this.data('bootstrapValidator') const options = typeof option === 'object' && option if (!data) { data = new BootstrapValidator(this, options) $this.data('bootstrapValidator', data) } // Allow to call plugin method if (typeof option === 'string') { data[option].apply(data, Array.prototype.slice.call(params, 1)) } }) } // The default options $.fn.bootstrapValidator.DEFAULT_OPTIONS = { // The form CSS class elementClass: 'bv-form', // Default invalid message message: 'This value is not valid', // The CSS selector for indicating the element consists the field // By default, each field is placed inside the
// You should adjust this option if your form group consists of many fields which not all of them need to be validated group: '.form-group', // The error messages container. It can be: // - 'tooltip' if you want to use Bootstrap tooltip to show error messages // - 'popover' if you want to use Bootstrap popover to show error messages // - a CSS selector indicating the container // In the first two cases, since the tooltip/popover should be small enough, the plugin only shows only one error message // You also can define the message container for particular field container: null, // The field will not be live validated if its length is less than this number of characters threshold: null, // Indicate fields which won't be validated // By default, the plugin will not validate the following kind of fields: // - disabled // - hidden // - invisible // // The setting consists of jQuery filters. Accept 3 formats: // - A string. Use a comma to separate filter // - An array. Each element is a filter // - An array. Each element can be a callback function // function($field, validator) { // $field is jQuery object representing the field element // validator is the BootstrapValidator instance // return true or false; // } // // The 3 following settings are equivalent: // // 1) ':disabled, :hidden, :not(:visible)' // 2) [':disabled', ':hidden', ':not(:visible)'] // 3) [':disabled', ':hidden', function($field) { // return !$field.is(':visible'); // }] excluded: [':disabled', ':hidden', ':not(:visible)'], // Shows ok/error/loading icons based on the field validity. // This feature requires Bootstrap v3.1.0 or later (http://getbootstrap.com/css/#forms-control-validation). // Since Bootstrap doesn't provide any methods to know its version, this option cannot be on/off automatically. // In other word, to use this feature you have to upgrade your Bootstrap to v3.1.0 or later. // // Examples: // - Use Glyphicons icons: // feedbackIcons: { // valid: 'glyphicon glyphicon-ok', // invalid: 'glyphicon glyphicon-remove', // validating: 'glyphicon glyphicon-refresh' // } // - Use FontAwesome icons: // feedbackIcons: { // valid: 'fa fa-check', // invalid: 'fa fa-times', // validating: 'fa fa-refresh' // } feedbackIcons: { valid: null, invalid: null, validating: null }, // The submit buttons selector // These buttons will be disabled to prevent the valid form from multiple submissions submitButtons: '[type="submit"]', // Live validating option // Can be one of 3 values: // - enabled: The plugin validates fields as soon as they are changed // - disabled: Disable the live validating. The error messages are only shown after the form is submitted // - submitted: The live validating is enabled after the form is submitted live: 'enabled', // Map the field name with validator rules fields: null, // Use custom event name to avoid window.onerror being invoked by jQuery // See https://github.com/nghuuphuoc/bootstrapvalidator/issues/630 events: { formInit: 'init.form.bv', formError: 'error.form.bv', formSuccess: 'success.form.bv', fieldAdded: 'added.field.bv', fieldRemoved: 'removed.field.bv', fieldInit: 'init.field.bv', fieldError: 'error.field.bv', fieldSuccess: 'success.field.bv', fieldStatus: 'status.field.bv', validatorError: 'error.validator.bv', validatorSuccess: 'success.validator.bv' } } // Available validators $.fn.bootstrapValidator.validators = {} // i18n $.fn.bootstrapValidator.i18n = {} $.fn.bootstrapValidator.Constructor = BootstrapValidator // Helper methods, which can be used in validator class $.fn.bootstrapValidator.helpers = { /** * Execute a callback function * * @param {String|Function} functionName Can be * - name of global function * - name of namespace function (such as A.B.C) * - a function * @param {Array} args The callback arguments */ call: function (functionName, args) { if (typeof functionName === 'function') { return functionName.apply(this, args) } else if (typeof functionName === 'string') { if (functionName.substring(functionName.length - 2) === '()') { functionName = functionName.substring(0, functionName.length - 2) } const ns = functionName.split('.') const func = ns.pop() let context = window for (let i = 0; i < ns.length; i++) { context = context[ns[i]] } return (typeof context[func] === 'undefined') ? null : context[func].apply(this, args) } }, /** * Format a string * It's used to format the error message * format('The field must between %s and %s', [10, 20]) = 'The field must between 10 and 20' * * @param {String} message * @param {Array} parameters * @returns {String} */ format: function (message, parameters) { if (!$.isArray(parameters)) { parameters = [parameters] } for (var i in parameters) { message = message.replace('%s', parameters[i]) } return message }, /** * Validate a date * * @param {Number} year The full year in 4 digits * @param {Number} month The month number * @param {Number} day The day number * @param {Boolean} [notInFuture] If true, the date must not be in the future * @returns {Boolean} */ date: function (year, month, day, notInFuture) { if (isNaN(year) || isNaN(month) || isNaN(day)) { return false } if (day.length > 2 || month.length > 2 || year.length > 4) { return false } day = parseInt(day, 10) month = parseInt(month, 10) year = parseInt(year, 10) if (year < 1000 || year > 9999 || month <= 0 || month > 12) { return false } const numDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] // Update the number of days in Feb of leap year if (year % 400 === 0 || (year % 100 !== 0 && year % 4 === 0)) { numDays[1] = 29 } // Check the day if (day <= 0 || day > numDays[month - 1]) { return false } if (notInFuture === true) { const currentDate = new Date() const currentYear = currentDate.getFullYear() const currentMonth = currentDate.getMonth() const currentDay = currentDate.getDate() return (year < currentYear || (year === currentYear && month - 1 < currentMonth) || (year === currentYear && month - 1 === currentMonth && day < currentDay)) } return true }, /** * Implement Luhn validation algorithm * Credit to https://gist.github.com/ShirtlessKirk/2134376 * * @see http://en.wikipedia.org/wiki/Luhn * @param {String} value * @returns {Boolean} */ luhn: function (value) { let length = value.length let mul = 0 const prodArr = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]] let sum = 0 while (length--) { sum += prodArr[mul][parseInt(value.charAt(length), 10)] mul ^= 1 } return (sum % 10 === 0 && sum > 0) }, /** * Implement modulus 11, 10 (ISO 7064) algorithm * * @param {String} value * @returns {Boolean} */ mod11And10: function (value) { let check = 5 const length = value.length for (let i = 0; i < length; i++) { check = (((check || 10) * 2) % 11 + parseInt(value.charAt(i), 10)) % 10 } return (check === 1) }, /** * Implements Mod 37, 36 (ISO 7064) algorithm * Usages: * mod37And36('A12425GABC1234002M') * mod37And36('002006673085', '0123456789') * * @param {String} value * @param {String} [alphabet] * @returns {Boolean} */ mod37And36: function (value, alphabet) { alphabet = alphabet || '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' const modulus = alphabet.length const length = value.length let check = Math.floor(modulus / 2) for (let i = 0; i < length; i++) { check = (((check || modulus) * 2) % (modulus + 1) + alphabet.indexOf(value.charAt(i))) % modulus } return (check === 1) } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.base64 = $.extend($.fn.bootstrapValidator.i18n.base64 || {}, { default: 'Please enter a valid base 64 encoded' }) $.fn.bootstrapValidator.validators.base64 = { /** * Return true if the input value is a base 64 encoded string. * * @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) { const value = $field.val() if (value === '') { return true } return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$/.test(value) } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.between = $.extend($.fn.bootstrapValidator.i18n.between || {}, { default: 'Please enter a value between %s and %s', notInclusive: 'Please enter a value between %s and %s strictly' }) $.fn.bootstrapValidator.validators.between = { html5Attributes: { message: 'message', min: 'min', max: 'max', inclusive: 'inclusive' }, enableByHtml5: function ($field) { if ($field.attr('type') === 'range') { return { min: $field.attr('min'), max: $field.attr('max') } } return false }, /** * Return true if the input value is between (strictly or not) two given numbers * * @param {BootstrapValidator} validator The validator plugin instance * @param {jQuery} $field Field element * @param {Object} options Can consist of the following keys: * - min * - max * * The min, max keys define the number which the field value compares to. min, max can be * - A number * - Name of field which its value defines the number * - Name of callback function that returns the number * - A callback function that returns the number * * - inclusive [optional]: Can be true or false. Default is true * - message: The invalid message * @returns {Boolean|Object} */ validate: function (validator, $field, options) { let value = $field.val() if (value === '') { return true } if (!$.isNumeric(value)) { return false } const min = $.isNumeric(options.min) ? options.min : validator.getDynamicOption($field, options.min) const max = $.isNumeric(options.max) ? options.max : validator.getDynamicOption($field, options.max) value = parseFloat(value) return (options.inclusive === true || options.inclusive === undefined) ? { valid: value >= min && value <= max, message: $.fn.bootstrapValidator.helpers.format(options.message || $.fn.bootstrapValidator.i18n.between.default, [min, max]) } : { valid: value > min && value < max, message: $.fn.bootstrapValidator.helpers.format(options.message || $.fn.bootstrapValidator.i18n.between.notInclusive, [min, max]) } } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.callback = $.extend($.fn.bootstrapValidator.i18n.callback || {}, { default: 'Please enter a valid value' }) $.fn.bootstrapValidator.validators.callback = { html5Attributes: { message: 'message', callback: 'callback' }, /** * Return result from the callback method * * @param {BootstrapValidator} validator The validator plugin instance * @param {jQuery} $field Field element * @param {Object} options Can consist of the following keys: * - callback: The callback method that passes 2 parameters: * callback: function(fieldValue, validator, $field) { * // fieldValue is the value of field * // validator is instance of BootstrapValidator * // $field is the field element * } * - message: The invalid message * @returns {Boolean|Deferred} */ validate: function (validator, $field, options) { const value = $field.val() if (options.callback) { const dfd = new $.Deferred() const response = $.fn.bootstrapValidator.helpers.call(options.callback, [value, validator, $field]) dfd.resolve($field, 'callback', typeof response === 'boolean' ? response : response.valid, typeof response === 'object' && response.message ? response.message : null) return dfd } return true } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.choice = $.extend($.fn.bootstrapValidator.i18n.choice || {}, { default: 'Please enter a valid value', less: 'Please choose %s options at minimum', more: 'Please choose %s options at maximum', between: 'Please choose %s - %s options' }) $.fn.bootstrapValidator.validators.choice = { html5Attributes: { message: 'message', min: 'min', max: 'max' }, /** * Check if the number of checked boxes are less or more than a given number * * @param {BootstrapValidator} validator The validator plugin instance * @param {jQuery} $field Field element * @param {Object} options Consists of following keys: * - min * - max * * At least one of two keys is required * The min, max keys define the number which the field value compares to. min, max can be * - A number * - Name of field which its value defines the number * - Name of callback function that returns the number * - A callback function that returns the number * * - message: The invalid message * @returns {Object} */ validate: function (validator, $field, options) { const numChoices = $field.is('select') ? validator.getFieldElements($field.attr('data-bv-field')).find('option').filter(':selected').length : validator.getFieldElements($field.attr('data-bv-field')).filter(':checked').length const min = options.min ? ($.isNumeric(options.min) ? options.min : validator.getDynamicOption($field, options.min)) : null const max = options.max ? ($.isNumeric(options.max) ? options.max : validator.getDynamicOption($field, options.max)) : null let isValid = true let message = options.message || $.fn.bootstrapValidator.i18n.choice.default if ((min && numChoices < parseInt(min, 10)) || (max && numChoices > parseInt(max, 10))) { isValid = false } switch (true) { case (!!min && !!max): message = $.fn.bootstrapValidator.helpers.format(options.message || $.fn.bootstrapValidator.i18n.choice.between, [parseInt(min, 10), parseInt(max, 10)]) break case (!!min): message = $.fn.bootstrapValidator.helpers.format(options.message || $.fn.bootstrapValidator.i18n.choice.less, parseInt(min, 10)) break case (!!max): message = $.fn.bootstrapValidator.helpers.format(options.message || $.fn.bootstrapValidator.i18n.choice.more, parseInt(max, 10)) break default: break } return { valid: isValid, message: message } } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.creditCard = $.extend($.fn.bootstrapValidator.i18n.creditCard || {}, { default: 'Please enter a valid credit card number' }) $.fn.bootstrapValidator.validators.creditCard = { /** * Return true if the input value is valid credit card number * Based on https://gist.github.com/DiegoSalazar/4075533 * * @param {BootstrapValidator} validator The validator plugin instance * @param {jQuery} $field Field element * @param {Object} [options] Can consist of the following key: * - message: The invalid message * @returns {Boolean} */ validate: function (validator, $field, options) { let value = $field.val() if (value === '') { return true } // Accept only digits, dashes or spaces if (/[^0-9-\s]+/.test(value)) { return false } value = value.replace(/\D/g, '') if (!$.fn.bootstrapValidator.helpers.luhn(value)) { return false } // Validate the card number based on prefix (IIN ranges) and length const cards = { AMERICAN_EXPRESS: { length: [15], prefix: ['34', '37'] }, DINERS_CLUB: { length: [14], prefix: ['300', '301', '302', '303', '304', '305', '36'] }, DINERS_CLUB_US: { length: [16], prefix: ['54', '55'] }, DISCOVER: { length: [16], prefix: ['6011', '622126', '622127', '622128', '622129', '62213', '62214', '62215', '62216', '62217', '62218', '62219', '6222', '6223', '6224', '6225', '6226', '6227', '6228', '62290', '62291', '622920', '622921', '622922', '622923', '622924', '622925', '644', '645', '646', '647', '648', '649', '65'] }, JCB: { length: [16], prefix: ['3528', '3529', '353', '354', '355', '356', '357', '358'] }, LASER: { length: [16, 17, 18, 19], prefix: ['6304', '6706', '6771', '6709'] }, MAESTRO: { length: [12, 13, 14, 15, 16, 17, 18, 19], prefix: ['5018', '5020', '5038', '6304', '6759', '6761', '6762', '6763', '6764', '6765', '6766'] }, MASTERCARD: { length: [16], prefix: ['51', '52', '53', '54', '55'] }, SOLO: { length: [16, 18, 19], prefix: ['6334', '6767'] }, UNIONPAY: { length: [16, 17, 18, 19], prefix: ['622126', '622127', '622128', '622129', '62213', '62214', '62215', '62216', '62217', '62218', '62219', '6222', '6223', '6224', '6225', '6226', '6227', '6228', '62290', '62291', '622920', '622921', '622922', '622923', '622924', '622925'] }, VISA: { length: [16], prefix: ['4'] } } let type, i for (type in cards) { for (i in cards[type].prefix) { if (value.substr(0, cards[type].prefix[i].length) === cards[type].prefix[i] && // Check the prefix $.inArray(value.length, cards[type].length) !== -1) // and length { return true } } } return false } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.cusip = $.extend($.fn.bootstrapValidator.i18n.cusip || {}, { default: 'Please enter a valid CUSIP number' }) $.fn.bootstrapValidator.validators.cusip = { /** * Validate a CUSIP * Examples: * - Valid: 037833100, 931142103, 14149YAR8, 126650BG6 * - Invalid: 31430F200, 022615AC2 * * @see http://en.wikipedia.org/wiki/CUSIP * @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) { let value = $field.val() if (value === '') { return true } value = value.toUpperCase() if (!/^[0-9A-Z]{9}$/.test(value)) { return false } const converted = $.map(value.split(''), function (item) { const code = item.charCodeAt(0) return (code >= 'A'.charCodeAt(0) && code <= 'Z'.charCodeAt(0)) // Replace A, B, C, ..., Z with 10, 11, ..., 35 ? (code - 'A'.charCodeAt(0) + 10) : item }) const length = converted.length let sum = 0 for (let i = 0; i < length - 1; i++) { let num = parseInt(converted[i], 10) if (i % 2 !== 0) { num *= 2 } if (num > 9) { num -= 9 } sum += num } sum = (10 - (sum % 10)) % 10 return sum === converted[length - 1] } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.cvv = $.extend($.fn.bootstrapValidator.i18n.cvv || {}, { default: 'Please enter a valid CVV number' }) $.fn.bootstrapValidator.validators.cvv = { html5Attributes: { message: 'message', ccfield: 'creditCardField' }, /** * Return true if the input value is a valid CVV number. * * @param {BootstrapValidator} validator The validator plugin instance * @param {jQuery} $field Field element * @param {Object} options Can consist of the following keys: * - creditCardField: The credit card number field. It can be null * - message: The invalid message * @returns {Boolean} */ validate: function (validator, $field, options) { const value = $field.val() if (value === '') { return true } if (!/^[0-9]{3,4}$/.test(value)) { return false } if (!options.creditCardField) { return true } // Get the credit card number let creditCard = validator.getFieldElements(options.creditCardField).val() if (creditCard === '') { return true } creditCard = creditCard.replace(/\D/g, '') // Supported credit card types const cards = { AMERICAN_EXPRESS: { length: [15], prefix: ['34', '37'] }, DINERS_CLUB: { length: [14], prefix: ['300', '301', '302', '303', '304', '305', '36'] }, DINERS_CLUB_US: { length: [16], prefix: ['54', '55'] }, DISCOVER: { length: [16], prefix: ['6011', '622126', '622127', '622128', '622129', '62213', '62214', '62215', '62216', '62217', '62218', '62219', '6222', '6223', '6224', '6225', '6226', '6227', '6228', '62290', '62291', '622920', '622921', '622922', '622923', '622924', '622925', '644', '645', '646', '647', '648', '649', '65'] }, JCB: { length: [16], prefix: ['3528', '3529', '353', '354', '355', '356', '357', '358'] }, LASER: { length: [16, 17, 18, 19], prefix: ['6304', '6706', '6771', '6709'] }, MAESTRO: { length: [12, 13, 14, 15, 16, 17, 18, 19], prefix: ['5018', '5020', '5038', '6304', '6759', '6761', '6762', '6763', '6764', '6765', '6766'] }, MASTERCARD: { length: [16], prefix: ['51', '52', '53', '54', '55'] }, SOLO: { length: [16, 18, 19], prefix: ['6334', '6767'] }, UNIONPAY: { length: [16, 17, 18, 19], prefix: ['622126', '622127', '622128', '622129', '62213', '62214', '62215', '62216', '62217', '62218', '62219', '6222', '6223', '6224', '6225', '6226', '6227', '6228', '62290', '62291', '622920', '622921', '622922', '622923', '622924', '622925'] }, VISA: { length: [16], prefix: ['4'] } } let type; let i; let creditCardType = null for (type in cards) { for (i in cards[type].prefix) { if (creditCard.substr(0, cards[type].prefix[i].length) === cards[type].prefix[i] && // Check the prefix $.inArray(creditCard.length, cards[type].length) !== -1) // and length { creditCardType = type break } } } return (creditCardType === null) ? false : ((creditCardType === 'AMERICAN_EXPRESS') ? (value.length === 4) : (value.length === 3)) } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.date = $.extend($.fn.bootstrapValidator.i18n.date || {}, { default: 'Please enter a valid date' }) $.fn.bootstrapValidator.validators.date = { html5Attributes: { message: 'message', format: 'format', separator: 'separator' }, /** * Return true if the input value is valid date * * @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 * - separator: Use to separate the date, month, and year. * By default, it is / * - format: The date format. Default is MM/DD/YYYY * The format can be: * * i) date: Consist of DD, MM, YYYY parts which are separated by the separator option * ii) date and time: * The time can consist of h, m, s parts which are separated by : * ii) date, time and A (indicating AM or PM) * @returns {Boolean} */ validate: function (validator, $field, options) { const value = $field.val() if (value === '') { return true } options.format = options.format || 'MM/DD/YYYY' // #683: Force the format to YYYY-MM-DD as the default browser behaviour when using type="date" attribute if ($field.attr('type') === 'date') { options.format = 'YYYY-MM-DD' } const formats = options.format.split(' ') let dateFormat = formats[0] let timeFormat = (formats.length > 1) ? formats[1] : null const amOrPm = (formats.length > 2) ? formats[2] : null const sections = value.split(' ') let date = sections[0] let time = (sections.length > 1) ? sections[1] : null if (formats.length !== sections.length) { return false } // Determine the separator let separator = options.separator if (!separator) { separator = (date.indexOf('/') !== -1) ? '/' : ((date.indexOf('-') !== -1) ? '-' : null) } if (separator === null || date.indexOf(separator) === -1) { return false } // Determine the date date = date.split(separator) dateFormat = dateFormat.split(separator) if (date.length !== dateFormat.length) { return false } const year = date[$.inArray('YYYY', dateFormat)] const month = date[$.inArray('MM', dateFormat)] const day = date[$.inArray('DD', dateFormat)] if (!year || !month || !day || year.length !== 4) { return false } // Determine the time let minutes = null; let hours = null; let seconds = null if (timeFormat) { timeFormat = timeFormat.split(':') time = time.split(':') if (timeFormat.length !== time.length) { return false } hours = time.length > 0 ? time[0] : null minutes = time.length > 1 ? time[1] : null seconds = time.length > 2 ? time[2] : null // Validate seconds if (seconds) { if (isNaN(seconds) || seconds.length > 2) { return false } seconds = parseInt(seconds, 10) if (seconds < 0 || seconds > 60) { return false } } // Validate hours if (hours) { if (isNaN(hours) || hours.length > 2) { return false } hours = parseInt(hours, 10) if (hours < 0 || hours >= 24 || (amOrPm && hours > 12)) { return false } } // Validate minutes if (minutes) { if (isNaN(minutes) || minutes.length > 2) { return false } minutes = parseInt(minutes, 10) if (minutes < 0 || minutes > 59) { return false } } } // Validate day, month, and year return $.fn.bootstrapValidator.helpers.date(year, month, day) } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.different = $.extend($.fn.bootstrapValidator.i18n.different || {}, { default: 'Please enter a different value' }) $.fn.bootstrapValidator.validators.different = { html5Attributes: { message: 'message', field: 'field' }, /** * Return true if the input value is different with given field's value * * @param {BootstrapValidator} validator The validator plugin instance * @param {jQuery} $field Field element * @param {Object} options Consists of the following key: * - field: The name of field that will be used to compare with current one * - message: The invalid message * @returns {Boolean} */ validate: function (validator, $field, options) { const value = $field.val() if (value === '') { return true } const compareWith = validator.getFieldElements(options.field) if (compareWith === null || compareWith.length === 0) { return true } if (value !== compareWith.val()) { validator.updateStatus(options.field, validator.STATUS_VALID, 'different') return true } else { return false } } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.digits = $.extend($.fn.bootstrapValidator.i18n.digits || {}, { default: 'Please enter only digits' }) $.fn.bootstrapValidator.validators.digits = { /** * Return true if the input value contains digits only * * @param {BootstrapValidator} validator Validate plugin instance * @param {jQuery} $field Field element * @param {Object} [options] * @returns {Boolean} */ validate: function (validator, $field, options) { const value = $field.val() if (value === '') { return true } return /^\d+$/.test(value) } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.ean = $.extend($.fn.bootstrapValidator.i18n.ean || {}, { default: 'Please enter a valid EAN number' }) $.fn.bootstrapValidator.validators.ean = { /** * Validate EAN (International Article Number) * Examples: * - Valid: 73513537, 9780471117094, 4006381333931 * - Invalid: 73513536 * * @see http://en.wikipedia.org/wiki/European_Article_Number * @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) { const value = $field.val() if (value === '') { return true } if (!/^(\d{8}|\d{12}|\d{13})$/.test(value)) { return false } const length = value.length let sum = 0 const weight = (length === 8) ? [3, 1] : [1, 3] for (let i = 0; i < length - 1; i++) { sum += parseInt(value.charAt(i), 10) * weight[i % 2] } sum = (10 - sum % 10) % 10 return (sum + '' === value.charAt(length - 1)) } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.emailAddress = $.extend($.fn.bootstrapValidator.i18n.emailAddress || {}, { default: 'Please enter a valid email address' }) $.fn.bootstrapValidator.validators.emailAddress = { enableByHtml5: function ($field) { return ($field.attr('type') === 'email') }, /** * 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] * @returns {Boolean} */ validate: function (validator, $field, options) { const value = $field.val() if (value === '') { return true } // Email address regular expression // http://stackoverflow.com/questions/46155/validate-email-address-in-javascript const 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) } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.file = $.extend($.fn.bootstrapValidator.i18n.file || {}, { default: 'Please choose a valid file' }) $.fn.bootstrapValidator.validators.file = { html5Attributes: { extension: 'extension', maxsize: 'maxSize', message: 'message', type: 'type' }, /** * Validate upload file. Use HTML 5 API if the browser supports * * @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 * - message: The invalid message * - type: The allowed MIME type, separated by a comma * @returns {Boolean} */ validate: function (validator, $field, options) { const value = $field.val() if (value === '') { return true } let ext const extensions = options.extension ? options.extension.toLowerCase().split(',') : null const types = options.type ? options.type.toLowerCase().split(',') : null const html5 = (window.File && window.FileList && window.FileReader) if (html5) { // Get FileList instance const files = $field.get(0).files const total = files.length for (let i = 0; i < total; i++) { // Check file size if (options.maxSize && files[i].size > parseInt(options.maxSize, 10)) { return false } // Check file extension ext = files[i].name.substr(files[i].name.lastIndexOf('.') + 1) if (extensions && $.inArray(ext.toLowerCase(), extensions) === -1) { return false } // Check file type if (files[i].type && types && $.inArray(files[i].type.toLowerCase(), types) === -1) { return false } } } else { // Check file extension ext = value.substr(value.lastIndexOf('.') + 1) if (extensions && $.inArray(ext.toLowerCase(), extensions) === -1) { return false } } return true } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.greaterThan = $.extend($.fn.bootstrapValidator.i18n.greaterThan || {}, { default: 'Please enter a value greater than or equal to %s', notInclusive: 'Please enter a value greater than %s' }) $.fn.bootstrapValidator.validators.greaterThan = { html5Attributes: { message: 'message', value: 'value', inclusive: 'inclusive' }, enableByHtml5: function ($field) { const type = $field.attr('type') const min = $field.attr('min') if (min && type !== 'date') { return { value: min } } return false }, /** * Return true if the input value is greater than or equals to given number * * @param {BootstrapValidator} validator Validate plugin instance * @param {jQuery} $field Field element * @param {Object} options Can consist of the following keys: * - value: Define the number to compare with. It can be * - A number * - Name of field which its value defines the number * - Name of callback function that returns the number * - A callback function that returns the number * * - inclusive [optional]: Can be true or false. Default is true * - message: The invalid message * @returns {Boolean|Object} */ validate: function (validator, $field, options) { let value = $field.val() if (value === '') { return true } if (!$.isNumeric(value)) { return false } const compareTo = $.isNumeric(options.value) ? options.value : validator.getDynamicOption($field, options.value) value = parseFloat(value) return (options.inclusive === true || options.inclusive === undefined) ? { valid: value >= compareTo, message: $.fn.bootstrapValidator.helpers.format(options.message || $.fn.bootstrapValidator.i18n.greaterThan.default, compareTo) } : { valid: value > compareTo, message: $.fn.bootstrapValidator.helpers.format(options.message || $.fn.bootstrapValidator.i18n.greaterThan.notInclusive, compareTo) } } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.grid = $.extend($.fn.bootstrapValidator.i18n.grid || {}, { default: 'Please enter a valid GRId number' }) $.fn.bootstrapValidator.validators.grid = { /** * Validate GRId (Global Release Identifier) * Examples: * - Valid: A12425GABC1234002M, A1-2425G-ABC1234002-M, A1 2425G ABC1234002 M, Grid:A1-2425G-ABC1234002-M * - Invalid: A1-2425G-ABC1234002-Q * * @see http://en.wikipedia.org/wiki/Global_Release_Identifier * @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) { let value = $field.val() if (value === '') { return true } value = value.toUpperCase() if (!/^[GRID:]*([0-9A-Z]{2})[-\s]*([0-9A-Z]{5})[-\s]*([0-9A-Z]{10})[-\s]*([0-9A-Z]{1})$/g.test(value)) { return false } value = value.replace(/\s/g, '').replace(/-/g, '') if (value.substr(0, 5) === 'GRID:') { value = value.substr(5) } return $.fn.bootstrapValidator.helpers.mod37And36(value) } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.hex = $.extend($.fn.bootstrapValidator.i18n.hex || {}, { default: 'Please enter a valid hexadecimal number' }) $.fn.bootstrapValidator.validators.hex = { /** * Return true if and only if the input value is a valid hexadecimal number * * @param {BootstrapValidator} validator The validator plugin instance * @param {jQuery} $field Field element * @param {Object} options Consist of key: * - message: The invalid message * @returns {Boolean} */ validate: function (validator, $field, options) { const value = $field.val() if (value === '') { return true } return /^[0-9a-fA-F]+$/.test(value) } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.hexColor = $.extend($.fn.bootstrapValidator.i18n.hexColor || {}, { default: 'Please enter a valid hex color' }) $.fn.bootstrapValidator.validators.hexColor = { enableByHtml5: function ($field) { return ($field.attr('type') === 'color') }, /** * Return true if the input value is a valid hex color * * @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) { const value = $field.val() if (value === '') { return true } return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(value) } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.iban = $.extend($.fn.bootstrapValidator.i18n.iban || {}, { default: 'Please enter a valid IBAN number', countryNotSupported: 'The country code %s is not supported', country: 'Please enter a valid IBAN number in %s', countries: { AD: 'Andorra', AE: 'United Arab Emirates', AL: 'Albania', AO: 'Angola', AT: 'Austria', AZ: 'Azerbaijan', BA: 'Bosnia and Herzegovina', BE: 'Belgium', BF: 'Burkina Faso', BG: 'Bulgaria', BH: 'Bahrain', BI: 'Burundi', BJ: 'Benin', BR: 'Brazil', CH: 'Switzerland', CI: 'Ivory Coast', CM: 'Cameroon', CR: 'Costa Rica', CV: 'Cape Verde', CY: 'Cyprus', CZ: 'Czech Republic', DE: 'Germany', DK: 'Denmark', DO: 'Dominican Republic', DZ: 'Algeria', EE: 'Estonia', ES: 'Spain', FI: 'Finland', FO: 'Faroe Islands', FR: 'France', GB: 'United Kingdom', GE: 'Georgia', GI: 'Gibraltar', GL: 'Greenland', GR: 'Greece', GT: 'Guatemala', HR: 'Croatia', HU: 'Hungary', IE: 'Ireland', IL: 'Israel', IR: 'Iran', IS: 'Iceland', IT: 'Italy', JO: 'Jordan', KW: 'Kuwait', KZ: 'Kazakhstan', LB: 'Lebanon', LI: 'Liechtenstein', LT: 'Lithuania', LU: 'Luxembourg', LV: 'Latvia', MC: 'Monaco', MD: 'Moldova', ME: 'Montenegro', MG: 'Madagascar', MK: 'Macedonia', ML: 'Mali', MR: 'Mauritania', MT: 'Malta', MU: 'Mauritius', MZ: 'Mozambique', NL: 'Netherlands', NO: 'Norway', PK: 'Pakistan', PL: 'Poland', PS: 'Palestinian', PT: 'Portugal', QA: 'Qatar', RO: 'Romania', RS: 'Serbia', SA: 'Saudi Arabia', SE: 'Sweden', SI: 'Slovenia', SK: 'Slovakia', SM: 'San Marino', SN: 'Senegal', TN: 'Tunisia', TR: 'Turkey', VG: 'Virgin Islands, British' } }) $.fn.bootstrapValidator.validators.iban = { html5Attributes: { message: 'message', country: 'country' }, // http://www.swift.com/dsp/resources/documents/IBAN_Registry.pdf // http://en.wikipedia.org/wiki/International_Bank_Account_Number#IBAN_formats_by_country REGEX: { AD: 'AD[0-9]{2}[0-9]{4}[0-9]{4}[A-Z0-9]{12}', // Andorra AE: 'AE[0-9]{2}[0-9]{3}[0-9]{16}', // United Arab Emirates AL: 'AL[0-9]{2}[0-9]{8}[A-Z0-9]{16}', // Albania AO: 'AO[0-9]{2}[0-9]{21}', // Angola AT: 'AT[0-9]{2}[0-9]{5}[0-9]{11}', // Austria AZ: 'AZ[0-9]{2}[A-Z]{4}[A-Z0-9]{20}', // Azerbaijan BA: 'BA[0-9]{2}[0-9]{3}[0-9]{3}[0-9]{8}[0-9]{2}', // Bosnia and Herzegovina BE: 'BE[0-9]{2}[0-9]{3}[0-9]{7}[0-9]{2}', // Belgium BF: 'BF[0-9]{2}[0-9]{23}', // Burkina Faso BG: 'BG[0-9]{2}[A-Z]{4}[0-9]{4}[0-9]{2}[A-Z0-9]{8}', // Bulgaria BH: 'BH[0-9]{2}[A-Z]{4}[A-Z0-9]{14}', // Bahrain BI: 'BI[0-9]{2}[0-9]{12}', // Burundi BJ: 'BJ[0-9]{2}[A-Z]{1}[0-9]{23}', // Benin BR: 'BR[0-9]{2}[0-9]{8}[0-9]{5}[0-9]{10}[A-Z][A-Z0-9]', // Brazil CH: 'CH[0-9]{2}[0-9]{5}[A-Z0-9]{12}', // Switzerland CI: 'CI[0-9]{2}[A-Z]{1}[0-9]{23}', // Ivory Coast CM: 'CM[0-9]{2}[0-9]{23}', // Cameroon CR: 'CR[0-9]{2}[0-9]{3}[0-9]{14}', // Costa Rica CV: 'CV[0-9]{2}[0-9]{21}', // Cape Verde CY: 'CY[0-9]{2}[0-9]{3}[0-9]{5}[A-Z0-9]{16}', // Cyprus CZ: 'CZ[0-9]{2}[0-9]{20}', // Czech Republic DE: 'DE[0-9]{2}[0-9]{8}[0-9]{10}', // Germany DK: 'DK[0-9]{2}[0-9]{14}', // Denmark DO: 'DO[0-9]{2}[A-Z0-9]{4}[0-9]{20}', // Dominican Republic DZ: 'DZ[0-9]{2}[0-9]{20}', // Algeria EE: 'EE[0-9]{2}[0-9]{2}[0-9]{2}[0-9]{11}[0-9]{1}', // Estonia ES: 'ES[0-9]{2}[0-9]{4}[0-9]{4}[0-9]{1}[0-9]{1}[0-9]{10}', // Spain FI: 'FI[0-9]{2}[0-9]{6}[0-9]{7}[0-9]{1}', // Finland FO: 'FO[0-9]{2}[0-9]{4}[0-9]{9}[0-9]{1}', // Faroe Islands FR: 'FR[0-9]{2}[0-9]{5}[0-9]{5}[A-Z0-9]{11}[0-9]{2}', // France GB: 'GB[0-9]{2}[A-Z]{4}[0-9]{6}[0-9]{8}', // United Kingdom GE: 'GE[0-9]{2}[A-Z]{2}[0-9]{16}', // Georgia GI: 'GI[0-9]{2}[A-Z]{4}[A-Z0-9]{15}', // Gibraltar GL: 'GL[0-9]{2}[0-9]{4}[0-9]{9}[0-9]{1}', // Greenland GR: 'GR[0-9]{2}[0-9]{3}[0-9]{4}[A-Z0-9]{16}', // Greece GT: 'GT[0-9]{2}[A-Z0-9]{4}[A-Z0-9]{20}', // Guatemala HR: 'HR[0-9]{2}[0-9]{7}[0-9]{10}', // Croatia HU: 'HU[0-9]{2}[0-9]{3}[0-9]{4}[0-9]{1}[0-9]{15}[0-9]{1}', // Hungary IE: 'IE[0-9]{2}[A-Z]{4}[0-9]{6}[0-9]{8}', // Ireland IL: 'IL[0-9]{2}[0-9]{3}[0-9]{3}[0-9]{13}', // Israel IR: 'IR[0-9]{2}[0-9]{22}', // Iran IS: 'IS[0-9]{2}[0-9]{4}[0-9]{2}[0-9]{6}[0-9]{10}', // Iceland IT: 'IT[0-9]{2}[A-Z]{1}[0-9]{5}[0-9]{5}[A-Z0-9]{12}', // Italy JO: 'JO[0-9]{2}[A-Z]{4}[0-9]{4}[0]{8}[A-Z0-9]{10}', // Jordan KW: 'KW[0-9]{2}[A-Z]{4}[0-9]{22}', // Kuwait KZ: 'KZ[0-9]{2}[0-9]{3}[A-Z0-9]{13}', // Kazakhstan LB: 'LB[0-9]{2}[0-9]{4}[A-Z0-9]{20}', // Lebanon LI: 'LI[0-9]{2}[0-9]{5}[A-Z0-9]{12}', // Liechtenstein LT: 'LT[0-9]{2}[0-9]{5}[0-9]{11}', // Lithuania LU: 'LU[0-9]{2}[0-9]{3}[A-Z0-9]{13}', // Luxembourg LV: 'LV[0-9]{2}[A-Z]{4}[A-Z0-9]{13}', // Latvia MC: 'MC[0-9]{2}[0-9]{5}[0-9]{5}[A-Z0-9]{11}[0-9]{2}', // Monaco MD: 'MD[0-9]{2}[A-Z0-9]{20}', // Moldova ME: 'ME[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2}', // Montenegro MG: 'MG[0-9]{2}[0-9]{23}', // Madagascar MK: 'MK[0-9]{2}[0-9]{3}[A-Z0-9]{10}[0-9]{2}', // Macedonia ML: 'ML[0-9]{2}[A-Z]{1}[0-9]{23}', // Mali MR: 'MR13[0-9]{5}[0-9]{5}[0-9]{11}[0-9]{2}', // Mauritania MT: 'MT[0-9]{2}[A-Z]{4}[0-9]{5}[A-Z0-9]{18}', // Malta MU: 'MU[0-9]{2}[A-Z]{4}[0-9]{2}[0-9]{2}[0-9]{12}[0-9]{3}[A-Z]{3}', // Mauritius MZ: 'MZ[0-9]{2}[0-9]{21}', // Mozambique NL: 'NL[0-9]{2}[A-Z]{4}[0-9]{10}', // Netherlands NO: 'NO[0-9]{2}[0-9]{4}[0-9]{6}[0-9]{1}', // Norway PK: 'PK[0-9]{2}[A-Z]{4}[A-Z0-9]{16}', // Pakistan PL: 'PL[0-9]{2}[0-9]{8}[0-9]{16}', // Poland PS: 'PS[0-9]{2}[A-Z]{4}[A-Z0-9]{21}', // Palestinian PT: 'PT[0-9]{2}[0-9]{4}[0-9]{4}[0-9]{11}[0-9]{2}', // Portugal QA: 'QA[0-9]{2}[A-Z]{4}[A-Z0-9]{21}', // Qatar RO: 'RO[0-9]{2}[A-Z]{4}[A-Z0-9]{16}', // Romania RS: 'RS[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2}', // Serbia SA: 'SA[0-9]{2}[0-9]{2}[A-Z0-9]{18}', // Saudi Arabia SE: 'SE[0-9]{2}[0-9]{3}[0-9]{16}[0-9]{1}', // Sweden SI: 'SI[0-9]{2}[0-9]{5}[0-9]{8}[0-9]{2}', // Slovenia SK: 'SK[0-9]{2}[0-9]{4}[0-9]{6}[0-9]{10}', // Slovakia SM: 'SM[0-9]{2}[A-Z]{1}[0-9]{5}[0-9]{5}[A-Z0-9]{12}', // San Marino SN: 'SN[0-9]{2}[A-Z]{1}[0-9]{23}', // Senegal TN: 'TN59[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2}', // Tunisia TR: 'TR[0-9]{2}[0-9]{5}[A-Z0-9]{1}[A-Z0-9]{16}', // Turkey VG: 'VG[0-9]{2}[A-Z]{4}[0-9]{16}' // Virgin Islands, British }, /** * Validate an International Bank Account Number (IBAN) * To test it, take the sample IBAN from * http://www.nordea.com/Our+services/International+products+and+services/Cash+Management/IBAN+countries/908462.html * * @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 * - country: The ISO 3166-1 country code. It can be * - A country code * - Name of field which its value defines the country code * - Name of callback function that returns the country code * - A callback function that returns the country code * @returns {Boolean|Object} */ validate: function (validator, $field, options) { let value = $field.val() if (value === '') { return true } value = value.replace(/[^a-zA-Z0-9]/g, '').toUpperCase() let country = options.country if (!country) { country = value.substr(0, 2) } else if (typeof country !== 'string' || !this.REGEX[country]) { // Determine the country code country = validator.getDynamicOption($field, country) } if (!this.REGEX[country]) { return { valid: false, message: $.fn.bootstrapValidator.helpers.format($.fn.bootstrapValidator.i18n.iban.countryNotSupported, country) } } if (!(new RegExp('^' + this.REGEX[country] + '$')).test(value)) { return { valid: false, message: $.fn.bootstrapValidator.helpers.format(options.message || $.fn.bootstrapValidator.i18n.iban.country, $.fn.bootstrapValidator.i18n.iban.countries[country]) } } value = value.substr(4) + value.substr(0, 4) value = $.map(value.split(''), function (n) { const code = n.charCodeAt(0) return (code >= 'A'.charCodeAt(0) && code <= 'Z'.charCodeAt(0)) // Replace A, B, C, ..., Z with 10, 11, ..., 35 ? (code - 'A'.charCodeAt(0) + 10) : n }) value = value.join('') let temp = parseInt(value.substr(0, 1), 10) const length = value.length for (let i = 1; i < length; ++i) { temp = (temp * 10 + parseInt(value.substr(i, 1), 10)) % 97 } return { valid: (temp === 1), message: $.fn.bootstrapValidator.helpers.format(options.message || $.fn.bootstrapValidator.i18n.iban.country, $.fn.bootstrapValidator.i18n.iban.countries[country]) } } } }(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', 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', IS: 'Iceland', LT: 'Lithuanian', LV: 'Latvian', ME: 'Montenegro', MK: 'Macedonian', NL: 'Dutch', RO: 'Romanian', RS: 'Serbian', SE: 'Swedish', SI: 'Slovenian', SK: 'Slovak', SM: 'San Marino', ZA: 'South African' } }) $.fn.bootstrapValidator.validators.id = { html5Attributes: { message: 'message', 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' ], /** * Validate identification number in different countries * * @see http://en.wikipedia.org/wiki/National_identification_number * @param {BootstrapValidator} validator The validator plugin instance * @param {jQuery} $field Field element * @param {Object} options Consist of key: * - message: The invalid message * - country: The ISO 3166-1 country code. It can be * - One of country code defined in COUNTRY_CODES * - Name of field which its value defines the country code * - Name of callback function that returns the country code * - A callback function that returns the country code * @returns {Boolean|Object} */ validate: function (validator, $field, options) { const value = $field.val() if (value === '') { return true } let country = options.country if (!country) { country = value.substr(0, 2) } else if (typeof country !== 'string' || $.inArray(country.toUpperCase(), this.COUNTRY_CODES) === -1) { // Determine the country code country = validator.getDynamicOption($field, country) } if ($.inArray(country, this.COUNTRY_CODES) === -1) { return { valid: false, message: $.fn.bootstrapValidator.helpers.format($.fn.bootstrapValidator.i18n.id.countryNotSupported, country) } } const method = ['_', country.toLowerCase()].join('') return this[method](value) ? true : { valid: false, message: $.fn.bootstrapValidator.helpers.format(options.message || $.fn.bootstrapValidator.i18n.id.country, $.fn.bootstrapValidator.i18n.id.countries[country.toUpperCase()]) } }, /** * Validate Unique Master Citizen Number which uses in * - Bosnia and Herzegovina (country code: BA) * - Macedonia (MK) * - Montenegro (ME) * - Serbia (RS) * - Slovenia (SI) * * @see http://en.wikipedia.org/wiki/Unique_Master_Citizen_Number * @param {String} value The ID * @param {String} countryCode The ISO country code, can be BA, MK, ME, RS, SI * @returns {Boolean} */ _validateJMBG: function (value, countryCode) { if (!/^\d{13}$/.test(value)) { return false } const day = parseInt(value.substr(0, 2), 10) const month = parseInt(value.substr(2, 2), 10) const year = parseInt(value.substr(4, 3), 10) const rr = parseInt(value.substr(7, 2), 10) const k = parseInt(value.substr(12, 1), 10) // Validate date of birth // FIXME: Validate the year of birth if (day > 31 || month > 12) { return false } // Validate checksum let sum = 0 for (let i = 0; i < 6; i++) { sum += (7 - i) * (parseInt(value.charAt(i), 10) + parseInt(value.charAt(i + 6), 10)) } sum = 11 - sum % 11 if (sum === 10 || sum === 11) { sum = 0 } if (sum !== k) { return false } // Validate political region // rr is the political region of birth, which can be in ranges: // 10-19: Bosnia and Herzegovina // 20-29: Montenegro // 30-39: Croatia (not used anymore) // 41-49: Macedonia // 50-59: Slovenia (only 50 is used) // 70-79: Central Serbia // 80-89: Serbian province of Vojvodina // 90-99: Kosovo switch (countryCode.toUpperCase()) { case 'BA': return (rr >= 10 && rr <= 19) case 'MK': return (rr >= 41 && rr <= 49) case 'ME': return (rr >= 20 && rr <= 29) case 'RS': return (rr >= 70 && rr <= 99) case 'SI': return (rr >= 50 && rr <= 59) default: return true } }, _ba: function (value) { return this._validateJMBG(value, 'BA') }, _mk: function (value) { return this._validateJMBG(value, 'MK') }, _me: function (value) { return this._validateJMBG(value, 'ME') }, _rs: function (value) { return this._validateJMBG(value, 'RS') }, /** * Examples: 0101006500006 */ _si: function (value) { return this._validateJMBG(value, 'SI') }, /** * Validate Bulgarian national identification number (EGN) * Examples: * - Valid: 7523169263, 8032056031, 803205 603 1, 8001010008, 7501020018, 7552010005, 7542011030 * - Invalid: 8019010008 * * @see http://en.wikipedia.org/wiki/Uniform_civil_number * @param {String} value The ID * @returns {Boolean} */ _bg: function (value) { if (!/^\d{10}$/.test(value) && !/^\d{6}\s\d{3}\s\d{1}$/.test(value)) { return false } value = value.replace(/\s/g, '') // Check the birth date let year = parseInt(value.substr(0, 2), 10) + 1900 let month = parseInt(value.substr(2, 2), 10) const day = parseInt(value.substr(4, 2), 10) if (month > 40) { year += 100 month -= 40 } else if (month > 20) { year -= 100 month -= 20 } if (!$.fn.bootstrapValidator.helpers.date(year, month, day)) { return false } let sum = 0 const weight = [2, 4, 8, 5, 10, 9, 7, 3, 6] for (let i = 0; i < 9; i++) { sum += parseInt(value.charAt(i), 10) * weight[i] } sum = (sum % 11) % 10 return (sum + '' === value.substr(9, 1)) }, /** * Validate Brazilian national identification number (CPF) * Examples: * - Valid: 39053344705, 390.533.447-05, 111.444.777-35 * - Invalid: 231.002.999-00 * * @see http://en.wikipedia.org/wiki/Cadastro_de_Pessoas_F%C3%ADsicas * @param {String} value The ID * @returns {Boolean} */ _br: function (value) { if (/^1{11}|2{11}|3{11}|4{11}|5{11}|6{11}|7{11}|8{11}|9{11}|0{11}$/.test(value)) { return false } if (!/^\d{11}$/.test(value) && !/^\d{3}\.\d{3}\.\d{3}-\d{2}$/.test(value)) { return false } value = value.replace(/\./g, '').replace(/-/g, '') let d1 = 0 for (var i = 0; i < 9; i++) { d1 += (10 - i) * parseInt(value.charAt(i), 10) } d1 = 11 - d1 % 11 if (d1 === 10 || d1 === 11) { d1 = 0 } if (d1 + '' !== value.charAt(9)) { return false } let d2 = 0 for (i = 0; i < 10; i++) { d2 += (11 - i) * parseInt(value.charAt(i), 10) } d2 = 11 - d2 % 11 if (d2 === 10 || d2 === 11) { d2 = 0 } return (d2 + '' === value.charAt(10)) }, /** * Validate Swiss Social Security Number (AHV-Nr/No AVS) * Examples: * - Valid: 756.1234.5678.95, 7561234567895 * * @see http://en.wikipedia.org/wiki/National_identification_number#Switzerland * @see http://www.bsv.admin.ch/themen/ahv/00011/02185/index.html?lang=de * @param {String} value The ID * @returns {Boolean} */ _ch: function (value) { if (!/^756[\.]{0,1}[0-9]{4}[\.]{0,1}[0-9]{4}[\.]{0,1}[0-9]{2}$/.test(value)) { return false } value = value.replace(/\D/g, '').substr(3) const length = value.length let sum = 0 const weight = (length === 8) ? [3, 1] : [1, 3] for (let i = 0; i < length - 1; i++) { sum += parseInt(value.charAt(i), 10) * weight[i % 2] } sum = 10 - sum % 10 return (sum + '' === value.charAt(length - 1)) }, /** * Validate Chilean national identification number (RUN/RUT) * Examples: * - Valid: 76086428-5, 22060449-7, 12531909-2 * * @see http://en.wikipedia.org/wiki/National_identification_number#Chile * @see https://palena.sii.cl/cvc/dte/ee_empresas_emisoras.html for samples * @param {String} value The ID * @returns {Boolean} */ _cl: function (value) { if (!/^\d{7,8}[-]{0,1}[0-9K]$/i.test(value)) { return false } value = value.replace(/\-/g, '') while (value.length < 9) { value = '0' + value } let sum = 0 const weight = [3, 2, 7, 6, 5, 4, 3, 2] for (let i = 0; i < 8; i++) { sum += parseInt(value.charAt(i), 10) * weight[i] } sum = 11 - sum % 11 if (sum === 11) { sum = 0 } else if (sum === 10) { sum = 'K' } return sum + '' === value.charAt(8).toUpperCase() }, /** * Validate Czech national identification number (RC) * Examples: * - Valid: 7103192745, 991231123 * - Invalid: 1103492745, 590312123 * * @param {String} value The ID * @returns {Boolean} */ _cz: function (value) { if (!/^\d{9,10}$/.test(value)) { return false } let year = 1900 + parseInt(value.substr(0, 2), 10) const month = parseInt(value.substr(2, 2), 10) % 50 % 20 const day = parseInt(value.substr(4, 2), 10) if (value.length === 9) { if (year >= 1980) { year -= 100 } if (year > 1953) { return false } } else if (year < 1954) { year += 100 } if (!$.fn.bootstrapValidator.helpers.date(year, month, day)) { return false } // Check that the birth date is not in the future if (value.length === 10) { let check = parseInt(value.substr(0, 9), 10) % 11 if (year < 1985) { check = check % 10 } return (check + '' === value.substr(9, 1)) } return true }, /** * Validate Danish Personal Identification number (CPR) * Examples: * - Valid: 2110625629, 211062-5629 * - Invalid: 511062-5629 * * @see https://en.wikipedia.org/wiki/Personal_identification_number_(Denmark) * @param {String} value The ID * @returns {Boolean} */ _dk: function (value) { if (!/^[0-9]{6}[-]{0,1}[0-9]{4}$/.test(value)) { return false } value = value.replace(/-/g, '') const day = parseInt(value.substr(0, 2), 10) const month = parseInt(value.substr(2, 2), 10) let year = parseInt(value.substr(4, 2), 10) switch (true) { case ('5678'.indexOf(value.charAt(6)) !== -1 && year >= 58): year += 1800 break case ('0123'.indexOf(value.charAt(6)) !== -1): case ('49'.indexOf(value.charAt(6)) !== -1 && year >= 37): year += 1900 break default: year += 2000 break } return $.fn.bootstrapValidator.helpers.date(year, month, day) }, /** * Validate Estonian Personal Identification Code (isikukood) * Examples: * - Valid: 37605030299 * * @see http://et.wikipedia.org/wiki/Isikukood * @param {String} value The ID * @returns {Boolean} */ _ee: function (value) { // Use the same format as Lithuanian Personal Code return this._lt(value) }, /** * Validate Spanish personal identity code (DNI) * Support i) DNI (for Spanish citizens) and ii) NIE (for foreign people) * * Examples: * - Valid: i) 54362315K, 54362315-K; ii) X2482300W, X-2482300W, X-2482300-W * - Invalid: i) 54362315Z; ii) X-2482300A * * @see https://en.wikipedia.org/wiki/National_identification_number#Spain * @param {String} value The ID * @returns {Boolean} */ _es: function (value) { if (!/^[0-9A-Z]{8}[-]{0,1}[0-9A-Z]$/.test(value) && // DNI !/^[XYZ][-]{0,1}[0-9]{7}[-]{0,1}[0-9A-Z]$/.test(value)) { // NIE return false } value = value.replace(/-/g, '') const index = 'XYZ'.indexOf(value.charAt(0)) if (index !== -1) { // It is NIE number value = index + value.substr(1) + '' } let check = parseInt(value.substr(0, 8), 10) check = 'TRWAGMYFPDXBNJZSQVHLCKE'[check % 23] return (check === value.substr(8, 1)) }, /** * Validate Finnish Personal Identity Code (HETU) * Examples: * - Valid: 311280-888Y, 131052-308T * - Invalid: 131052-308U, 310252-308Y * * @param {String} value The ID * @returns {Boolean} */ _fi: function (value) { if (!/^[0-9]{6}[-+A][0-9]{3}[0-9ABCDEFHJKLMNPRSTUVWXY]$/.test(value)) { return false } const day = parseInt(value.substr(0, 2), 10) const month = parseInt(value.substr(2, 2), 10) let year = parseInt(value.substr(4, 2), 10) const centuries = { '+': 1800, '-': 1900, A: 2000 } year = centuries[value.charAt(6)] + year if (!$.fn.bootstrapValidator.helpers.date(year, month, day)) { return false } const individual = parseInt(value.substr(7, 3), 10) if (individual < 2) { return false } let n = value.substr(0, 6) + value.substr(7, 3) + '' n = parseInt(n, 10) return '0123456789ABCDEFHJKLMNPRSTUVWXY'.charAt(n % 31) === value.charAt(10) }, /** * Validate Croatian personal identification number (OIB) * Examples: * - Valid: 33392005961 * - Invalid: 33392005962 * * @param {String} value The ID * @returns {Boolean} */ _hr: function (value) { if (!/^[0-9]{11}$/.test(value)) { return false } return $.fn.bootstrapValidator.helpers.mod11And10(value) }, /** * Validate Irish Personal Public Service Number (PPS) * Examples: * - Valid: 6433435F, 6433435FT, 6433435FW, 6433435OA, 6433435IH, 1234567TW, 1234567FA * - Invalid: 6433435E, 6433435VH * * @see https://en.wikipedia.org/wiki/Personal_Public_Service_Number * @param {String} value The ID * @returns {Boolean} */ _ie: function (value) { if (!/^\d{7}[A-W][AHWTX]?$/.test(value)) { return false } const getCheckDigit = function (value) { while (value.length < 7) { value = '0' + value } const alphabet = 'WABCDEFGHIJKLMNOPQRSTUV' let sum = 0 for (let i = 0; i < 7; i++) { sum += parseInt(value.charAt(i), 10) * (8 - i) } sum += 9 * alphabet.indexOf(value.substr(7)) return alphabet[sum % 23] } // 2013 format if (value.length === 9 && (value.charAt(8) === 'A' || value.charAt(8) === 'H')) { return value.charAt(7) === getCheckDigit(value.substr(0, 7) + value.substr(8) + '') } // The old format else { return value.charAt(7) === getCheckDigit(value.substr(0, 7)) } }, /** * Validate Iceland national identification number (Kennitala) * Examples: * - Valid: 120174-3399, 1201743399, 0902862349 * * @see http://en.wikipedia.org/wiki/Kennitala * @param {String} value The ID * @returns {Boolean} */ _is: function (value) { if (!/^[0-9]{6}[-]{0,1}[0-9]{4}$/.test(value)) { return false } value = value.replace(/-/g, '') const day = parseInt(value.substr(0, 2), 10) const month = parseInt(value.substr(2, 2), 10) let year = parseInt(value.substr(4, 2), 10) const century = parseInt(value.charAt(9), 10) year = (century === 9) ? (1900 + year) : ((20 + century) * 100 + year) if (!$.fn.bootstrapValidator.helpers.date(year, month, day, true)) { return false } // Validate the check digit let sum = 0 const weight = [3, 2, 7, 6, 5, 4, 3, 2] for (let i = 0; i < 8; i++) { sum += parseInt(value.charAt(i), 10) * weight[i] } sum = 11 - sum % 11 return (sum + '' === value.charAt(8)) }, /** * Validate Lithuanian Personal Code (Asmens kodas) * Examples: * - Valid: 38703181745 * - Invalid: 38703181746, 78703181745, 38703421745 * * @see http://en.wikipedia.org/wiki/National_identification_number#Lithuania * @see http://www.adomas.org/midi2007/pcode.html * @param {String} value The ID * @returns {Boolean} */ _lt: function (value) { if (!/^[0-9]{11}$/.test(value)) { return false } const gender = parseInt(value.charAt(0), 10) let year = parseInt(value.substr(1, 2), 10) const month = parseInt(value.substr(3, 2), 10) const day = parseInt(value.substr(5, 2), 10) const century = (gender % 2 === 0) ? (17 + gender / 2) : (17 + (gender + 1) / 2) year = century * 100 + year if (!$.fn.bootstrapValidator.helpers.date(year, month, day, true)) { return false } // Validate the check digit let sum = 0 let weight = [1, 2, 3, 4, 5, 6, 7, 8, 9, 1] for (var i = 0; i < 10; i++) { sum += parseInt(value.charAt(i), 10) * weight[i] } sum = sum % 11 if (sum !== 10) { return sum + '' === value.charAt(10) } // Re-calculate the check digit sum = 0 weight = [3, 4, 5, 6, 7, 8, 9, 1, 2, 3] for (i = 0; i < 10; i++) { sum += parseInt(value.charAt(i), 10) * weight[i] } sum = sum % 11 if (sum === 10) { sum = 0 } return (sum + '' === value.charAt(10)) }, /** * Validate Latvian Personal Code (Personas kods) * Examples: * - Valid: 161175-19997, 16117519997 * - Invalid: 161375-19997 * * @see http://laacz.lv/2006/11/25/pk-parbaudes-algoritms/ * @param {String} value The ID * @returns {Boolean} */ _lv: function (value) { if (!/^[0-9]{6}[-]{0,1}[0-9]{5}$/.test(value)) { return false } value = value.replace(/\D/g, '') // Check birth date const day = parseInt(value.substr(0, 2), 10) const month = parseInt(value.substr(2, 2), 10) let year = parseInt(value.substr(4, 2), 10) year = year + 1800 + parseInt(value.charAt(6), 10) * 100 if (!$.fn.bootstrapValidator.helpers.date(year, month, day, true)) { return false } // Check personal code let sum = 0 const weight = [10, 5, 8, 4, 2, 1, 6, 3, 7, 9] for (let i = 0; i < 10; i++) { sum += parseInt(value.charAt(i), 10) * weight[i] } sum = (sum + 1) % 11 % 10 return (sum + '' === value.charAt(10)) }, /** * Validate Dutch national identification number (BSN) * Examples: * - Valid: 111222333, 941331490, 9413.31.490 * - Invalid: 111252333 * * @see https://nl.wikipedia.org/wiki/Burgerservicenummer * @param {String} value The ID * @returns {Boolean} */ _nl: function (value) { while (value.length < 9) { value = '0' + value } if (!/^[0-9]{4}[.]{0,1}[0-9]{2}[.]{0,1}[0-9]{3}$/.test(value)) { return false } value = value.replace(/\./g, '') if (parseInt(value, 10) === 0) { return false } let sum = 0 const length = value.length for (let i = 0; i < length - 1; i++) { sum += (9 - i) * parseInt(value.charAt(i), 10) } sum = sum % 11 if (sum === 10) { sum = 0 } return (sum + '' === value.charAt(length - 1)) }, /** * Validate Romanian numerical personal code (CNP) * Examples: * - Valid: 1630615123457, 1800101221144 * - Invalid: 8800101221144, 1632215123457, 1630615123458 * * @see http://en.wikipedia.org/wiki/National_identification_number#Romania * @param {String} value The ID * @returns {Boolean} */ _ro: function (value) { if (!/^[0-9]{13}$/.test(value)) { return false } const gender = parseInt(value.charAt(0), 10) if (gender === 0 || gender === 7 || gender === 8) { return false } // Determine the date of birth let year = parseInt(value.substr(1, 2), 10) const month = parseInt(value.substr(3, 2), 10) const day = parseInt(value.substr(5, 2), 10) // The year of date is determined base on the gender const centuries = { 1: 1900, // Male born between 1900 and 1999 2: 1900, // Female born between 1900 and 1999 3: 1800, // Male born between 1800 and 1899 4: 1800, // Female born between 1800 and 1899 5: 2000, // Male born after 2000 6: 2000 // Female born after 2000 } if (day > 31 && month > 12) { return false } if (gender !== 9) { year = centuries[gender + ''] + year if (!$.fn.bootstrapValidator.helpers.date(year, month, day)) { return false } } // Validate the check digit let sum = 0 const weight = [2, 7, 9, 1, 4, 6, 3, 5, 8, 2, 7, 9] const length = value.length for (let i = 0; i < length - 1; i++) { sum += parseInt(value.charAt(i), 10) * weight[i] } sum = sum % 11 if (sum === 10) { sum = 1 } return (sum + '' === value.charAt(length - 1)) }, /** * Validate Swedish personal identity number (personnummer) * Examples: * - Valid: 8112289874, 811228-9874, 811228+9874 * - Invalid: 811228-9873 * * @see http://en.wikipedia.org/wiki/Personal_identity_number_(Sweden) * @param {String} value The ID * @returns {Boolean} */ _se: function (value) { if (!/^[0-9]{10}$/.test(value) && !/^[0-9]{6}[-|+][0-9]{4}$/.test(value)) { return false } value = value.replace(/[^0-9]/g, '') const year = parseInt(value.substr(0, 2), 10) + 1900 const month = parseInt(value.substr(2, 2), 10) const day = parseInt(value.substr(4, 2), 10) if (!$.fn.bootstrapValidator.helpers.date(year, month, day)) { return false } // Validate the last check digit return $.fn.bootstrapValidator.helpers.luhn(value) }, /** * Validate Slovak national identifier number (RC) * Examples: * - Valid: 7103192745, 991231123 * - Invalid: 7103192746, 1103492745 * * @param {String} value The ID * @returns {Boolean} */ _sk: function (value) { // Slovakia uses the same format as Czech Republic return this._cz(value) }, /** * Validate San Marino citizen number * * @see http://en.wikipedia.org/wiki/National_identification_number#San_Marino * @param {String} value The ID * @returns {Boolean} */ _sm: function (value) { return /^\d{5}$/.test(value) }, /** * Validate South African ID * Example: * - Valid: 8001015009087 * - Invalid: 8001015009287, 8001015009086 * * @see http://en.wikipedia.org/wiki/National_identification_number#South_Africa * @param {String} value The ID * @returns {Boolean} */ _za: function (value) { if (!/^[0-9]{10}[0|1][8|9][0-9]$/.test(value)) { return false } let year = parseInt(value.substr(0, 2), 10) const currentYear = new Date().getFullYear() % 100 const month = parseInt(value.substr(2, 2), 10) const day = parseInt(value.substr(4, 2), 10) year = (year >= currentYear) ? (year + 1900) : (year + 2000) if (!$.fn.bootstrapValidator.helpers.date(year, month, day)) { return false } // Validate the last check digit return $.fn.bootstrapValidator.helpers.luhn(value) } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.identical = $.extend($.fn.bootstrapValidator.i18n.identical || {}, { default: 'Please enter the same value' }) $.fn.bootstrapValidator.validators.identical = { html5Attributes: { message: 'message', field: 'field' }, /** * Check if input value equals to value of particular one * * @param {BootstrapValidator} validator The validator plugin instance * @param {jQuery} $field Field element * @param {Object} options Consists of the following key: * - field: The name of field that will be used to compare with current one * @returns {Boolean} */ validate: function (validator, $field, options) { const value = $field.val() if (value === '') { return true } const compareWith = validator.getFieldElements(options.field) if (compareWith === null || compareWith.length === 0) { return true } if (value === compareWith.val()) { validator.updateStatus(options.field, validator.STATUS_VALID, 'identical') return true } else { return false } } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.imei = $.extend($.fn.bootstrapValidator.i18n.imei || {}, { default: 'Please enter a valid IMEI number' }) $.fn.bootstrapValidator.validators.imei = { /** * Validate IMEI (International Mobile Station Equipment Identity) * Examples: * - Valid: 35-209900-176148-1, 35-209900-176148-23, 3568680000414120, 490154203237518 * - Invalid: 490154203237517 * * @see http://en.wikipedia.org/wiki/International_Mobile_Station_Equipment_Identity * @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) { let value = $field.val() if (value === '') { return true } switch (true) { case /^\d{15}$/.test(value): case /^\d{2}-\d{6}-\d{6}-\d{1}$/.test(value): case /^\d{2}\s\d{6}\s\d{6}\s\d{1}$/.test(value): value = value.replace(/[^0-9]/g, '') return $.fn.bootstrapValidator.helpers.luhn(value) case /^\d{14}$/.test(value): case /^\d{16}$/.test(value): case /^\d{2}-\d{6}-\d{6}(|-\d{2})$/.test(value): case /^\d{2}\s\d{6}\s\d{6}(|\s\d{2})$/.test(value): return true default: return false } } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.imo = $.extend($.fn.bootstrapValidator.i18n.imo || {}, { default: 'Please enter a valid IMO number' }) $.fn.bootstrapValidator.validators.imo = { /** * Validate IMO (International Maritime Organization) * Examples: * - Valid: IMO 8814275, IMO 9176187 * - Invalid: IMO 8814274 * * @see http://en.wikipedia.org/wiki/IMO_Number * @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) { const value = $field.val() if (value === '') { return true } if (!/^IMO \d{7}$/i.test(value)) { return false } // Grab just the digits let sum = 0 const digits = value.replace(/^.*(\d{7})$/, '$1') // Go over each char, multiplying by the inverse of it's position // IMO 9176187 // (9 * 7) + (1 * 6) + (7 * 5) + (6 * 4) + (1 * 3) + (8 * 2) = 147 // Take the last digit of that, that's the check digit (7) for (let i = 6; i >= 1; i--) { sum += (digits.slice((6 - i), -i) * (i + 1)) } return sum % 10 === parseInt(digits.charAt(6), 10) } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.integer = $.extend($.fn.bootstrapValidator.i18n.integer || {}, { default: 'Please enter a valid number' }) $.fn.bootstrapValidator.validators.integer = { enableByHtml5: function ($field) { return ($field.attr('type') === 'number') && ($field.attr('step') === undefined || $field.attr('step') % 1 === 0) }, /** * Return true if the input value is an integer * * @param {BootstrapValidator} validator The validator plugin instance * @param {jQuery} $field Field element * @param {Object} options Can consist of the following key: * - message: The invalid message * @returns {Boolean} */ validate: function (validator, $field, options) { if (this.enableByHtml5($field) && $field.get(0).validity && $field.get(0).validity.badInput === true) { return false } const value = $field.val() if (value === '') { return true } return /^(?:-?(?:0|[1-9][0-9]*))$/.test(value) } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.ip = $.extend($.fn.bootstrapValidator.i18n.ip || {}, { default: 'Please enter a valid IP address', ipv4: 'Please enter a valid IPv4 address', ipv6: 'Please enter a valid IPv6 address' }) $.fn.bootstrapValidator.validators.ip = { html5Attributes: { message: 'message', ipv4: 'ipv4', ipv6: 'ipv6' }, /** * Return true if the input value is a IP address. * * @param {BootstrapValidator} validator The validator plugin instance * @param {jQuery} $field Field element * @param {Object} options Can consist of the following keys: * - ipv4: Enable IPv4 validator, default to true * - ipv6: Enable IPv6 validator, default to true * - message: The invalid message * @returns {Boolean|Object} */ validate: function (validator, $field, options) { const value = $field.val() if (value === '') { return true } options = $.extend({}, { ipv4: true, ipv6: true }, options) const ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/ const ipv6Regex = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/ let valid = false let message switch (true) { case (options.ipv4 && !options.ipv6): valid = ipv4Regex.test(value) message = options.message || $.fn.bootstrapValidator.i18n.ip.ipv4 break case (!options.ipv4 && options.ipv6): valid = ipv6Regex.test(value) message = options.message || $.fn.bootstrapValidator.i18n.ip.ipv6 break case (options.ipv4 && options.ipv6): /* falls through */ default: valid = ipv4Regex.test(value) && ipv6Regex.test(value) message = options.message || $.fn.bootstrapValidator.i18n.ip.default break } return { valid: valid, message: message } } } }(window.jQuery)); (function ($) { $.fn.bootstrapValidator.i18n.isbn = $.extend($.fn.bootstrapValidator.i18n.isbn || {}, { default: 'Please enter a valid ISBN number' }) $.fn.bootstrapValidator.validators.isbn = { /** * Return true if the input value is a valid ISBN 10 or ISBN 13 number * Examples: * - Valid: * ISBN 10: 99921-58-10-7, 9971-5-0210-0, 960-425-059-0, 80-902734-1-6, 85-359-0277-5, 1-84356-028-3, 0-684-84328-5, 0-8044-2957-X, 0-85131-041-9, 0-943396-04-2, 0-9752298-0-X * ISBN 13: 978-0-306-40615-7 * - Invalid: * ISBN 10: 99921-58-10-6 * ISBN 13: 978-0-306-40615-6 * * @see http://en.wikipedia.org/wiki/International_Standard_Book_Number * @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) { let value = $field.val() if (value === '') { return true } // http://en.wikipedia.org/wiki/International_Standard_Book_Number#Overview // Groups are separated by a hyphen or a space let type switch (true) { case /^\d{9}[\dX]$/.test(value): case (value.length === 13 && /^(\d+)-(\d+)-(\d+)-([\dX])$/.test(value)): case (value.length === 13 && /^(\d+)\s(\d+)\s(\d+)\s([\dX])$/.test(value)): type = 'ISBN10' break case /^(978|979)\d{9}[\dX]$/.test(value): case (value.length === 17 && /^(978|979)-(\d+)-(\d+)-(\d+)-([\dX])$/.test(value)): case (value.length === 17 && /^(978|979)\s(\d+)\s(\d+)\s(\d+)\s([\dX])$/.test(value)): type = 'ISBN13' break default: return false } // Replace all special characters except digits and X value = value.replace(/[^0-9X]/gi, '') const chars = value.split('') const length = chars.length let sum = 0 let i let checksum switch (type) { case 'ISBN10': sum = 0 for (i = 0; i < length - 1; i++) { sum += parseInt(chars[i], 10) * (10 - i) } checksum = 11 - (sum % 11) if (checksum === 11) { checksum = 0 } else if (checksum === 10) { checksum = 'X' } return (checksum + '' === chars[length - 1]) case 'ISBN13': sum = 0 for (i = 0; i < length - 1; i++) { sum += ((i % 2 === 0) ? parseInt(chars[i], 10) : (parseInt(chars[i], 10) * 3)) } checksum = 10 - (sum % 10) if (checksum === 10) { checksum = '0' } return (checksum + '' === chars[length - 1]) default: return false } } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.isin = $.extend($.fn.bootstrapValidator.i18n.isin || {}, { default: 'Please enter a valid ISIN number' }) $.fn.bootstrapValidator.validators.isin = { // Available country codes // See http://isin.net/country-codes/ COUNTRY_CODES: 'AF|AX|AL|DZ|AS|AD|AO|AI|AQ|AG|AR|AM|AW|AU|AT|AZ|BS|BH|BD|BB|BY|BE|BZ|BJ|BM|BT|BO|BQ|BA|BW|BV|BR|IO|BN|BG|BF|BI|KH|CM|CA|CV|KY|CF|TD|CL|CN|CX|CC|CO|KM|CG|CD|CK|CR|CI|HR|CU|CW|CY|CZ|DK|DJ|DM|DO|EC|EG|SV|GQ|ER|EE|ET|FK|FO|FJ|FI|FR|GF|PF|TF|GA|GM|GE|DE|GH|GI|GR|GL|GD|GP|GU|GT|GG|GN|GW|GY|HT|HM|VA|HN|HK|HU|IS|IN|ID|IR|IQ|IE|IM|IL|IT|JM|JP|JE|JO|KZ|KE|KI|KP|KR|KW|KG|LA|LV|LB|LS|LR|LY|LI|LT|LU|MO|MK|MG|MW|MY|MV|ML|MT|MH|MQ|MR|MU|YT|MX|FM|MD|MC|MN|ME|MS|MA|MZ|MM|NA|NR|NP|NL|NC|NZ|NI|NE|NG|NU|NF|MP|NO|OM|PK|PW|PS|PA|PG|PY|PE|PH|PN|PL|PT|PR|QA|RE|RO|RU|RW|BL|SH|KN|LC|MF|PM|VC|WS|SM|ST|SA|SN|RS|SC|SL|SG|SX|SK|SI|SB|SO|ZA|GS|SS|ES|LK|SD|SR|SJ|SZ|SE|CH|SY|TW|TJ|TZ|TH|TL|TG|TK|TO|TT|TN|TR|TM|TC|TV|UG|UA|AE|GB|US|UM|UY|UZ|VU|VE|VN|VG|VI|WF|EH|YE|ZM|ZW', /** * Validate an ISIN (International Securities Identification Number) * Examples: * - Valid: US0378331005, AU0000XVGZA3, GB0002634946 * - Invalid: US0378331004, AA0000XVGZA3 * * @see http://en.wikipedia.org/wiki/International_Securities_Identifying_Number * @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) { let value = $field.val() if (value === '') { return true } value = value.toUpperCase() const regex = new RegExp('^(' + this.COUNTRY_CODES + ')[0-9A-Z]{10}$') if (!regex.test(value)) { return false } let converted = '' const length = value.length // Convert letters to number for (var i = 0; i < length - 1; i++) { const c = value.charCodeAt(i) converted += ((c > 57) ? (c - 55).toString() : value.charAt(i)) } let digits = '' const n = converted.length const group = (n % 2 !== 0) ? 0 : 1 for (i = 0; i < n; i++) { digits += (parseInt(converted[i], 10) * ((i % 2) === group ? 2 : 1) + '') } let sum = 0 for (i = 0; i < digits.length; i++) { sum += parseInt(digits.charAt(i), 10) } sum = (10 - (sum % 10)) % 10 return sum + '' === value.charAt(length - 1) } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.ismn = $.extend($.fn.bootstrapValidator.i18n.ismn || {}, { default: 'Please enter a valid ISMN number' }) $.fn.bootstrapValidator.validators.ismn = { /** * Validate ISMN (International Standard Music Number) * Examples: * - Valid: M230671187, 979-0-0601-1561-5, 979 0 3452 4680 5, 9790060115615 * - Invalid: 9790060115614 * * @see http://en.wikipedia.org/wiki/International_Standard_Music_Number * @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) { let value = $field.val() if (value === '') { return true } // Groups are separated by a hyphen or a space let type switch (true) { case /^M\d{9}$/.test(value): case /^M-\d{4}-\d{4}-\d{1}$/.test(value): case /^M\s\d{4}\s\d{4}\s\d{1}$/.test(value): type = 'ISMN10' break case /^9790\d{9}$/.test(value): case /^979-0-\d{4}-\d{4}-\d{1}$/.test(value): case /^979\s0\s\d{4}\s\d{4}\s\d{1}$/.test(value): type = 'ISMN13' break default: return false } if (type === 'ISMN10') { value = '9790' + value.substr(1) } // Replace all special characters except digits value = value.replace(/[^0-9]/gi, '') const length = value.length let sum = 0 const weight = [1, 3] for (let i = 0; i < length - 1; i++) { sum += parseInt(value.charAt(i), 10) * weight[i % 2] } sum = 10 - sum % 10 return (sum + '' === value.charAt(length - 1)) } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.issn = $.extend($.fn.bootstrapValidator.i18n.issn || {}, { default: 'Please enter a valid ISSN number' }) $.fn.bootstrapValidator.validators.issn = { /** * Validate ISSN (International Standard Serial Number) * Examples: * - Valid: 0378-5955, 0024-9319, 0032-1478 * - Invalid: 0032-147X * * @see http://en.wikipedia.org/wiki/International_Standard_Serial_Number * @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) { let value = $field.val() if (value === '') { return true } // Groups are separated by a hyphen or a space if (!/^\d{4}\-\d{3}[\dX]$/.test(value)) { return false } // Replace all special characters except digits and X value = value.replace(/[^0-9X]/gi, '') const chars = value.split('') const length = chars.length let sum = 0 if (chars[7] === 'X') { chars[7] = 10 } for (let i = 0; i < length; i++) { sum += parseInt(chars[i], 10) * (8 - i) } return (sum % 11 === 0) } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.lessThan = $.extend($.fn.bootstrapValidator.i18n.lessThan || {}, { default: 'Please enter a value less than or equal to %s', notInclusive: 'Please enter a value less than %s' }) $.fn.bootstrapValidator.validators.lessThan = { html5Attributes: { message: 'message', value: 'value', inclusive: 'inclusive' }, enableByHtml5: function ($field) { const type = $field.attr('type') const max = $field.attr('max') if (max && type !== 'date') { return { value: max } } return false }, /** * Return true if the input value is less than or equal to given number * * @param {BootstrapValidator} validator The validator plugin instance * @param {jQuery} $field Field element * @param {Object} options Can consist of the following keys: * - value: The number used to compare to. It can be * - A number * - Name of field which its value defines the number * - Name of callback function that returns the number * - A callback function that returns the number * * - inclusive [optional]: Can be true or false. Default is true * - message: The invalid message * @returns {Boolean|Object} */ validate: function (validator, $field, options) { let value = $field.val() if (value === '') { return true } if (!$.isNumeric(value)) { return false } const compareTo = $.isNumeric(options.value) ? options.value : validator.getDynamicOption($field, options.value) value = parseFloat(value) return (options.inclusive === true || options.inclusive === undefined) ? { valid: value <= compareTo, message: $.fn.bootstrapValidator.helpers.format(options.message || $.fn.bootstrapValidator.i18n.lessThan.default, compareTo) } : { valid: value < compareTo, message: $.fn.bootstrapValidator.helpers.format(options.message || $.fn.bootstrapValidator.i18n.lessThan.notInclusive, compareTo) } } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.mac = $.extend($.fn.bootstrapValidator.i18n.mac || {}, { default: 'Please enter a valid MAC address' }) $.fn.bootstrapValidator.validators.mac = { /** * Return true if the input value is a MAC address. * * @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) { const value = $field.val() if (value === '') { return true } return /^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$/.test(value) } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.meid = $.extend($.fn.bootstrapValidator.i18n.meid || {}, { default: 'Please enter a valid MEID number' }) $.fn.bootstrapValidator.validators.meid = { /** * Validate MEID (Mobile Equipment Identifier) * Examples: * - Valid: 293608736500703710, 29360-87365-0070-3710, AF0123450ABCDE, AF-012345-0ABCDE * - Invalid: 2936087365007037101 * * @see http://en.wikipedia.org/wiki/Mobile_equipment_identifier * @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) { let value = $field.val() if (value === '') { return true } switch (true) { // 14 digit hex representation (no check digit) case /^[0-9A-F]{15}$/i.test(value): // 14 digit hex representation + dashes or spaces (no check digit) case /^[0-9A-F]{2}[- ][0-9A-F]{6}[- ][0-9A-F]{6}[- ][0-9A-F]$/i.test(value): // 18 digit decimal representation (no check digit) case /^\d{19}$/.test(value): // 18 digit decimal representation + dashes or spaces (no check digit) case /^\d{5}[- ]\d{5}[- ]\d{4}[- ]\d{4}[- ]\d$/.test(value): // Grab the check digit var cd = value.charAt(value.length - 1) // Strip any non-hex chars value = value.replace(/[- ]/g, '') // If it's all digits, luhn base 10 is used if (value.match(/^\d*$/i)) { return $.fn.bootstrapValidator.helpers.luhn(value) } // Strip the check digit value = value.slice(0, -1) // Get every other char, and double it var cdCalc = '' for (var i = 1; i <= 13; i += 2) { cdCalc += (parseInt(value.charAt(i), 16) * 2).toString(16) } // Get the sum of each char in the string var sum = 0 for (i = 0; i < cdCalc.length; i++) { sum += parseInt(cdCalc.charAt(i), 16) } // If the last digit of the calc is 0, the check digit is 0 return (sum % 10 === 0) ? (cd === '0') // Subtract it from the next highest 10s number (64 goes to 70) and subtract the sum // Double it and turn it into a hex char : (cd === ((Math.floor((sum + 10) / 10) * 10 - sum) * 2).toString(16)) // 14 digit hex representation (no check digit) case /^[0-9A-F]{14}$/i.test(value): // 14 digit hex representation + dashes or spaces (no check digit) case /^[0-9A-F]{2}[- ][0-9A-F]{6}[- ][0-9A-F]{6}$/i.test(value): // 18 digit decimal representation (no check digit) case /^\d{18}$/.test(value): // 18 digit decimal representation + dashes or spaces (no check digit) case /^\d{5}[- ]\d{5}[- ]\d{4}[- ]\d{4}$/.test(value): return true default: return false } } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.notEmpty = $.extend($.fn.bootstrapValidator.i18n.notEmpty || {}, { default: 'Please enter a value' }) $.fn.bootstrapValidator.validators.notEmpty = { enableByHtml5: function ($field) { const required = $field.attr('required') + '' return (required === 'required' || required === 'true') }, /** * Check if input value is empty or not * * @param {BootstrapValidator} validator The validator plugin instance * @param {jQuery} $field Field element * @param {Object} options * @returns {Boolean} */ validate: function (validator, $field, options) { const type = $field.attr('type') if (type === 'radio' || type === 'checkbox') { return validator .getFieldElements($field.attr('data-bv-field')) .filter(':checked') .length > 0 } return $.trim($field.val()) !== '' } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.numeric = $.extend($.fn.bootstrapValidator.i18n.numeric || {}, { default: 'Please enter a valid float number' }) $.fn.bootstrapValidator.validators.numeric = { html5Attributes: { message: 'message', separator: 'separator' }, enableByHtml5: function ($field) { return ($field.attr('type') === 'number') && ($field.attr('step') !== undefined) && ($field.attr('step') % 1 !== 0) }, /** * Validate decimal number * * @param {BootstrapValidator} validator The validator plugin instance * @param {jQuery} $field Field element * @param {Object} options Consist of key: * - message: The invalid message * - separator: The decimal separator. Can be "." (default), "," * @returns {Boolean} */ validate: function (validator, $field, options) { if (this.enableByHtml5($field) && $field.get(0).validity && $field.get(0).validity.badInput === true) { return false } let value = $field.val() if (value === '') { return true } const separator = options.separator || '.' if (separator !== '.') { value = value.replace(separator, '.') } return !isNaN(parseFloat(value)) && isFinite(value) } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.phone = $.extend($.fn.bootstrapValidator.i18n.phone || {}, { 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', ES: 'Spain', FR: 'France', GB: 'United Kingdom', MA: 'Morocco', PK: 'Pakistan', US: 'USA' } }) $.fn.bootstrapValidator.validators.phone = { html5Attributes: { message: 'message', country: 'country' }, // The supported countries COUNTRY_CODES: ['BR', 'ES', 'FR', 'GB', 'MA', 'PK', 'US'], /** * Return true if the input value contains a valid phone number for the country * selected in the options * * @param {BootstrapValidator} validator Validate plugin instance * @param {jQuery} $field Field element * @param {Object} options Consist of key: * - message: The invalid message * - country: The ISO-3166 country code. It can be * - A country code * - Name of field which its value defines the country code * - Name of callback function that returns the country code * - A callback function that returns the country code * * @returns {Boolean|Object} */ validate: function (validator, $field, options) { let value = $field.val() if (value === '') { return true } let country = options.country if (typeof country !== 'string' || $.inArray(country, this.COUNTRY_CODES) === -1) { // Try to determine the country country = validator.getDynamicOption($field, country) } if (!country || $.inArray(country.toUpperCase(), this.COUNTRY_CODES) === -1) { return { valid: false, message: $.fn.bootstrapValidator.helpers.format($.fn.bootstrapValidator.i18n.phone.countryNotSupported, country) } } let isValid = true switch (country.toUpperCase()) { case 'BR': // 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 'ES': // http://regex101.com/r/rB9mA9/1 value = $.trim(value) isValid = (/^(?:(?:(?:\+|00)34\D?))?(?:9|6)(?:\d\D?){8}$/).test(value) break case 'FR': // http://regexr.com/39a2p value = $.trim(value) isValid = (/^(?:(?:(?:\+|00)33[ ]?(?:\(0\)[ ]?)?)|0){1}[1-9]{1}([ .-]?)(?:\d{2}\1?){3}\d{2}$/).test(value) break case 'GB': // http://aa-asterisk.org.uk/index.php/Regular_Expressions_for_Validating_and_Formatting_GB_Telephone_Numbers#Match_GB_telephone_number_in_any_format // Test: http://regexr.com/38uhv value = $.trim(value) isValid = (/^\(?(?:(?:0(?:0|11)\)?[\s-]?\(?|\+)44\)?[\s-]?\(?(?:0\)?[\s-]?\(?)?|0)(?:\d{2}\)?[\s-]?\d{4}[\s-]?\d{4}|\d{3}\)?[\s-]?\d{3}[\s-]?\d{3,4}|\d{4}\)?[\s-]?(?:\d{5}|\d{3}[\s-]?\d{3})|\d{5}\)?[\s-]?\d{4,5}|8(?:00[\s-]?11[\s-]?11|45[\s-]?46[\s-]?4\d))(?:(?:[\s-]?(?:x|ext\.?\s?|\#)\d+)?)$/).test(value) break case 'MA': // 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 'US': /* falls through */ default: // Make sure US phone numbers have 10 digits // May start with 1, +1, or 1-; should discard // Area code may be delimited with (), & sections may be delimited with . or - // Test: http://regexr.com/38mqi value = value.replace(/\D/g, '') isValid = (/^(?:(1\-?)|(\+1 ?))?\(?(\d{3})[\)\-\.]?(\d{3})[\-\.]?(\d{4})$/).test(value) && (value.length === 10) break } return { valid: isValid, message: $.fn.bootstrapValidator.helpers.format(options.message || $.fn.bootstrapValidator.i18n.phone.country, $.fn.bootstrapValidator.i18n.phone.countries[country]) } } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.regexp = $.extend($.fn.bootstrapValidator.i18n.regexp || {}, { default: 'Please enter a value matching the pattern' }) $.fn.bootstrapValidator.validators.regexp = { html5Attributes: { message: 'message', regexp: 'regexp' }, enableByHtml5: function ($field) { const pattern = $field.attr('pattern') if (pattern) { return { regexp: pattern } } return false }, /** * Check if the element value matches given regular expression * * @param {BootstrapValidator} validator The validator plugin instance * @param {jQuery} $field Field element * @param {Object} options Consists of the following key: * - regexp: The regular expression you need to check * @returns {Boolean} */ validate: function (validator, $field, options) { const value = $field.val() if (value === '') { return true } const regexp = (typeof options.regexp === 'string') ? new RegExp(options.regexp) : options.regexp return regexp.test(value) } } }(window.jQuery)) ;(function ($) { $.fn.bootstrapValidator.i18n.remote = $.extend($.fn.bootstrapValidator.i18n.remote || {}, { default: 'Please enter a valid value' }) $.fn.bootstrapValidator.validators.remote = { html5Attributes: { message: 'message', name: 'name', type: 'type', url: 'url' }, /** * 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: * - url {String|Function} * - type {String} [optional] Can be GET or POST (default) * - data {Object|Function} [optional]: By default, it will take the value * { *