diff --git a/ajax/libs/jquery.bootstrapvalidator/0.5.1/css/bootstrapValidator.css b/ajax/libs/jquery.bootstrapvalidator/0.5.1/css/bootstrapValidator.css new file mode 100644 index 000000000..ebe9522f6 --- /dev/null +++ b/ajax/libs/jquery.bootstrapvalidator/0.5.1/css/bootstrapValidator.css @@ -0,0 +1,21 @@ +/** + * BootstrapValidator (http://bootstrapvalidator.com) + * The best jQuery plugin to validate form fields. Designed to use with Bootstrap 3 + * + * @author http://twitter.com/nghuuphuoc + * @copyright (c) 2013 - 2014 Nguyen Huu Phuoc + * @license MIT + */ + +.bv-form .help-block { + margin-bottom: 0; +} +.bv-form .tooltip-inner { + text-align: left; +} +.nav-tabs li.bv-tab-success > a { + color: #3c763d; +} +.nav-tabs li.bv-tab-error > a { + color: #a94442; +} diff --git a/ajax/libs/jquery.bootstrapvalidator/0.5.1/css/bootstrapValidator.min.css b/ajax/libs/jquery.bootstrapvalidator/0.5.1/css/bootstrapValidator.min.css new file mode 100644 index 000000000..15c0b8dce --- /dev/null +++ b/ajax/libs/jquery.bootstrapvalidator/0.5.1/css/bootstrapValidator.min.css @@ -0,0 +1,11 @@ +/*! + * 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 + */ + +.bv-form .help-block{margin-bottom:0}.bv-form .tooltip-inner{text-align:left}.nav-tabs li.bv-tab-success>a{color:#3c763d}.nav-tabs li.bv-tab-error>a{color:#a94442} \ No newline at end of file diff --git a/ajax/libs/jquery.bootstrapvalidator/0.5.1/js/bootstrapValidator.js b/ajax/libs/jquery.bootstrapvalidator/0.5.1/js/bootstrapValidator.js new file mode 100644 index 000000000..98fe4e024 --- /dev/null +++ b/ajax/libs/jquery.bootstrapvalidator/0.5.1/js/bootstrapValidator.js @@ -0,0 +1,6844 @@ +/*! + * 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($) { + var 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 + var ieVersion = (function() { + var v = 3, div = document.createElement('div'), a = div.all || []; + while (div.innerHTML = '', a[0]) {} + return v > 4 ? v : !v; + }()); + + var 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() { + var that = this, + options = { + excluded: this.$form.attr('data-bv-excluded'), + trigger: this.$form.attr('data-bv-trigger'), + message: this.$form.attr('data-bv-message'), + container: this.$form.attr('data-bv-container'), + group: this.$form.attr('data-bv-group'), + submitButtons: this.$form.attr('data-bv-submitbuttons'), + threshold: this.$form.attr('data-bv-threshold'), + live: this.$form.attr('data-bv-live'), + onSuccess: this.$form.attr('data-bv-onsuccess'), + onError: this.$form.attr('data-bv-onerror'), + fields: {}, + feedbackIcons: { + valid: this.$form.attr('data-bv-feedbackicons-valid'), + invalid: this.$form.attr('data-bv-feedbackicons-invalid'), + validating: this.$form.attr('data-bv-feedbackicons-validating') + }, + events: { + formInit: this.$form.attr('data-bv-events-form-init'), + formError: this.$form.attr('data-bv-events-form-error'), + formSuccess: this.$form.attr('data-bv-events-form-success'), + fieldAdded: this.$form.attr('data-bv-events-field-added'), + 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() { + var $field = $(this), + field = $field.attr('name') || $field.attr('data-bv-field'), + 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 + var $button = $(e.target).eq(0); + if (that.options.submitButtons && !$button.is(that.options.submitButtons) && !$button.is(that.$hiddenButton)) { + that.$form.off('submit.bv').submit(); + } + }); + + for (var 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) { + var field = $field.attr('name') || $field.attr('data-bv-field'), + validators = {}, + validator, + v, // Validator name + enabled, + optionName, + optionValue, + html5AttrName, + html5AttrMap; + + for (v in $.fn.bootstrapValidator.validators) { + validator = $.fn.bootstrapValidator.validators[v]; + enabled = $field.attr('data-bv-' + v.toLowerCase()) + ''; + html5AttrMap = ('function' === typeof validator.enableByHtml5) ? validator.enableByHtml5($field) : null; + + if ((html5AttrMap && enabled !== 'false') + || (html5AttrMap !== true && ('' === enabled || 'true' === enabled))) + { + // 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 ('true' === optionValue) { + optionValue = true; + } else if ('false' === optionValue) { + optionValue = false; + } + validators[v][optionName] = optionValue; + } + } + } + } + + var 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 + }, + emptyOptions = $.isEmptyObject(opts), // Check if the field options are set using HTML attributes + emptyValidators = $.isEmptyObject(validators); // Check if the field validators are set using HTML attributes + + 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) { + var 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; + } + var 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; + } + + var that = this, + total = fields.length, + type = fields.attr('type'), + updateAll = (total === 1) || ('radio' === type) || ('checkbox' === type), + event = ('radio' === type || 'checkbox' === type || 'file' === type || 'SELECT' === fields.eq(0).get(0).tagName) ? 'change' : this._changeEvent, + trigger = (this.options.fields[field].trigger || this.options.trigger || event).split(' '), + events = $.map(trigger, function(item) { + return item + '.update.bv'; + }).join(' '); + + for (var i = 0; i < total; i++) { + var $field = fields.eq(i), + group = this.options.fields[field].group || this.options.group, + $parent = $field.parents(group), + // Allow user to indicate where the error messages are shown + container = this.options.fields[field].container || this.options.container, + $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'); + var $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 ('checkbox' === type || 'radio' === type) { + var $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 ''; + } + + var 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) { + var $parent = $field.parent(); + if ($parent.is(group)) { + return $parent; + } + + var cssClasses = $parent.attr('class'); + if (!cssClasses) { + return this._getMessageContainer($parent, group); + } + + cssClasses = cssClasses.split(' '); + var n = cssClasses.length; + for (var 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() { + var isValid = this.isValid(), + eventType = isValid ? this.options.events.formSuccess : this.options.events.formError, + 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) { + var excludedAttr = $field.attr('data-bv-excluded'), + // I still need to check the 'name' attribute while initializing the field + 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 ('string' === typeof this.options.excluded) { + this.options.excluded = $.map(this.options.excluded.split(','), function(item) { + // Trim the spaces + return $.trim(item); + }); + } + + var length = this.options.excluded.length; + for (var i = 0; i < length; i++) { + if (('string' === typeof this.options.excluded[i] && $field.is(this.options.excluded[i])) + || ('function' === typeof this.options.excluded[i] && 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) { + var field = $field.attr('data-bv-field'), + threshold = this.options.fields[field].threshold || this.options.threshold; + if (!threshold) { + return true; + } + var 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 ('submitted' === this.options.live) { + // Enable live mode + this.options.live = 'enabled'; + var that = this; + for (var field in this.options.fields) { + (function(f) { + var fields = that.getFieldElements(f); + if (fields.length) { + var type = $(fields[0]).attr('type'), + event = ('radio' === type || 'checkbox' === type || 'file' === type || 'SELECT' === $(fields[0]).get(0).tagName) ? 'change' : that._changeEvent, + trigger = that.options.fields[field].trigger || that.options.trigger || event, + events = $.map(trigger.split(' '), function(item) { + return item + '.live.bv'; + }).join(' '); + + fields.off(events).on(events, function() { + if (that._exceedThreshold($(this))) { + that.validateField($(this)); + } + }); + } + })(field); + } + } + + var $invalidField = this.$invalidFields.eq(0); + if ($invalidField) { + // Activate the tab containing the invalid field if exists + var $tabPane = $invalidField.parents('.tab-pane'), 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) { + var field = $field.attr('data-bv-field'), + validators = this.options.fields[field].validators, + counter = {}, + numValidators = 0, + 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++; + var 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) { + var 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; + } + + var that = this, + type = fields.attr('type'), + total = ('radio' === type || 'checkbox' === type) ? 1 : fields.length, + updateAll = ('radio' === type || 'checkbox' === type), + validators = this.options.fields[field].validators, + validatorName, + validateResult; + + for (var i = 0; i < total; i++) { + var $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 + var 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 ('object' === typeof validateResult && validateResult.resolve) { + this.updateStatus(updateAll ? field : $field, this.STATUS_VALIDATING, validatorName); + $field.data('bv.dfs.' + validatorName, validateResult); + + validateResult.done(function($f, v, isValid, message) { + // 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 ('object' === typeof validateResult && 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 ('boolean' === typeof validateResult) { + 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) { + var $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) { + var 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; + } + + var that = this, + type = fields.attr('type'), + group = this.options.fields[field].group || this.options.group, + total = ('radio' === type || 'checkbox' === type) ? 1 : fields.length; + + for (var i = 0; i < total; i++) { + var $field = fields.eq(i); + if (this._isExcluded($field)) { + continue; + } + + var $parent = $field.parents(group), + $message = $field.data('bv.messages'), + $allErrors = $message.find('.help-block[data-bv-validator][data-bv-for="' + field + '"]'), + $errors = validatorName ? $allErrors.filter('[data-bv-validator="' + validatorName + '"]') : $allErrors, + $icon = $parent.find('.form-control-feedback[data-bv-icon-for="' + field + '"]'), + container = this.options.fields[field].container || this.options.container, + 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 + var $tabPane = $field.parents('.tab-pane'), + tabId, $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 && 'tooltip' === container): + (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 && 'popover' === container): + (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) { + var 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; + } + + var type = fields.attr('type'), + total = ('radio' === type || 'checkbox' === type) ? 1 : fields.length, + $field, validatorName, status; + for (var 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) { + var that = this, + map = {}, + $container = ('string' === typeof container) ? $(container) : container; + if ($container.length === 0) { + return true; + } + + $container.find('[data-bv-field]').each(function() { + var $field = $(this), + 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() { + var v = $(this).attr('data-bv-validator'), + f = $(this).attr('data-bv-for'); + return (that.options.fields[f].validators[v].enabled !== false + && $f.data('bv.result.' + v) && $f.data('bv.result.' + v) !== that.STATUS_VALID); + }) + .length !== 0) + { + // 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) { + var that = this, + messages = [], + $fields = $([]); + + switch (true) { + case (field && 'object' === typeof field): + $fields = field; + break; + case (field && 'string' === typeof field): + var f = this.getFieldElements(field); + if (f.length > 0) { + var type = f.attr('type'); + $fields = ('radio' === type || 'checkbox' === type) ? f.eq(0) : f; + } + break; + default: + $fields = this.$invalidFields; + break; + } + + var 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() { + var v = $(this).attr('data-bv-validator'), + 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 ('object' === typeof field) { + field = field.attr('data-bv-field'); + } + if (!this.options.fields[field]) { + return null; + } + + var options = this.options.fields[field]; + if (!validator) { + return options; + } + if (!options.validators || !options.validators[validator]) { + return null; + } + + return option ? options.validators[validator][option] : options.validators[validator]; + }, + + /** + * Update the option of a specific validator + * + * @param {String|jQuery} field The field name or field element + * @param {String} validator The validator name + * @param {String} option The option name + * @param {String} value The value to set + * @returns {BootstrapValidator} + */ + updateOption: function(field, validator, option, value) { + if ('object' === typeof field) { + 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) { + var 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); + + var type = fields.attr('type'), + total = ('radio' === type || 'checkbox' === type) ? 1 : fields.length; + + for (var i = 0; i < total; i++) { + var $field = fields.eq(i); + + // Try to parse the options from HTML attributes + var 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(('checkbox' === type || 'radio' === type) ? 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) { + var 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; + } + + var type = fields.attr('type'), + total = ('radio' === type || 'checkbox' === type) ? 1 : fields.length; + + for (var i = 0; i < total; i++) { + var $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 ('checkbox' === type || 'radio' === type) { + 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) { + var $fields = $([]); + switch (typeof field) { + case 'object': + $fields = field; + field = field.attr('data-bv-field'); + break; + case 'string': + $fields = this.getFieldElements(field); + break; + default: + break; + } + + var total = $fields.length; + if (this.options.fields[field]) { + for (var 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) { + var type = $fields.attr('type'); + ('radio' === type || 'checkbox' === type) ? $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) { + var 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) { + var $field = ('string' === typeof field) ? this.getFieldElements(field) : field, + value = $field.val(); + + // Option can be determined by + // ... a function + if ('function' === typeof option) { + return $.fn.bootstrapValidator.helpers.call(option, [value, this, $field]); + } + // ... value of other field + else if ('string' === typeof option) { + var $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() { + var 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 (var 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) { + var params = arguments; + return this.each(function() { + var $this = $(this), + data = $this.data('bootstrapValidator'), + options = 'object' === typeof option && option; + if (!data) { + data = new BootstrapValidator(this, options); + $this.data('bootstrapValidator', data); + } + + // Allow to call plugin method + if ('string' === typeof option) { + 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 ('function' === typeof functionName) { + return functionName.apply(this, args); + } else if ('string' === typeof functionName) { + if ('()' === functionName.substring(functionName.length - 2)) { + functionName = functionName.substring(0, functionName.length - 2); + } + var ns = functionName.split('.'), + func = ns.pop(), + context = window; + for (var 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; + } + var 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) { + var currentDate = new Date(), + currentYear = currentDate.getFullYear(), + currentMonth = currentDate.getMonth(), + 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) { + var length = value.length, + mul = 0, + prodArr = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]], + 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) { + var check = 5, + length = value.length; + for (var 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'; + var modulus = alphabet.length, + length = value.length, + check = Math.floor(modulus / 2); + for (var 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) { + var 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 ('range' === $field.attr('type')) { + 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) { + var value = $field.val(); + if (value === '') { + return true; + } + if (!$.isNumeric(value)) { + return false; + } + + var min = $.isNumeric(options.min) ? options.min : validator.getDynamicOption($field, options.min), + 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) { + var value = $field.val(); + + if (options.callback) { + var dfd = new $.Deferred(), + response = $.fn.bootstrapValidator.helpers.call(options.callback, [value, validator, $field]); + dfd.resolve($field, 'callback', 'boolean' === typeof response ? response : response.valid, 'object' === typeof response && response.message ? response.message : null); + return dfd; + } + + 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) { + var 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, + min = options.min ? ($.isNumeric(options.min) ? options.min : validator.getDynamicOption($field, options.min)) : null, + max = options.max ? ($.isNumeric(options.max) ? options.max : validator.getDynamicOption($field, options.max)) : null, + isValid = true, + 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) { + var 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 + var 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'] + } + }; + + var 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) { + var value = $field.val(); + if (value === '') { + return true; + } + + value = value.toUpperCase(); + if (!/^[0-9A-Z]{9}$/.test(value)) { + return false; + } + + var converted = $.map(value.split(''), function(item) { + var 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; + }), + length = converted.length, + sum = 0; + for (var i = 0; i < length - 1; i++) { + var 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) { + var 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 + var creditCard = validator.getFieldElements(options.creditCardField).val(); + if (creditCard === '') { + return true; + } + + creditCard = creditCard.replace(/\D/g, ''); + + // Supported credit card types + var 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'] + } + }; + var type, i, 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 + : (('AMERICAN_EXPRESS' === creditCardType) ? (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) { + var 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'; + } + + var formats = options.format.split(' '), + dateFormat = formats[0], + timeFormat = (formats.length > 1) ? formats[1] : null, + amOrPm = (formats.length > 2) ? formats[2] : null, + sections = value.split(' '), + date = sections[0], + time = (sections.length > 1) ? sections[1] : null; + + if (formats.length !== sections.length) { + return false; + } + + // Determine the separator + var 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; + } + + var year = date[$.inArray('YYYY', dateFormat)], + month = date[$.inArray('MM', dateFormat)], + day = date[$.inArray('DD', dateFormat)]; + + if (!year || !month || !day || year.length !== 4) { + return false; + } + + // Determine the time + var minutes = null, hours = null, 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) { + var value = $field.val(); + if (value === '') { + return true; + } + + var 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) { + var 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) { + var value = $field.val(); + if (value === '') { + return true; + } + + if (!/^(\d{8}|\d{12}|\d{13})$/.test(value)) { + return false; + } + + var length = value.length, + sum = 0, + weight = (length === 8) ? [3, 1] : [1, 3]; + for (var 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 ('email' === $field.attr('type')); + }, + + /** + * Return true if and only if the input value is a valid email address + * + * @param {BootstrapValidator} validator Validate plugin instance + * @param {jQuery} $field Field element + * @param {Object} [options] + * @returns {Boolean} + */ + validate: function(validator, $field, options) { + var value = $field.val(); + if (value === '') { + return true; + } + + // Email address regular expression + // http://stackoverflow.com/questions/46155/validate-email-address-in-javascript + var emailRegExp = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return emailRegExp.test(value); + } + }; +}(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) { + var value = $field.val(); + if (value === '') { + return true; + } + + var ext, + extensions = options.extension ? options.extension.toLowerCase().split(',') : null, + types = options.type ? options.type.toLowerCase().split(',') : null, + html5 = (window.File && window.FileList && window.FileReader); + + if (html5) { + // Get FileList instance + var files = $field.get(0).files, + total = files.length; + for (var 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) { + var type = $field.attr('type'), + 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) { + var value = $field.val(); + if (value === '') { + return true; + } + if (!$.isNumeric(value)) { + return false; + } + + var 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) { + var 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 ('GRID:' === value.substr(0, 5)) { + 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) { + var 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 ('color' === $field.attr('type')); + }, + + /** + * 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) { + var 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) { + var value = $field.val(); + if (value === '') { + return true; + } + + value = value.replace(/[^a-zA-Z0-9]/g, '').toUpperCase(); + var 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) { + var 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(''); + + var temp = parseInt(value.substr(0, 1), 10), + length = value.length; + for (var 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) { + var value = $field.val(); + if (value === '') { + return true; + } + + var 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) }; + } + + var 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; + } + var day = parseInt(value.substr(0, 2), 10), + month = parseInt(value.substr(2, 2), 10), + year = parseInt(value.substr(4, 3), 10), + rr = parseInt(value.substr(7, 2), 10), + 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 + var sum = 0; + for (var 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 (10 <= rr && rr <= 19); + case 'MK': + return (41 <= rr && rr <= 49); + case 'ME': + return (20 <= rr && rr <= 29); + case 'RS': + return (70 <= rr && rr <= 99); + case 'SI': + return (50 <= rr && 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 + var year = parseInt(value.substr(0, 2), 10) + 1900, + month = parseInt(value.substr(2, 2), 10), + 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; + } + + var sum = 0, + weight = [2, 4, 8, 5, 10, 9, 7, 3, 6]; + for (var 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, ''); + + var 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; + } + + var 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); + var length = value.length, + sum = 0, + weight = (length === 8) ? [3, 1] : [1, 3]; + for (var 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; + } + var sum = 0, + weight = [3, 2, 7, 6, 5, 4, 3, 2]; + for (var i = 0; i < 8; i++) { + sum += parseInt(value.charAt(i), 10) * weight[i]; + } + 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; + } + var year = 1900 + parseInt(value.substr(0, 2), 10), + month = parseInt(value.substr(2, 2), 10) % 50 % 20, + 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) { + var 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, ''); + var day = parseInt(value.substr(0, 2), 10), + month = parseInt(value.substr(2, 2), 10), + 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, ''); + var index = 'XYZ'.indexOf(value.charAt(0)); + if (index !== -1) { + // It is NIE number + value = index + value.substr(1) + ''; + } + + var 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; + } + var day = parseInt(value.substr(0, 2), 10), + month = parseInt(value.substr(2, 2), 10), + year = parseInt(value.substr(4, 2), 10), + centuries = { + '+': 1800, + '-': 1900, + 'A': 2000 + }; + year = centuries[value.charAt(6)] + year; + + if (!$.fn.bootstrapValidator.helpers.date(year, month, day)) { + return false; + } + + var individual = parseInt(value.substr(7, 3), 10); + if (individual < 2) { + return false; + } + var 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; + } + + var getCheckDigit = function(value) { + while (value.length < 7) { + value = '0' + value; + } + var alphabet = 'WABCDEFGHIJKLMNOPQRSTUV', + sum = 0; + for (var 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 && ('A' === value.charAt(8) || 'H' === value.charAt(8))) { + 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, ''); + var day = parseInt(value.substr(0, 2), 10), + month = parseInt(value.substr(2, 2), 10), + year = parseInt(value.substr(4, 2), 10), + 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 + var sum = 0, + weight = [3, 2, 7, 6, 5, 4, 3, 2]; + for (var i = 0; i < 8; i++) { + sum += parseInt(value.charAt(i), 10) * weight[i]; + } + 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; + } + var gender = parseInt(value.charAt(0), 10), + year = parseInt(value.substr(1, 2), 10), + month = parseInt(value.substr(3, 2), 10), + day = parseInt(value.substr(5, 2), 10), + 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 + var sum = 0, + 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 + var day = parseInt(value.substr(0, 2), 10), + month = parseInt(value.substr(2, 2), 10), + 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 + var sum = 0, + weight = [10, 5, 8, 4, 2, 1, 6, 3, 7, 9]; + for (var 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; + } + var sum = 0, + length = value.length; + for (var 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; + } + var gender = parseInt(value.charAt(0), 10); + if (gender === 0 || gender === 7 || gender === 8) { + return false; + } + + // Determine the date of birth + var year = parseInt(value.substr(1, 2), 10), + month = parseInt(value.substr(3, 2), 10), + day = parseInt(value.substr(5, 2), 10), + // The year of date is determined base on the gender + 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 + var sum = 0, + weight = [2, 7, 9, 1, 4, 6, 3, 5, 8, 2, 7, 9], + length = value.length; + for (var 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, ''); + + var year = parseInt(value.substr(0, 2), 10) + 1900, + month = parseInt(value.substr(2, 2), 10), + 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; + } + var year = parseInt(value.substr(0, 2), 10), + currentYear = new Date().getFullYear() % 100, + month = parseInt(value.substr(2, 2), 10), + 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) { + var value = $field.val(); + if (value === '') { + return true; + } + + var 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) { + var 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) { + var value = $field.val(); + if (value === '') { + return true; + } + + if (!/^IMO \d{7}$/i.test(value)) { + return false; + } + + // Grab just the digits + var sum = 0, + 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 (var 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 ('number' === $field.attr('type')) && ($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; + } + + var 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) { + var value = $field.val(); + if (value === '') { + return true; + } + options = $.extend({}, { ipv4: true, ipv6: true }, options); + + var 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]?)$/, + 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*$/, + valid = false, + 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) { + var 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 + var 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, ''); + var chars = value.split(''), + length = chars.length, + sum = 0, + i, + 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) { + var value = $field.val(); + if (value === '') { + return true; + } + + value = value.toUpperCase(); + var regex = new RegExp('^(' + this.COUNTRY_CODES + ')[0-9A-Z]{10}$'); + if (!regex.test(value)) { + return false; + } + + var converted = '', + length = value.length; + // Convert letters to number + for (var i = 0; i < length - 1; i++) { + var c = value.charCodeAt(i); + converted += ((c > 57) ? (c - 55).toString() : value.charAt(i)); + } + + var digits = '', + n = converted.length, + group = (n % 2 !== 0) ? 0 : 1; + for (i = 0; i < n; i++) { + digits += (parseInt(converted[i], 10) * ((i % 2) === group ? 2 : 1) + ''); + } + + var 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) { + var value = $field.val(); + if (value === '') { + return true; + } + + // Groups are separated by a hyphen or a space + var 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 ('ISMN10' === type) { + value = '9790' + value.substr(1); + } + + // Replace all special characters except digits + value = value.replace(/[^0-9]/gi, ''); + var length = value.length, + sum = 0, + weight = [1, 3]; + for (var 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) { + var 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, ''); + var chars = value.split(''), + length = chars.length, + sum = 0; + + if (chars[7] === 'X') { + chars[7] = 10; + } + for (var 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) { + var type = $field.attr('type'), + 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) { + var value = $field.val(); + if (value === '') { + return true; + } + if (!$.isNumeric(value)) { + return false; + } + + var 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) { + var 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) { + var 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) { + var required = $field.attr('required') + ''; + return ('required' === required || 'true' === required); + }, + + /** + * 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) { + var type = $field.attr('type'); + if ('radio' === type || 'checkbox' === type) { + 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 ('number' === $field.attr('type')) && ($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; + } + + var value = $field.val(); + if (value === '') { + return true; + } + var 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) { + var value = $field.val(); + if (value === '') { + return true; + } + + var 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) + }; + } + + var 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) { + var 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) { + var value = $field.val(); + if (value === '') { + return true; + } + + var regexp = ('string' === typeof options.regexp) ? 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 + * { + *