vendor/assets/javascripts/bootstrapValidator.js in bootstrap-validator-rails-0.5.2 vs vendor/assets/javascripts/bootstrapValidator.js in bootstrap-validator-rails-0.5.3
- old
+ new
@@ -1,19 +1,27 @@
/*!
* BootstrapValidator (http://bootstrapvalidator.com)
* The best jQuery plugin to validate form fields. Designed to use with Bootstrap 3
*
- * @version v0.5.2, built on 2014-09-25 4:01:07 PM
+ * @version v0.5.3, built on 2014-11-05 9:14:18 PM
* @author https://twitter.com/nghuuphuoc
* @copyright (c) 2013 - 2014 Nguyen Huu Phuoc
- * @license MIT
+ * @license Commercial: http://bootstrapvalidator.com/license/
+ * Non-commercial: http://creativecommons.org/licenses/by-nc-nd/3.0/
*/
if (typeof jQuery === 'undefined') {
- throw new Error('BootstrapValidator\'s JavaScript requires jQuery');
+ throw new Error('BootstrapValidator requires jQuery');
}
(function($) {
+ var version = $.fn.jquery.split(' ')[0].split('.');
+ if ((+version[0] < 2 && +version[1] < 9) || (+version[0] === 1 && +version[1] === 9 && +version[2] < 1)) {
+ throw new Error('BootstrapValidator requires jQuery version 1.9.1 or higher');
+ }
+}(window.jQuery));
+
+(function($) {
var BootstrapValidator = function(form, options) {
this.$form = $(form);
this.options = $.extend({}, $.fn.bootstrapValidator.DEFAULT_OPTIONS, options);
this.$invalidFields = $([]); // Array of invalid fields
@@ -56,10 +64,11 @@
* Init form
*/
_init: function() {
var that = this,
options = {
+ autoFocus: this.$form.attr('data-bv-autofocus'),
container: this.$form.attr('data-bv-container'),
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'),
@@ -174,33 +183,37 @@
_parseOptions: function($field) {
var field = $field.attr('name') || $field.attr('data-bv-field'),
validators = {},
validator,
v, // Validator name
+ attrName,
enabled,
optionName,
+ optionAttrName,
optionValue,
html5AttrName,
html5AttrMap;
for (v in $.fn.bootstrapValidator.validators) {
validator = $.fn.bootstrapValidator.validators[v];
- enabled = $field.attr('data-bv-' + v.toLowerCase()) + '';
+ attrName = 'data-bv-' + v.toLowerCase(),
+ enabled = $field.attr(attrName) + '';
html5AttrMap = ('function' === typeof validator.enableByHtml5) ? validator.enableByHtml5($field) : null;
if ((html5AttrMap && enabled !== 'false')
- || (html5AttrMap !== true && ('' === enabled || 'true' === enabled)))
+ || (html5AttrMap !== true && ('' === enabled || 'true' === enabled || attrName === enabled.toLowerCase())))
{
// 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);
+ optionAttrName = 'data-bv-' + v.toLowerCase() + '-' + html5AttrName,
+ optionValue = $field.attr(optionAttrName);
if (optionValue) {
- if ('true' === optionValue) {
+ if ('true' === optionValue || optionAttrName === optionValue.toLowerCase()) {
optionValue = true;
} else if ('false' === optionValue) {
optionValue = false;
}
validators[v][optionName] = optionValue;
@@ -208,10 +221,11 @@
}
}
}
var opts = {
+ autoFocus: $field.attr('data-bv-autofocus'),
container: $field.attr('data-bv-container'),
excluded: $field.attr('data-bv-excluded'),
feedbackIcons: $field.attr('data-bv-feedbackicons'),
group: $field.attr('data-bv-group'),
message: $field.attr('data-bv-message'),
@@ -362,36 +376,44 @@
// Fix feedback icons in input-group
if ($parent.find('.input-group').length !== 0) {
$icon.addClass('bv-icon-input-group')
.insertAfter($parent.find('.input-group').eq(0));
}
+
+ // Store the icon as a data of field element
+ if (!updateAll) {
+ $field.data('bv.icon', $icon);
+ } else if (i === total - 1) {
+ // All fields with the same name have the same icon
+ fields.data('bv.icon', $icon);
+ }
if (container) {
$field
// Show tooltip/popover message when field gets focus
- .off('focus.bv')
- .on('focus.bv', function() {
+ .off('focus.container.bv')
+ .on('focus.container.bv', function() {
switch (container) {
case 'tooltip':
- $icon.tooltip('show');
+ $(this).data('bv.icon').tooltip('show');
break;
case 'popover':
- $icon.popover('show');
+ $(this).data('bv.icon').popover('show');
break;
default:
break;
}
})
// and hide them when losing focus
- .off('blur.bv')
- .on('blur.bv', function() {
+ .off('blur.container.bv')
+ .on('blur.container.bv', function() {
switch (container) {
case 'tooltip':
- $icon.tooltip('hide');
+ $(this).data('bv.icon').tooltip('hide');
break;
case 'popover':
- $icon.popover('hide');
+ $(this).data('bv.icon').popover('hide');
break;
default:
break;
}
});
@@ -632,20 +654,25 @@
}
})(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');
- }
+ // Determined the first invalid field which will be focused on automatically
+ for (var i = 0; i < this.$invalidFields.length; i++) {
+ var $field = this.$invalidFields.eq(i),
+ autoFocus = this._isOptionEnabled($field.attr('data-bv-field'), 'autoFocus');
+ if (autoFocus) {
+ // Activate the tab containing the field if exists
+ var $tabPane = $field.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();
+ // Focus the field
+ $field.focus();
+ break;
+ }
}
},
/**
* The default handler of success.form.bv event.
@@ -717,18 +744,35 @@
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) {
+ else if ((counter[this.STATUS_NOT_VALIDATED] === 0 || !this._isOptionEnabled(field, 'verbose')) && 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);
}
},
+ /**
+ * Check whether or not a field option is enabled
+ *
+ * @param {String} field The field name
+ * @param {String} option The option name, "verbose", "autoFocus", for example
+ * @returns {Boolean}
+ */
+ _isOptionEnabled: function(field, option) {
+ if (this.options.fields[field] && (this.options.fields[field][option] === 'true' || this.options.fields[field][option] === true)) {
+ return true;
+ }
+ if (this.options.fields[field] && (this.options.fields[field][option] === 'false' || this.options.fields[field][option] === false)) {
+ return false;
+ }
+ return this.options[option] === 'true' || this.options[option] === true;
+ },
+
// ---
// Public methods
// ---
/**
@@ -755,11 +799,11 @@
* @param {String} [option] The option name
* @return {String|Object}
*/
getOptions: function(field, validator, option) {
if (!field) {
- return this.options;
+ return option ? this.options[option] : this.options;
}
if ('object' === typeof field) {
field = field.attr('data-bv-field');
}
if (!this.options.fields[field]) {
@@ -775,11 +819,10 @@
}
return option ? options.validators[validator][option] : options.validators[validator];
},
-
/**
* Disable/enable submit buttons
*
* @param {Boolean} disabled Can be true or false
* @returns {BootstrapValidator}
@@ -804,15 +847,17 @@
if (!this.options.fields) {
return this;
}
this.disableSubmitButtons(true);
+ this._submitIfValid = false;
for (var field in this.options.fields) {
this.validateField(field);
}
this._submit();
+ this._submitIfValid = true;
return this;
},
/**
@@ -833,20 +878,20 @@
break;
default:
break;
}
- if (fields.length === 0 || (this.options.fields[field] && this.options.fields[field].enabled === false)) {
+ if (fields.length === 0 || !this.options.fields[field] || this.options.fields[field].enabled === false) {
return this;
}
var that = this,
type = fields.attr('type'),
total = ('radio' === type || 'checkbox' === type) ? 1 : fields.length,
updateAll = ('radio' === type || 'checkbox' === type),
validators = this.options.fields[field].validators,
- verbose = this.options.fields[field].verbose === 'true' || this.options.fields[field].verbose === true || this.options.verbose === 'true' || this.options.verbose === true,
+ verbose = this._isOptionEnabled(field, 'verbose'),
validatorName,
validateResult;
for (var i = 0; i < total; i++) {
var $field = fields.eq(i);
@@ -970,10 +1015,11 @@
break;
}
if (status === this.STATUS_NOT_VALIDATED) {
// Reset the flag
+ // To prevent the form from doing submit when a deferred validator returns true while typing
this._submitIfValid = false;
}
var that = this,
type = fields.attr('type'),
@@ -988,11 +1034,11 @@
var $parent = $field.parents(group),
$message = $field.data('bv.messages'),
$allErrors = $message.find('.help-block[data-bv-validator][data-bv-for="' + field + '"]'),
$errors = validatorName ? $allErrors.filter('[data-bv-validator="' + validatorName + '"]') : $allErrors,
- $icon = $parent.find('.form-control-feedback[data-bv-icon-for="' + field + '"]'),
+ $icon = $field.data('bv.icon'),
container = ('function' === typeof (this.options.fields[field].container || this.options.container)) ? (this.options.fields[field].container || this.options.container).call(this, $field, this) : (this.options.fields[field].container || this.options.container),
isValidField = null;
// Update status
if (validatorName) {
@@ -1079,26 +1125,26 @@
case ($icon && 'tooltip' === container):
(isValidField === false)
? $icon.css('cursor', 'pointer').tooltip('destroy').tooltip({
container: 'body',
html: true,
- placement: 'top',
+ placement: 'auto top',
title: $allErrors.filter('[data-bv-result="' + that.STATUS_INVALID + '"]').eq(0).html()
})
- : $icon.tooltip('hide');
+ : $icon.css('cursor', '').tooltip('destroy');
break;
// ... or popover
case ($icon && 'popover' === container):
(isValidField === false)
? $icon.css('cursor', 'pointer').popover('destroy').popover({
container: 'body',
content: $allErrors.filter('[data-bv-result="' + that.STATUS_INVALID + '"]').eq(0).html(),
html: true,
- placement: 'top',
+ placement: 'auto top',
trigger: 'hover click'
})
- : $icon.popover('hide');
+ : $icon.css('cursor', '').popover('destroy');
break;
default:
(status === this.STATUS_INVALID) ? $errors.show() : $errors.hide();
break;
}
@@ -1148,11 +1194,11 @@
fields = this.getFieldElements(field);
break;
default:
break;
}
- if (fields.length === 0 || this.options.fields[field] === null || this.options.fields[field].enabled === false) {
+ if (fields.length === 0 || !this.options.fields[field] || this.options.fields[field].enabled === false) {
return true;
}
var type = fields.attr('type'),
total = ('radio' === type || 'checkbox' === type) ? 1 : fields.length,
@@ -1607,11 +1653,11 @@
// 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 + '"]');
+ $icon = $field.data('bv.icon');
if ($icon) {
var container = ('function' === typeof (this.options.fields[field].container || this.options.container)) ? (this.options.fields[field].container || this.options.container).call(this, $field, this) : (this.options.fields[field].container || this.options.container);
switch (container) {
case 'tooltip':
$icon.tooltip('destroy').remove();
@@ -1622,10 +1668,11 @@
default:
$icon.remove();
break;
}
}
+ $field.removeData('bv.icon');
for (validator in this.options.fields[field].validators) {
if ($field.data('bv.dfs.' + validator)) {
$field.data('bv.dfs.' + validator).reject();
}
@@ -1672,33 +1719,42 @@
}
});
};
// The default options
+ // Sorted in alphabetical order
$.fn.bootstrapValidator.DEFAULT_OPTIONS = {
- // The form CSS class
- elementClass: 'bv-form',
+ // The first invalid field will be focused automatically
+ autoFocus: true,
- // 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 <div class="form-group"></div>
- // 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,
+ // The form CSS class
+ elementClass: 'bv-form',
+ // 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'
+ },
+
// Indicate fields which won't be validated
// By default, the plugin will not validate the following kind of fields:
// - disabled
// - hidden
// - invisible
@@ -1744,40 +1800,35 @@
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"]',
+ // Map the field name with validator rules
+ fields: null,
+ // The CSS selector for indicating the element consists the field
+ // By default, each field is placed inside the <div class="form-group"></div>
+ // 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',
+
// 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,
+ // Default invalid message
+ message: 'This value is not valid',
- // 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'
- },
-
+ // The submit buttons selector
+ // These buttons will be disabled to prevent the valid form from multiple submissions
+ submitButtons: '[type="submit"]',
+
+ // The field will not be live validated if its length is less than this number of characters
+ threshold: null,
+
// Whether to be verbose when validating a field or not.
// Possible values:
// - true: when a field has multiple validators, all of them will be checked, and respectively - if errors occur in
// multiple validators, all of them will be displayed to the user
// - false: when a field has multiple validators, validation for this field will be terminated upon the first encountered error.
@@ -2022,26 +2073,35 @@
validate: function(validator, $field, options) {
var value = $field.val();
if (value === '') {
return true;
}
+
+ value = this._format(value);
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);
+ var min = $.isNumeric(options.min) ? options.min : validator.getDynamicOption($field, options.min),
+ max = $.isNumeric(options.max) ? options.max : validator.getDynamicOption($field, options.max),
+ minValue = this._format(min),
+ maxValue = this._format(max);
+
value = parseFloat(value);
return (options.inclusive === true || options.inclusive === undefined)
? {
- valid: value >= min && value <= max,
+ valid: value >= minValue && value <= maxValue,
message: $.fn.bootstrapValidator.helpers.format(options.message || $.fn.bootstrapValidator.i18n.between['default'], [min, max])
}
: {
- valid: value > min && value < max,
+ valid: value > minValue && value < maxValue,
message: $.fn.bootstrapValidator.helpers.format(options.message || $.fn.bootstrapValidator.i18n.between.notInclusive, [min, max])
};
+ },
+
+ _format: function(value) {
+ return (value + '').replace(',', '.');
}
};
}(window.jQuery));
;(function($) {
$.fn.bootstrapValidator.validators.blank = {
@@ -2180,10 +2240,139 @@
return { valid: isValid, message: message };
}
};
}(window.jQuery));
;(function($) {
+ $.fn.bootstrapValidator.i18n.color = $.extend($.fn.bootstrapValidator.i18n.color || {}, {
+ 'default': 'Please enter a valid color'
+ });
+
+ $.fn.bootstrapValidator.validators.color = {
+ SUPPORTED_TYPES: [
+ 'hex', 'rgb', 'rgba', 'hsl', 'hsla', 'keyword'
+ ],
+
+ KEYWORD_COLORS: [
+ // Colors start with A
+ 'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure',
+ // B
+ 'beige', 'bisque', 'black', 'blanchedalmond', 'blue', 'blueviolet', 'brown', 'burlywood',
+ // C
+ 'cadetblue', 'chartreuse', 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 'cyan',
+ // D
+ 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgreen', 'darkgrey', 'darkkhaki', 'darkmagenta',
+ 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred', 'darksalmon', 'darkseagreen', 'darkslateblue',
+ 'darkslategray', 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink', 'deepskyblue', 'dimgray',
+ 'dimgrey', 'dodgerblue',
+ // F
+ 'firebrick', 'floralwhite', 'forestgreen', 'fuchsia',
+ // G
+ 'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'gray', 'green', 'greenyellow', 'grey',
+ // H
+ 'honeydew', 'hotpink',
+ // I
+ 'indianred', 'indigo', 'ivory',
+ // K
+ 'khaki',
+ // L
+ 'lavender', 'lavenderblush', 'lawngreen', 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan',
+ 'lightgoldenrodyellow', 'lightgray', 'lightgreen', 'lightgrey', 'lightpink', 'lightsalmon', 'lightseagreen',
+ 'lightskyblue', 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow', 'lime', 'limegreen',
+ 'linen',
+ // M
+ 'magenta', 'maroon', 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple', 'mediumseagreen',
+ 'mediumslateblue', 'mediumspringgreen', 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream',
+ 'mistyrose', 'moccasin',
+ // N
+ 'navajowhite', 'navy',
+ // O
+ 'oldlace', 'olive', 'olivedrab', 'orange', 'orangered', 'orchid',
+ // P
+ 'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip', 'peachpuff', 'peru', 'pink',
+ 'plum', 'powderblue', 'purple',
+ // R
+ 'red', 'rosybrown', 'royalblue',
+ // S
+ 'saddlebrown', 'salmon', 'sandybrown', 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue',
+ 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue',
+ // T
+ 'tan', 'teal', 'thistle', 'tomato', 'transparent', 'turquoise',
+ // V
+ 'violet',
+ // W
+ 'wheat', 'white', 'whitesmoke',
+ // Y
+ 'yellow', 'yellowgreen'
+ ],
+
+ /**
+ * Return true if the input value is a valid 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
+ * - type: The array of valid color types
+ * @returns {Boolean}
+ */
+ validate: function(validator, $field, options) {
+ var value = $field.val();
+ if (value === '') {
+ return true;
+ }
+
+ var types = options.type || this.SUPPORTED_TYPES;
+ if (!$.isArray(types)) {
+ types = types.replace(/s/g, '').split(',');
+ }
+
+ var method,
+ type,
+ isValid = false;
+
+ for (var i = 0; i < types.length; i++) {
+ type = types[i];
+ method = '_' + type.toLowerCase();
+ isValid = isValid || this[method](value);
+ if (isValid) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ _hex: function(value) {
+ return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(value);
+ },
+
+ _hsl: function(value) {
+ return /^hsl\((\s*(-?\d+)\s*,)(\s*(\b(0?\d{1,2}|100)\b%)\s*,)(\s*(\b(0?\d{1,2}|100)\b%)\s*)\)$/.test(value);
+ },
+
+ _hsla: function(value) {
+ return /^hsla\((\s*(-?\d+)\s*,)(\s*(\b(0?\d{1,2}|100)\b%)\s*,){2}(\s*(0?(\.\d+)?|1(\.0+)?)\s*)\)$/.test(value);
+ },
+
+ _keyword: function(value) {
+ return $.inArray(value, this.KEYWORD_COLORS) >= 0;
+ },
+
+ _rgb: function(value) {
+ var regexInteger = /^rgb\((\s*(\b([01]?\d{1,2}|2[0-4]\d|25[0-5])\b)\s*,){2}(\s*(\b([01]?\d{1,2}|2[0-4]\d|25[0-5])\b)\s*)\)$/,
+ regexPercent = /^rgb\((\s*(\b(0?\d{1,2}|100)\b%)\s*,){2}(\s*(\b(0?\d{1,2}|100)\b%)\s*)\)$/;
+ return regexInteger.test(value) || regexPercent.test(value);
+ },
+
+ _rgba: function(value) {
+ var regexInteger = /^rgba\((\s*(\b([01]?\d{1,2}|2[0-4]\d|25[0-5])\b)\s*,){3}(\s*(0?(\.\d+)?|1(\.0+)?)\s*)\)$/,
+ regexPercent = /^rgba\((\s*(\b(0?\d{1,2}|100)\b%)\s*,){3}(\s*(0?(\.\d+)?|1(\.0+)?)\s*)\)$/;
+ return regexInteger.test(value) || regexPercent.test(value);
+ }
+ };
+}(window.jQuery));
+;(function($) {
$.fn.bootstrapValidator.i18n.creditCard = $.extend($.fn.bootstrapValidator.i18n.creditCard || {}, {
'default': 'Please enter a valid credit card number'
});
$.fn.bootstrapValidator.validators.creditCard = {
@@ -2455,37 +2644,44 @@
}
};
}(window.jQuery));
;(function($) {
$.fn.bootstrapValidator.i18n.date = $.extend($.fn.bootstrapValidator.i18n.date || {}, {
- 'default': 'Please enter a valid date'
+ 'default': 'Please enter a valid date',
+ min: 'Please enter a date after %s',
+ max: 'Please enter a date before %s',
+ range: 'Please enter a date in the range %s - %s'
});
$.fn.bootstrapValidator.validators.date = {
html5Attributes: {
message: 'message',
format: 'format',
+ min: 'min',
+ max: 'max',
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
+ * - min: the minimum date
+ * - max: the maximum date
* - 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}
+ * @returns {Boolean|Object}
*/
validate: function(validator, $field, options) {
var value = $field.val();
if (value === '') {
return true;
@@ -2505,87 +2701,200 @@
sections = value.split(' '),
date = sections[0],
time = (sections.length > 1) ? sections[1] : null;
if (formats.length !== sections.length) {
- return false;
+ return {
+ valid: false,
+ message: options.message || $.fn.bootstrapValidator.i18n.date['default']
+ };
}
// 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;
+ return {
+ valid: false,
+ message: options.message || $.fn.bootstrapValidator.i18n.date['default']
+ };
}
// Determine the date
date = date.split(separator);
dateFormat = dateFormat.split(separator);
if (date.length !== dateFormat.length) {
- return false;
+ return {
+ valid: false,
+ message: options.message || $.fn.bootstrapValidator.i18n.date['default']
+ };
}
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;
+ return {
+ valid: false,
+ message: options.message || $.fn.bootstrapValidator.i18n.date['default']
+ };
}
// 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;
+ return {
+ valid: false,
+ message: options.message || $.fn.bootstrapValidator.i18n.date['default']
+ };
}
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;
+ return {
+ valid: false,
+ message: options.message || $.fn.bootstrapValidator.i18n.date['default']
+ };
}
seconds = parseInt(seconds, 10);
if (seconds < 0 || seconds > 60) {
- return false;
+ return {
+ valid: false,
+ message: options.message || $.fn.bootstrapValidator.i18n.date['default']
+ };
}
}
// Validate hours
if (hours) {
if (isNaN(hours) || hours.length > 2) {
- return false;
+ return {
+ valid: false,
+ message: options.message || $.fn.bootstrapValidator.i18n.date['default']
+ };
}
hours = parseInt(hours, 10);
if (hours < 0 || hours >= 24 || (amOrPm && hours > 12)) {
- return false;
+ return {
+ valid: false,
+ message: options.message || $.fn.bootstrapValidator.i18n.date['default']
+ };
}
}
// Validate minutes
if (minutes) {
if (isNaN(minutes) || minutes.length > 2) {
- return false;
+ return {
+ valid: false,
+ message: options.message || $.fn.bootstrapValidator.i18n.date['default']
+ };
}
minutes = parseInt(minutes, 10);
if (minutes < 0 || minutes > 59) {
- return false;
+ return {
+ valid: false,
+ message: options.message || $.fn.bootstrapValidator.i18n.date['default']
+ };
}
}
}
// Validate day, month, and year
- return $.fn.bootstrapValidator.helpers.date(year, month, day);
+ var valid = $.fn.bootstrapValidator.helpers.date(year, month, day),
+ message = options.message || $.fn.bootstrapValidator.i18n.date['default'];
+
+ // declare the date, min and max objects
+ var min = null,
+ max = null,
+ minOption = options.min,
+ maxOption = options.max;
+
+ if (minOption) {
+ if (isNaN(Date.parse(minOption))) {
+ minOption = validator.getDynamicOption($field, minOption);
+ }
+ min = this._parseDate(minOption, dateFormat, separator);
+ }
+
+ if (maxOption) {
+ if (isNaN(Date.parse(maxOption))) {
+ maxOption = validator.getDynamicOption($field, maxOption);
+ }
+ max = this._parseDate(maxOption, dateFormat, separator);
+ }
+
+ date = new Date(year, month, day, hours, minutes, seconds);
+
+ switch (true) {
+ case (minOption && !maxOption && valid):
+ valid = date.getTime() >= min.getTime();
+ message = options.message || $.fn.bootstrapValidator.helpers.format($.fn.bootstrapValidator.i18n.date.min, minOption);
+ break;
+
+ case (maxOption && !minOption && valid):
+ valid = date.getTime() <= max.getTime();
+ message = options.message || $.fn.bootstrapValidator.helpers.format($.fn.bootstrapValidator.i18n.date.max, maxOption);
+ break;
+
+ case (maxOption && minOption && valid):
+ valid = date.getTime() <= max.getTime() && date.getTime() >= min.getTime();
+ message = options.message || $.fn.bootstrapValidator.helpers.format($.fn.bootstrapValidator.i18n.date.range, [minOption, maxOption]);
+ break;
+
+ default:
+ break;
+ }
+
+ return {
+ valid: valid,
+ message: message
+ };
+ },
+
+ /**
+ * Return a date object after parsing the date string
+ *
+ * @param {String} date The date string to parse
+ * @param {String} format The date format
+ * The format can be:
+ * - date: Consist of DD, MM, YYYY parts which are separated by the separator option
+ * - date and time:
+ * The time can consist of h, m, s parts which are separated by :
+ * @param {String} separator The separator used to separate the date, month, and year
+ * @returns {Date}
+ */
+ _parseDate: function(date, format, separator) {
+ var minutes = 0, hours = 0, seconds = 0,
+ sections = date.split(' '),
+ dateSection = sections[0],
+ timeSection = (sections.length > 1) ? sections[1] : null;
+
+ dateSection = dateSection.split(separator);
+ var year = dateSection[$.inArray('YYYY', format)],
+ month = dateSection[$.inArray('MM', format)],
+ day = dateSection[$.inArray('DD', format)];
+ if (timeSection) {
+ timeSection = timeSection.split(':');
+ hours = timeSection.length > 0 ? timeSection[0] : null;
+ minutes = timeSection.length > 1 ? timeSection[1] : null;
+ seconds = timeSection.length > 2 ? timeSection[2] : null;
+ }
+
+ return new Date(year, month, day, hours, minutes, seconds);
}
};
}(window.jQuery));
;(function($) {
$.fn.bootstrapValidator.i18n.different = $.extend($.fn.bootstrapValidator.i18n.different || {}, {
@@ -2792,12 +3101,16 @@
});
$.fn.bootstrapValidator.validators.file = {
html5Attributes: {
extension: 'extension',
+ maxfiles: 'maxFiles',
+ minfiles: 'minFiles',
maxsize: 'maxSize',
minsize: 'minSize',
+ maxtotalsize: 'maxTotalSize',
+ mintotalsize: 'minTotalSize',
message: 'message',
type: 'type'
},
/**
@@ -2805,12 +3118,16 @@
*
* @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
+ * - maxFiles: The maximum number of files
+ * - minFiles: The minimum number of files
* - maxSize: The maximum size in bytes
- * - minSize: the minimum size in bytes
+ * - minSize: The minimum size in bytes
+ * - maxTotalSize: The maximum size in bytes for all files
+ * - minTotalSize: The minimum size in bytes for all files
* - message: The invalid message
* - type: The allowed MIME type, separated by a comma
* @returns {Boolean}
*/
validate: function(validator, $field, options) {
@@ -2824,33 +3141,37 @@
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;
+ var files = $field.get(0).files,
+ total = files.length,
+ totalSize = 0;
+
+ if ((options.maxFiles && total > parseInt(options.maxFiles, 10)) // Check the maxFiles
+ || (options.minFiles && total < parseInt(options.minFiles, 10))) // Check the minFiles
+ {
+ return false;
+ }
+
for (var i = 0; i < total; i++) {
- // Check the minSize
- if (options.minSize && files[i].size < parseInt(options.minSize, 10)) {
- return false;
- }
-
- // Check the maxSize
- if (options.maxSize && files[i].size > parseInt(options.maxSize, 10)) {
- return false;
- }
+ totalSize += files[i].size;
+ ext = files[i].name.substr(files[i].name.lastIndexOf('.') + 1);
- // Check file extension
- ext = files[i].name.substr(files[i].name.lastIndexOf('.') + 1);
- if (extensions && $.inArray(ext.toLowerCase(), extensions) === -1) {
+ if ((options.minSize && files[i].size < parseInt(options.minSize, 10)) // Check the minSize
+ || (options.maxSize && files[i].size > parseInt(options.maxSize, 10)) // Check the maxSize
+ || (extensions && $.inArray(ext.toLowerCase(), extensions) === -1) // Check file extension
+ || (files[i].type && types && $.inArray(files[i].type.toLowerCase(), types) === -1)) // Check file type
+ {
return false;
}
+ }
- // Check file type
- if (files[i].type && types && $.inArray(files[i].type.toLowerCase(), types) === -1) {
- return false;
- }
+ if ((options.maxTotalSize && totalSize > parseInt(options.maxTotalSize, 10)) // Check the maxTotalSize
+ || (options.minTotalSize && totalSize < parseInt(options.minTotalSize, 10))) // Check the minTotalSize
+ {
+ return false;
}
} else {
// Check file extension
ext = value.substr(value.lastIndexOf('.') + 1);
if (extensions && $.inArray(ext.toLowerCase(), extensions) === -1) {
@@ -2906,25 +3227,33 @@
validate: function(validator, $field, options) {
var value = $field.val();
if (value === '') {
return true;
}
+
+ value = this._format(value);
if (!$.isNumeric(value)) {
return false;
}
- var compareTo = $.isNumeric(options.value) ? options.value : validator.getDynamicOption($field, options.value);
+ var compareTo = $.isNumeric(options.value) ? options.value : validator.getDynamicOption($field, options.value),
+ compareToValue = this._format(compareTo);
+
value = parseFloat(value);
return (options.inclusive === true || options.inclusive === undefined)
? {
- valid: value >= compareTo,
+ valid: value >= compareToValue,
message: $.fn.bootstrapValidator.helpers.format(options.message || $.fn.bootstrapValidator.i18n.greaterThan['default'], compareTo)
}
: {
- valid: value > compareTo,
+ valid: value > compareToValue,
message: $.fn.bootstrapValidator.helpers.format(options.message || $.fn.bootstrapValidator.i18n.greaterThan.notInclusive, compareTo)
};
+ },
+
+ _format: function(value) {
+ return (value + '').replace(',', '.');
}
};
}(window.jQuery));
;(function($) {
$.fn.bootstrapValidator.i18n.grid = $.extend($.fn.bootstrapValidator.i18n.grid || {}, {
@@ -3010,11 +3339,16 @@
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);
+
+ return ('color' === $field.attr('type'))
+ // Only accept 6 hex character values due to the HTML 5 spec
+ // See http://www.w3.org/TR/html-markup/input.color.html#input.color.attrs.value
+ ? /^#[0-9A-F]{6}$/i.test(value)
+ : /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(value);
}
};
}(window.jQuery));
;(function($) {
$.fn.bootstrapValidator.i18n.iban = $.extend($.fn.bootstrapValidator.i18n.iban || {}, {
@@ -3043,11 +3377,11 @@
CV: 'Cape Verde',
CY: 'Cyprus',
CZ: 'Czech Republic',
DE: 'Germany',
DK: 'Denmark',
- DO: 'Dominica',
+ DO: 'Dominican Republic',
DZ: 'Algeria',
EE: 'Estonia',
ES: 'Spain',
FI: 'Finland',
FO: 'Faroe Islands',
@@ -5155,25 +5489,33 @@
validate: function(validator, $field, options) {
var value = $field.val();
if (value === '') {
return true;
}
+
+ value = this._format(value);
if (!$.isNumeric(value)) {
return false;
}
- var compareTo = $.isNumeric(options.value) ? options.value : validator.getDynamicOption($field, options.value);
+ var compareTo = $.isNumeric(options.value) ? options.value : validator.getDynamicOption($field, options.value),
+ compareToValue = this._format(compareTo);
+
value = parseFloat(value);
return (options.inclusive === true || options.inclusive === undefined)
? {
- valid: value <= compareTo,
+ valid: value <= compareToValue,
message: $.fn.bootstrapValidator.helpers.format(options.message || $.fn.bootstrapValidator.i18n.lessThan['default'], compareTo)
}
: {
- valid: value < compareTo,
+ valid: value < compareToValue,
message: $.fn.bootstrapValidator.helpers.format(options.message || $.fn.bootstrapValidator.i18n.lessThan.notInclusive, compareTo)
};
+ },
+
+ _format: function(value) {
+ return (value + '').replace(',', '.');
}
};
}(window.jQuery));
;(function($) {
$.fn.bootstrapValidator.i18n.mac = $.extend($.fn.bootstrapValidator.i18n.mac || {}, {
@@ -5369,10 +5711,11 @@
country: 'Please enter a valid phone number in %s',
countries: {
BR: 'Brazil',
CN: 'China',
CZ: 'Czech Republic',
+ DE: 'Germany',
DK: 'Denmark',
ES: 'Spain',
FR: 'France',
GB: 'United Kingdom',
MA: 'Morocco',
@@ -5391,11 +5734,11 @@
message: 'message',
country: 'country'
},
// The supported countries
- COUNTRY_CODES: ['BR', 'CN', 'CZ', 'DK', 'ES', 'FR', 'GB', 'MA', 'PK', 'RO', 'RU', 'SK', 'TH', 'US', 'VE'],
+ COUNTRY_CODES: ['BR', 'CN', 'CZ', 'DE', 'DK', 'ES', 'FR', 'GB', 'MA', 'PK', 'RO', 'RU', 'SK', 'TH', 'US', 'VE'],
/**
* Return true if the input value contains a valid phone number for the country
* selected in the options
*
@@ -5447,10 +5790,16 @@
case 'CZ':
// Test: http://regexr.com/39hhl
isValid = /^(((00)([- ]?)|\+)(420)([- ]?))?((\d{3})([- ]?)){2}(\d{3})$/.test(value);
break;
+ case 'DE':
+ // Test: http://regexr.com/39pkg
+ value = $.trim(value);
+ isValid = (/^(((((((00|\+)49[ \-/]?)|0)[1-9][0-9]{1,4})[ \-/]?)|((((00|\+)49\()|\(0)[1-9][0-9]{1,4}\)[ \-/]?))[0-9]{1,7}([ \-/]?[0-9]{1,5})?)$/).test(value);
+ break;
+
case 'DK':
// Mathing DK phone numbers with country code in 1 of 3 formats and an
// 8 digit phone number not starting with a 0 or 1. Can have 1 space
// between each character except inside the country code.
// Test: http://regex101.com/r/sS8fO4/1
@@ -5587,10 +5936,11 @@
html5Attributes: {
message: 'message',
name: 'name',
type: 'type',
url: 'url',
+ data: 'data',
delay: 'delay'
},
/**
* Destroy the timer when destroying the bootstrapValidator (using validator.destroy() method)
@@ -5637,10 +5987,15 @@
// Support dynamic data
if ('function' === typeof data) {
data = data.call(this, validator);
}
+ // Parse string data from HTML5 attribute
+ if ('string' === typeof data) {
+ data = JSON.parse(data);
+ }
+
// Support dynamic url
if ('function' === typeof url) {
url = url.call(this, validator);
}
@@ -5934,11 +6289,13 @@
$.fn.bootstrapValidator.validators.stringLength = {
html5Attributes: {
message: 'message',
min: 'min',
- max: 'max'
+ max: 'max',
+ trim: 'trim',
+ utf8bytes: 'utf8Bytes'
},
enableByHtml5: function($field) {
var options = {},
maxLength = $field.attr('maxlength'),
@@ -5967,23 +6324,45 @@
* - 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
+ * - trim: Indicate the length will be calculated after trimming the value or not. It is false, by default
+ * - utf8bytes: Evaluate string length in UTF-8 bytes, default to false
* @returns {Object}
*/
validate: function(validator, $field, options) {
var value = $field.val();
+ if (options.trim === true || options.trim === 'true') {
+ value = $.trim(value);
+ }
+
if (value === '') {
return true;
}
- var min = $.isNumeric(options.min) ? options.min : validator.getDynamicOption($field, options.min),
- max = $.isNumeric(options.max) ? options.max : validator.getDynamicOption($field, options.max),
- length = value.length,
- isValid = true,
- message = options.message || $.fn.bootstrapValidator.i18n.stringLength['default'];
+ var min = $.isNumeric(options.min) ? options.min : validator.getDynamicOption($field, options.min),
+ max = $.isNumeric(options.max) ? options.max : validator.getDynamicOption($field, options.max),
+ // Credit to http://stackoverflow.com/a/23329386 (@lovasoa) for UTF-8 byte length code
+ utf8Length = function(str) {
+ var s = str.length;
+ for (var i = str.length - 1; i >= 0; i--) {
+ var code = str.charCodeAt(i);
+ if (code > 0x7f && code <= 0x7ff) {
+ s++;
+ } else if (code > 0x7ff && code <= 0xffff) {
+ s += 2;
+ }
+ if (code >= 0xDC00 && code <= 0xDFFF) {
+ i--;
+ }
+ }
+ return s;
+ },
+ length = options.utf8Bytes ? utf8Length(value) : value.length,
+ isValid = true,
+ message = options.message || $.fn.bootstrapValidator.i18n.stringLength['default'];
if ((min && length < parseInt(min, 10)) || (max && length > parseInt(max, 10))) {
isValid = false;
}
@@ -7633,18 +8012,24 @@
$.fn.bootstrapValidator.i18n.zipCode = $.extend($.fn.bootstrapValidator.i18n.zipCode || {}, {
'default': 'Please enter a valid postal code',
countryNotSupported: 'The country code %s is not supported',
country: 'Please enter a valid postal code in %s',
countries: {
+ AT: 'Austria',
BR: 'Brazil',
CA: 'Canada',
+ CH: 'Switzerland',
CZ: 'Czech Republic',
+ DE: 'Germany',
DK: 'Denmark',
+ FR: 'France',
GB: 'United Kingdom',
+ IE: 'Ireland',
IT: 'Italy',
MA: 'Morocco',
NL: 'Netherlands',
+ PT: 'Portugal',
RO: 'Romania',
RU: 'Russia',
SE: 'Sweden',
SG: 'Singapore',
SK: 'Slovakia',
@@ -7656,11 +8041,11 @@
html5Attributes: {
message: 'message',
country: 'country'
},
- COUNTRY_CODES: ['BR', 'CA', 'CZ', 'DK', 'GB', 'IT', 'MA', 'NL', 'RO', 'RU', 'SE', 'SG', 'SK', 'US'],
+ COUNTRY_CODES: [ 'AT', 'BR', 'CA', 'CH', 'CZ', 'DE', 'DK', 'FR', 'GB', 'IE', 'IT', 'MA', 'NL', 'PT', 'RO', 'RU', 'SE', 'SG', 'SK', 'US'],
/**
* Return true if and only if the input value is a valid country zip code
*
* @param {BootstrapValidator} validator The validator plugin instance
@@ -7700,31 +8085,56 @@
}
var isValid = false;
country = country.toUpperCase();
switch (country) {
+ // http://en.wikipedia.org/wiki/List_of_postal_codes_in_Austria
+ case 'AT':
+ isValid = /^([1-9]{1})(\d{3})$/.test(value);
+ break;
+
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 'CH':
+ isValid = /^([1-9]{1})(\d{3})$/.test(value);
+ break;
+
case 'CZ':
// Test: http://regexr.com/39hhr
isValid = /^(\d{3})([ ]?)(\d{2})$/.test(value);
break;
+ // http://stackoverflow.com/questions/7926687/regular-expression-german-zip-codes
+ case 'DE':
+ isValid = /^(?!01000|99999)(0[1-9]\d{3}|[1-9]\d{4})$/.test(value);
+ break;
+
case 'DK':
isValid = /^(DK(-|\s)?)?\d{4}$/i.test(value);
break;
+ // http://en.wikipedia.org/wiki/Postal_codes_in_France
+ case 'FR':
+ isValid = /^[0-9]{5}$/i.test(value);
+ break;
+
case 'GB':
isValid = this._gb(value);
break;
+ // http://www.eircode.ie/docs/default-source/Common/prepare-your-business-for-eircode---published-v2.pdf?sfvrsn=2
+ // Test: http://refiddle.com/1kpl
+ case 'IE':
+ isValid = /^(D6W|[ACDEFHKNPRTVWXY]\d{2})\s[0-9ACDEFHKNPRTVWXY]{4}$/.test(value);
+ break;
+
// http://en.wikipedia.org/wiki/List_of_postal_codes_in_Italy
case 'IT':
isValid = /^(I-|IT-)?\d{5}$/i.test(value);
break;
@@ -7735,11 +8145,16 @@
// 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;
-
+
+ // Test: http://refiddle.com/1l2t
+ case 'PT':
+ isValid = /^[1-9]\d{3}-\d{3}$/.test(value);
+ break;
+
case 'RO':
isValid = /^(0[1-8]{1}|[1-9]{1}[0-5]{1})?[0-9]{4}$/i.test(value);
break;
case 'RU':
@@ -7750,10 +8165,10 @@
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 'SK':
// Test: http://regexr.com/39hhr
isValid = /^(\d{3})([ ]?)(\d{2})$/.test(value);
break;