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

- old
+ new

@@ -1,10 +1,10 @@ /*! * BootstrapValidator (http://bootstrapvalidator.com) * The best jQuery plugin to validate form fields. Designed to use with Bootstrap 3 * - * @version v0.5.0, built on 2014-07-14 4:31:02 PM + * @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($) { @@ -12,10 +12,11 @@ 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'; @@ -66,10 +67,23 @@ 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 @@ -96,27 +110,47 @@ 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 = $('<button/>') + .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('init.form.bv'), { + this.$form.trigger($.Event(this.options.events.formInit), { bv: this, options: this.options }); // Prepare the events if (this.options.onSuccess) { - this.$form.on('success.form.bv', function(e) { + this.$form.on(this.options.events.formSuccess, function(e) { $.fn.bootstrapValidator.helpers.call(that.options.onSuccess, [e]); }); } if (this.options.onError) { - this.$form.on('error.form.bv', function(e) { + this.$form.on(this.options.events.formError, function(e) { $.fn.bootstrapValidator.helpers.call(that.options.onError, [e]); }); } }, @@ -275,16 +309,16 @@ .appendTo($message); } // Prepare the validator events if (this.options.fields[field].validators[validatorName].onSuccess) { - $field.on('success.validator.bv', function(e, data) { + $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('error.validator.bv', function(e, data) { + $field.on(this.options.events.validatorError, function(e, data) { $.fn.bootstrapValidator.helpers.call(that.options.fields[field].validators[validatorName].onError, [e, data]); }); } } @@ -298,14 +332,23 @@ $parent.removeClass('has-success').removeClass('has-error').addClass('has-feedback'); var $icon = $('<i/>') .css('display', 'none') .addClass('form-control-feedback') .attr('data-bv-icon-for', field) - // Place it after the label containing the checkbox/radio - // so when clicking the icon, it doesn't effect to the checkbox/radio element - .insertAfter(('checkbox' === type || 'radio' === type) ? $field.parent() : $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); } @@ -319,21 +362,21 @@ } } // Prepare the events if (this.options.fields[field].onSuccess) { - fields.on('success.field.bv', function(e, data) { + 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('error.field.bv', function(e, data) { + 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('status.field.bv', function(e, data) { + fields.on(this.options.events.fieldStatus, function(e, data) { $.fn.bootstrapValidator.helpers.call(that.options.fields[field].onStatus, [e, data]); }); } // Set live mode @@ -355,11 +398,11 @@ } }); break; } - fields.trigger($.Event('init.field.bv'), { + fields.trigger($.Event(this.options.events.fieldInit), { bv: this, field: field, element: fields }); }, @@ -423,11 +466,11 @@ /** * Called when all validations are completed */ _submit: function() { var isValid = this.isValid(), - eventType = isValid ? 'success.form.bv' : 'error.form.bv', + eventType = isValid ? this.options.events.formSuccess : this.options.events.formError, e = $.Event(eventType); this.$form.trigger(e); // Call default handler @@ -586,14 +629,14 @@ // Trigger an event after given validator completes if (validatorName) { switch ($field.data('bv.result.' + validatorName)) { case this.STATUS_INVALID: - $field.trigger($.Event('error.validator.bv'), data); + $field.trigger($.Event(this.options.events.validatorError), data); break; case this.STATUS_VALID: - $field.trigger($.Event('success.validator.bv'), data); + $field.trigger($.Event(this.options.events.validatorSuccess), data); break; default: break; } } @@ -617,18 +660,18 @@ if (counter[this.STATUS_VALID] === numValidators) { // Remove from the list of invalid fields this.$invalidFields = this.$invalidFields.not($field); - $field.trigger($.Event('success.field.bv'), data); + $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('error.field.bv'), data); + $field.trigger($.Event(this.options.events.fieldError), data); } }, // --- // Public methods @@ -954,11 +997,11 @@ (status === this.STATUS_INVALID) ? $errors.show() : $errors.hide(); break; } // Trigger an event - $field.trigger($.Event('status.field.bv'), { + $field.trigger($.Event(this.options.events.fieldStatus), { bv: this, field: field, element: $field, status: status }); @@ -1256,11 +1299,11 @@ this._initField(('checkbox' === type || 'radio' === type) ? field : $field); } this.disableSubmitButtons(false); // Trigger an event - this.$form.trigger($.Event('added.field.bv'), { + this.$form.trigger($.Event(this.options.events.fieldAdded), { field: field, element: fields, options: this.options.fields[field] }); @@ -1312,11 +1355,11 @@ this._initField(field); } this.disableSubmitButtons(false); // Trigger an event - this.$form.trigger($.Event('removed.field.bv'), { + this.$form.trigger($.Event(this.options.events.fieldRemoved), { field: field, element: fields }); return this; @@ -1462,11 +1505,11 @@ if ($f.length) { return $f.val(); } // ... return value of callback else { - return $.fn.bootstrapValidator.helpers.call(option, [value, this, $field]); + return $.fn.bootstrapValidator.helpers.call(option, [value, this, $field]) || option; } } return null; }, @@ -1520,19 +1563,20 @@ $field.removeData('bv.result.' + validator).removeData('bv.dfs.' + validator); } } } - // Enable submit buttons - this.disableSubmitButtons(false); + 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(); + .find('[data-bv-submit-hidden]').remove().end() + .find('[type="submit"]').off('click.bv'); } }; // Plugin definition $.fn.bootstrapValidator = function(option) { @@ -1636,11 +1680,27 @@ // - 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 + 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 = {}; @@ -1671,11 +1731,12 @@ func = ns.pop(), context = window; for (var i = 0; i < ns.length; i++) { context = context[ns[i]]; } - return context[func].apply(this, args); + + return (typeof context[func] === 'undefined') ? null : context[func].apply(this, args); } }, /** * Format a string @@ -1709,10 +1770,13 @@ */ 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); @@ -1874,14 +1938,16 @@ 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]) @@ -2311,10 +2377,15 @@ 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(' '), @@ -2343,11 +2414,11 @@ var year = date[$.inArray('YYYY', dateFormat)], month = date[$.inArray('MM', dateFormat)], day = date[$.inArray('DD', dateFormat)]; - if (!year || !month || !day) { + if (!year || !month || !day || year.length !== 4) { return false; } // Determine the time var minutes = null, hours = null, seconds = null; @@ -2363,28 +2434,37 @@ 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 (isNaN(seconds) || seconds < 0 || seconds > 60) { + if (seconds < 0 || seconds > 60) { return false; } } // Validate hours if (hours) { + if (isNaN(hours) || hours.length > 2) { + return false; + } hours = parseInt(hours, 10); - if (isNaN(hours) || hours < 0 || hours >= 24 || (amOrPm && hours > 12)) { + 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 (isNaN(minutes) || minutes < 0 || minutes > 59) { + if (minutes < 0 || minutes > 59) { return false; } } } @@ -2419,11 +2499,11 @@ if (value === '') { return true; } var compareWith = validator.getFieldElements(options.field); - if (compareWith === null) { + if (compareWith === null || compareWith.length === 0) { return true; } if (value !== compareWith.val()) { validator.updateStatus(options.field, validator.STATUS_VALID, 'different'); @@ -2580,11 +2660,11 @@ if (extensions && $.inArray(ext.toLowerCase(), extensions) === -1) { return false; } // Check file type - if (types && $.inArray(files[i].type.toLowerCase(), types) === -1) { + if (files[i].type && types && $.inArray(files[i].type.toLowerCase(), types) === -1) { return false; } } } else { // Check file extension @@ -2610,12 +2690,13 @@ value: 'value', inclusive: 'inclusive' }, enableByHtml5: function($field) { - var min = $field.attr('min'); - if (min) { + var type = $field.attr('type'), + min = $field.attr('min'); + if (min && type !== 'date') { return { value: min }; } @@ -2641,13 +2722,15 @@ 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) @@ -2844,89 +2927,89 @@ }, // 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 + 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 @@ -3835,11 +3918,11 @@ if (value === '') { return true; } var compareWith = validator.getFieldElements(options.field); - if (compareWith === null) { + if (compareWith === null || compareWith.length === 0) { return true; } if (value === compareWith.val()) { validator.updateStatus(options.field, validator.STATUS_VALID, 'identical'); @@ -3893,10 +3976,55 @@ } } }; }(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 = { @@ -3912,10 +4040,14 @@ * @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); @@ -3952,27 +4084,41 @@ if (value === '') { return true; } options = $.extend({}, { ipv4: true, ipv6: true }, options); - if (options.ipv4) { - return { - valid: /^(?:(?: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]?)$/.test(value), - message: options.message || $.fn.bootstrapValidator.i18n.ip.ipv4 - }; - } else if (options.ipv6) { - return { - valid: /^\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*$/.test(value), - message: options.message || $.fn.bootstrapValidator.i18n.ip.ipv6 - }; + 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 false; + return { + valid: valid, + message: message + }; } }; -}(window.jQuery)); -;(function($) { +}(window.jQuery));;(function($) { $.fn.bootstrapValidator.i18n.isbn = $.extend($.fn.bootstrapValidator.i18n.isbn || {}, { 'default': 'Please enter a valid ISBN number' }); $.fn.bootstrapValidator.validators.isbn = { @@ -4232,12 +4378,13 @@ value: 'value', inclusive: 'inclusive' }, enableByHtml5: function($field) { - var max = $field.attr('max'); - if (max) { + var type = $field.attr('type'), + max = $field.attr('max'); + if (max && type !== 'date') { return { value: max }; } @@ -4263,13 +4410,15 @@ 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) @@ -4305,10 +4454,93 @@ 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 = { @@ -4362,10 +4594,14 @@ * - 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 || '.'; @@ -4381,11 +4617,16 @@ $.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 = { @@ -4393,11 +4634,11 @@ message: 'message', country: 'country' }, // The supported countries - COUNTRY_CODES: ['GB', 'US'], + 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 * @@ -4409,11 +4650,10 @@ * - 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 * - * Currently it only supports United State (US) or United Kingdom (GB) countries * @returns {Boolean|Object} */ validate: function(validator, $field, options) { var value = $field.val(); if (value === '') { @@ -4433,17 +4673,48 @@ }; } 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 @@ -4509,12 +4780,13 @@ }); $.fn.bootstrapValidator.validators.remote = { html5Attributes: { message: 'message', - url: 'url', - name: 'name' + name: 'name', + type: 'type', + url: 'url' }, /** * Request a remote server to check the input value * @@ -4527,22 +4799,24 @@ * { * <fieldName>: <fieldValue> * } * - name {String} [optional]: Override the field name for the request. * - message: The invalid message + * - headers: Additional headers * @returns {Boolean|Deferred} */ validate: function(validator, $field, options) { var value = $field.val(); if (value === '') { return true; } - var name = $field.attr('data-bv-field'), - data = options.data || {}, - url = options.url, - type = options.type || 'POST'; + var name = $field.attr('data-bv-field'), + data = options.data || {}, + url = options.url, + type = options.type || 'POST', + headers = options.headers || {}; // Support dynamic data if ('function' === typeof data) { data = data.call(this, validator); } @@ -4555,10 +4829,11 @@ data[options.name || name] = value; var dfd = new $.Deferred(); var xhr = $.ajax({ type: type, + headers: headers, url: url, dataType: 'json', data: data }); xhr.then(function(response) { @@ -4944,20 +5219,21 @@ // http://mathiasbynens.be/demo/url-regex // // Notes on possible differences from a standard/generic validation: // // - utf-8 char class take in consideration the full Unicode range - // - TLDs have been made mandatory so single names like "localhost" fails + // - TLDs are mandatory unless `allowLocal` is true // - protocols have been restricted to ftp, http and https only as requested // // Changes: // // - IP address dotted notation validation, range: 1.0.0.0 - 223.255.255.255 // first and last IP address of each class is considered invalid // (since they are broadcast/network addresses) // // - Added exclusion of private, reserved and/or local networks ranges + // unless `allowLocal` is true // var allowLocal = options.allowLocal === true || options.allowLocal === 'true', urlExp = new RegExp( "^" + // protocol identifier @@ -4985,10 +5261,12 @@ "(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)" + // domain name "(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*" + // TLD identifier "(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))" + + // Allow intranet sites (no TLD) if `allowLocal` is true + (allowLocal ? '?' : '') + ")" + // port number "(?::\\d{2,5})?" + // resource path "(?:/[^\\s]*)?" + @@ -5052,10 +5330,11 @@ country: 'Please enter a valid %s VAT number', countries: { AT: 'Austrian', BE: 'Belgian', BG: 'Bulgarian', + BR: 'Brazilian', CH: 'Swiss', CY: 'Cypriot', CZ: 'Czech', DE: 'German', DK: 'Danish', @@ -5067,10 +5346,11 @@ GR: 'Greek', EL: 'Greek', HU: 'Hungarian', HR: 'Croatian', IE: 'Irish', + IS: 'Iceland', IT: 'Italian', LT: 'Lithuanian', LU: 'Luxembourg', LV: 'Latvian', MT: 'Maltese', @@ -5081,11 +5361,12 @@ RO: 'Romanian', RU: 'Russian', RS: 'Serbian', SE: 'Swedish', SI: 'Slovenian', - SK: 'Slovak' + SK: 'Slovak', + ZA: 'South African' } }); $.fn.bootstrapValidator.validators.vat = { html5Attributes: { @@ -5093,12 +5374,12 @@ country: 'country' }, // Supported country codes COUNTRY_CODES: [ - 'AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'EL', 'HU', 'IE', 'IT', - 'LV', 'LT', 'LU', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'RU', 'RS', 'SK', 'SI', 'ES', 'SE', 'CH', 'GB' + 'AT', 'BE', 'BG', 'BR', 'CH', 'CY', 'CZ', 'DE', 'DK', 'EE', 'EL', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', + 'IE', 'IS', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'RU', 'RS', 'SE', 'SK', 'SI', 'ZA' ], /** * Validate an European VAT number * @@ -5299,11 +5580,69 @@ return (egn(value) || pnf(value) || vat(value)); } return false; }, + + /** + * Validate Brazilian VAT number (CNPJ) + * + * @param {String} value VAT number + * @returns {Boolean} + */ + _br: function(value) { + if (value === '') { + return true; + } + var cnpj = value.replace(/[^\d]+/g, ''); + if (cnpj === '' || cnpj.length !== 14) { + return false; + } + // Remove invalids CNPJs + if (cnpj === '00000000000000' || cnpj === '11111111111111' || cnpj === '22222222222222' || + cnpj === '33333333333333' || cnpj === '44444444444444' || cnpj === '55555555555555' || + cnpj === '66666666666666' || cnpj === '77777777777777' || cnpj === '88888888888888' || + cnpj === '99999999999999') + { + return false; + } + + // Validate verification digits + var length = cnpj.length - 2, + numbers = cnpj.substring(0, length), + digits = cnpj.substring(length), + sum = 0, + pos = length - 7; + + for (var i = length; i >= 1; i--) { + sum += parseInt(numbers.charAt(length - i), 10) * pos--; + if (pos < 2) { + pos = 9; + } + } + + var result = sum % 11 < 2 ? 0 : 11 - sum % 11; + if (result !== parseInt(digits.charAt(0), 10)) { + return false; + } + + length = length + 1; + numbers = cnpj.substring(0, length); + sum = 0; + pos = length - 7; + for (i = length; i >= 1; i--) { + sum += parseInt(numbers.charAt(length - i), 10) * pos--; + if (pos < 2) { + pos = 9; + } + } + + result = sum % 11 < 2 ? 0 : 11 - sum % 11; + return (result === parseInt(digits.charAt(1), 10)); + }, + /** * Validate Swiss VAT number * * @param {String} value VAT number * @returns {Boolean} @@ -5639,11 +5978,11 @@ return false; } value = value.substr(2); - if (!$.fn.bootstrapValidator.helpers.luhn(value.substr(2))) { + if (!$.fn.bootstrapValidator.helpers.luhn(value.substr(2))) { return false; } if (/^[0-9]{2}$/.test(value.substr(0, 2))) { // First two characters are digits @@ -5833,10 +6172,23 @@ return true; }, /** + * Validate Icelandic VAT (VSK) number + * Examples: + * - Valid: 12345, 123456 + * - Invalid: 1234567 + * + * @params {String} value VAT number + * @returns {Boolean} + */ + _is: function(value) { + return /^IS\d{5,6}$/.test(value); + }, + + /** * Validate Italian VAT number, which consists of 11 digits. * - First 7 digits are a company identifier * - Next 3 are the province of residence * - The last one is a check digit * @@ -6260,10 +6612,23 @@ if (!/^SK[1-9][0-9][(2-4)|(6-9)][0-9]{7}$/.test(value)) { return false; } return (parseInt(value.substr(2), 10) % 11 === 0); + }, + + /** + * Validate South African VAT number + * Examples: + * - Valid: 4012345678 + * - Invalid: 40123456789, 3012345678 + * + * @params {String} value VAT number + * @returns {Boolean} + */ + _za: function(value) { + return /^ZA4\d{9}$/.test(value); } }; }(window.jQuery)); ;(function($) { $.fn.bootstrapValidator.i18n.vin = $.extend($.fn.bootstrapValidator.i18n.vin || {}, { @@ -6318,28 +6683,30 @@ $.fn.bootstrapValidator.i18n.zipCode = $.extend($.fn.bootstrapValidator.i18n.zipCode || {}, { 'default': 'Please enter a valid zip code', countryNotSupported: 'The country code %s is not supported', country: 'Please enter a valid %s', countries: { - 'CA': 'Canadian postal code', - 'DK': 'Danish postal code', - 'GB': 'United Kingdom postal code', - 'IT': 'Italian postal code', - 'NL': 'Dutch postal code', - 'SE': 'Swiss postal code', - 'SG': 'Singapore postal code', - 'US': 'US zip code' + BR: 'Brazilian postal code', + CA: 'Canadian postal code', + DK: 'Danish postal code', + GB: 'United Kingdom postal code', + IT: 'Italian postal code', + MA: 'Moroccan postal code', + NL: 'Dutch postal code', + SE: 'Swiss postal code', + SG: 'Singapore postal code', + US: 'US zip code' } }); $.fn.bootstrapValidator.validators.zipCode = { html5Attributes: { message: 'message', country: 'country' }, - COUNTRY_CODES: ['CA', 'DK', 'GB', 'IT', 'NL', 'SE', 'SG', 'US'], + COUNTRY_CODES: ['BR', 'CA', 'DK', 'GB', 'IT', 'MA', 'NL', 'SE', 'SG', 'US'], /** * Return true if and only if the input value is a valid country zip code * * @param {BootstrapValidator} validator The validator plugin instance @@ -6348,20 +6715,10 @@ * - message: The invalid message * - country: The country * * The country can be defined by: * - An ISO 3166 country code - * Currently it supports the following countries: - * - US (United States) - * - CA (Canada) - * - DK (Denmark) - * - GB (United Kingdom) - * - IT (Italy) - * - NL (Netherlands) - * - SE (Sweden) - * - SG (Singapore) - * * - 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 * * callback: function(value, validator, $field) { @@ -6389,10 +6746,14 @@ } var isValid = false; country = country.toUpperCase(); switch (country) { + case 'BR': + isValid = /^(\d{2})([\.]?)(\d{3})([\-]?)(\d{3})$/.test(value); + break; + case 'CA': isValid = /^(?:A|B|C|E|G|H|J|K|L|M|N|P|R|S|T|V|X|Y){1}[0-9]{1}(?:A|B|C|E|G|H|J|K|L|M|N|P|R|S|T|V|W|X|Y|Z){1}\s?[0-9]{1}(?:A|B|C|E|G|H|J|K|L|M|N|P|R|S|T|V|W|X|Y|Z){1}[0-9]{1}$/i.test(value); break; case 'DK': @@ -6406,10 +6767,15 @@ // http://en.wikipedia.org/wiki/List_of_postal_codes_in_Italy case 'IT': isValid = /^(I-|IT-)?\d{5}$/i.test(value); break; + // http://en.wikipedia.org/wiki/List_of_postal_codes_in_Morocco + case 'MA': + isValid = /^[1-9][0-9]{4}$/i.test(value); + break; + // http://en.wikipedia.org/wiki/Postal_codes_in_the_Netherlands case 'NL': isValid = /^[1-9][0-9]{3} ?(?!sa|sd|ss)[a-z]{2}$/i.test(value); break; @@ -6417,11 +6783,11 @@ isValid = /^(S-)?\d{3}\s?\d{2}$/i.test(value); break; case 'SG': isValid = /^([0][1-9]|[1-6][0-9]|[7]([0-3]|[5-9])|[8][0-2])(\d{4})$/i.test(value); - break; - + break; + case 'US': /* falls through */ default: isValid = /^\d{4,5}([\-]?\d{4})?$/.test(value); break;