bootstrap_validator_rails 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +32 -0
  4. data/lib/bootstrap_validator_rails/engine.rb +4 -0
  5. data/lib/bootstrap_validator_rails/version.rb +3 -0
  6. data/lib/bootstrap_validator_rails.rb +42 -0
  7. data/lib/tasks/bootstrap_validator_rails_tasks.rake +4 -0
  8. data/test/bootstrap_validator_rails_test.rb +13 -0
  9. data/test/dummy/README.rdoc +28 -0
  10. data/test/dummy/Rakefile +6 -0
  11. data/test/dummy/app/assets/javascripts/application.js +13 -0
  12. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  13. data/test/dummy/app/controllers/application_controller.rb +5 -0
  14. data/test/dummy/app/helpers/application_helper.rb +2 -0
  15. data/test/dummy/app/models/post.rb +3 -0
  16. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  17. data/test/dummy/bin/bundle +3 -0
  18. data/test/dummy/bin/rails +4 -0
  19. data/test/dummy/bin/rake +4 -0
  20. data/test/dummy/config/application.rb +23 -0
  21. data/test/dummy/config/boot.rb +5 -0
  22. data/test/dummy/config/database.yml +25 -0
  23. data/test/dummy/config/environment.rb +5 -0
  24. data/test/dummy/config/environments/development.rb +37 -0
  25. data/test/dummy/config/environments/production.rb +82 -0
  26. data/test/dummy/config/environments/test.rb +39 -0
  27. data/test/dummy/config/initializers/assets.rb +8 -0
  28. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  29. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  30. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  31. data/test/dummy/config/initializers/inflections.rb +16 -0
  32. data/test/dummy/config/initializers/mime_types.rb +4 -0
  33. data/test/dummy/config/initializers/session_store.rb +3 -0
  34. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  35. data/test/dummy/config/locales/en.yml +23 -0
  36. data/test/dummy/config/routes.rb +56 -0
  37. data/test/dummy/config/secrets.yml +22 -0
  38. data/test/dummy/config.ru +4 -0
  39. data/test/dummy/db/development.sqlite3 +0 -0
  40. data/test/dummy/db/migrate/20140723124112_create_posts.rb +7 -0
  41. data/test/dummy/db/schema.rb +20 -0
  42. data/test/dummy/db/test.sqlite3 +0 -0
  43. data/test/dummy/log/ENV=test.log +0 -0
  44. data/test/dummy/log/development.log +22 -0
  45. data/test/dummy/log/test.log +781 -0
  46. data/test/dummy/public/404.html +67 -0
  47. data/test/dummy/public/422.html +67 -0
  48. data/test/dummy/public/500.html +66 -0
  49. data/test/dummy/public/favicon.ico +0 -0
  50. data/test/test_helper.rb +15 -0
  51. data/vendor/assets/javascripts/bootstrapValidator.js +1803 -0
  52. data/vendor/assets/javascripts/language/de_DE.js +326 -0
  53. data/vendor/assets/javascripts/language/en_US.js +327 -0
  54. data/vendor/assets/javascripts/language/es_CL.js +326 -0
  55. data/vendor/assets/javascripts/language/hu_HU.js +326 -0
  56. data/vendor/assets/javascripts/language/vi_VN.js +322 -0
  57. data/vendor/assets/javascripts/language/zh_CN.js +326 -0
  58. data/vendor/assets/javascripts/language/zh_TW.js +326 -0
  59. data/vendor/assets/javascripts/validator/base64.js +25 -0
  60. data/vendor/assets/javascripts/validator/between.js +66 -0
  61. data/vendor/assets/javascripts/validator/callback.js +40 -0
  62. data/vendor/assets/javascripts/validator/choice.js +68 -0
  63. data/vendor/assets/javascripts/validator/creditCard.js +103 -0
  64. data/vendor/assets/javascripts/validator/cusip.js +55 -0
  65. data/vendor/assets/javascripts/validator/cvv.js +116 -0
  66. data/vendor/assets/javascripts/validator/date.js +118 -0
  67. data/vendor/assets/javascripts/validator/different.js +41 -0
  68. data/vendor/assets/javascripts/validator/digits.js +24 -0
  69. data/vendor/assets/javascripts/validator/ean.js +40 -0
  70. data/vendor/assets/javascripts/validator/emailAddress.js +31 -0
  71. data/vendor/assets/javascripts/validator/file.js +69 -0
  72. data/vendor/assets/javascripts/validator/greaterThan.js +61 -0
  73. data/vendor/assets/javascripts/validator/grid.js +37 -0
  74. data/vendor/assets/javascripts/validator/hex.js +25 -0
  75. data/vendor/assets/javascripts/validator/hexColor.js +28 -0
  76. data/vendor/assets/javascripts/validator/iban.js +246 -0
  77. data/vendor/assets/javascripts/validator/id.js +815 -0
  78. data/vendor/assets/javascripts/validator/identical.js +40 -0
  79. data/vendor/assets/javascripts/validator/imei.js +44 -0
  80. data/vendor/assets/javascripts/validator/integer.js +28 -0
  81. data/vendor/assets/javascripts/validator/ip.js +48 -0
  82. data/vendor/assets/javascripts/validator/isbn.js +86 -0
  83. data/vendor/assets/javascripts/validator/isin.js +59 -0
  84. data/vendor/assets/javascripts/validator/ismn.js +59 -0
  85. data/vendor/assets/javascripts/validator/issn.js +46 -0
  86. data/vendor/assets/javascripts/validator/lessThan.js +61 -0
  87. data/vendor/assets/javascripts/validator/mac.js +25 -0
  88. data/vendor/assets/javascripts/validator/notEmpty.js +32 -0
  89. data/vendor/assets/javascripts/validator/numeric.js +39 -0
  90. data/vendor/assets/javascripts/validator/phone.js +84 -0
  91. data/vendor/assets/javascripts/validator/regexp.js +42 -0
  92. data/vendor/assets/javascripts/validator/remote.js +70 -0
  93. data/vendor/assets/javascripts/validator/rtn.js +38 -0
  94. data/vendor/assets/javascripts/validator/sedol.js +40 -0
  95. data/vendor/assets/javascripts/validator/siren.js +28 -0
  96. data/vendor/assets/javascripts/validator/siret.js +38 -0
  97. data/vendor/assets/javascripts/validator/step.js +64 -0
  98. data/vendor/assets/javascripts/validator/stringCase.js +36 -0
  99. data/vendor/assets/javascripts/validator/stringLength.js +81 -0
  100. data/vendor/assets/javascripts/validator/uri.js +101 -0
  101. data/vendor/assets/javascripts/validator/uuid.js +46 -0
  102. data/vendor/assets/javascripts/validator/vat.js +1220 -0
  103. data/vendor/assets/javascripts/validator/vin.js +49 -0
  104. data/vendor/assets/javascripts/validator/zipCode.js +162 -0
  105. data/vendor/assets/stylesheets/bootstrapValidator.css +21 -0
  106. metadata +233 -0
@@ -0,0 +1,1803 @@
1
+ /**
2
+ * BootstrapValidator (http://bootstrapvalidator.com)
3
+ * The best jQuery plugin to validate form fields. Designed to use with Bootstrap 3
4
+ *
5
+ * @author http://twitter.com/nghuuphuoc
6
+ * @copyright (c) 2013 - 2014 Nguyen Huu Phuoc
7
+ * @license MIT
8
+ */
9
+ (function($) {
10
+ var BootstrapValidator = function(form, options) {
11
+ this.$form = $(form);
12
+ this.options = $.extend({}, $.fn.bootstrapValidator.DEFAULT_OPTIONS, options);
13
+
14
+ this.$invalidFields = $([]); // Array of invalid fields
15
+ this.$submitButton = null; // The submit button which is clicked to submit form
16
+
17
+ // Validating status
18
+ this.STATUS_NOT_VALIDATED = 'NOT_VALIDATED';
19
+ this.STATUS_VALIDATING = 'VALIDATING';
20
+ this.STATUS_INVALID = 'INVALID';
21
+ this.STATUS_VALID = 'VALID';
22
+
23
+ // Determine the event that is fired when user change the field value
24
+ // Most modern browsers supports input event except IE 7, 8.
25
+ // IE 9 supports input event but the event is still not fired if I press the backspace key.
26
+ // Get IE version
27
+ // https://gist.github.com/padolsey/527683/#comment-7595
28
+ var ieVersion = (function() {
29
+ var v = 3, div = document.createElement('div'), a = div.all || [];
30
+ while (div.innerHTML = '<!--[if gt IE '+(++v)+']><br><![endif]-->', a[0]) {}
31
+ return v > 4 ? v : !v;
32
+ }());
33
+
34
+ var el = document.createElement('div');
35
+ this._changeEvent = (ieVersion === 9 || !('oninput' in el)) ? 'keyup' : 'input';
36
+
37
+ // The flag to indicate that the form is ready to submit when a remote/callback validator returns
38
+ this._submitIfValid = null;
39
+
40
+ // Field elements
41
+ this._cacheFields = {};
42
+
43
+ this._init();
44
+ };
45
+
46
+ BootstrapValidator.prototype = {
47
+ constructor: BootstrapValidator,
48
+
49
+ /**
50
+ * Init form
51
+ */
52
+ _init: function() {
53
+ var that = this,
54
+ options = {
55
+ excluded: this.$form.attr('data-bv-excluded'),
56
+ trigger: this.$form.attr('data-bv-trigger'),
57
+ message: this.$form.attr('data-bv-message'),
58
+ container: this.$form.attr('data-bv-container'),
59
+ group: this.$form.attr('data-bv-group'),
60
+ submitButtons: this.$form.attr('data-bv-submitbuttons'),
61
+ threshold: this.$form.attr('data-bv-threshold'),
62
+ live: this.$form.attr('data-bv-live'),
63
+ onSuccess: this.$form.attr('data-bv-onsuccess'),
64
+ onError: this.$form.attr('data-bv-onerror'),
65
+ fields: {},
66
+ feedbackIcons: {
67
+ valid: this.$form.attr('data-bv-feedbackicons-valid'),
68
+ invalid: this.$form.attr('data-bv-feedbackicons-invalid'),
69
+ validating: this.$form.attr('data-bv-feedbackicons-validating')
70
+ }
71
+ };
72
+
73
+ this.$form
74
+ // Disable client side validation in HTML 5
75
+ .attr('novalidate', 'novalidate')
76
+ .addClass(this.options.elementClass)
77
+ // Disable the default submission first
78
+ .on('submit.bv', function(e) {
79
+ e.preventDefault();
80
+ that.validate();
81
+ })
82
+ .on('click.bv', this.options.submitButtons, function() {
83
+ that.$submitButton = $(this);
84
+ // The user just click the submit button
85
+ that._submitIfValid = true;
86
+ })
87
+ // Find all fields which have either "name" or "data-bv-field" attribute
88
+ .find('[name], [data-bv-field]')
89
+ .each(function() {
90
+ var $field = $(this),
91
+ field = $field.attr('name') || $field.attr('data-bv-field'),
92
+ opts = that._parseOptions($field);
93
+ if (opts) {
94
+ $field.attr('data-bv-field', field);
95
+ options.fields[field] = $.extend({}, opts, options.fields[field]);
96
+ }
97
+ });
98
+
99
+ this.options = $.extend(true, this.options, options);
100
+ for (var field in this.options.fields) {
101
+ this._initField(field);
102
+ }
103
+
104
+ this.$form.trigger($.Event('init.form.bv'), {
105
+ bv: this,
106
+ options: this.options
107
+ });
108
+
109
+ // Prepare the events
110
+ if (this.options.onSuccess) {
111
+ this.$form.on('success.form.bv', function(e) {
112
+ $.fn.bootstrapValidator.helpers.call(that.options.onSuccess, [e]);
113
+ });
114
+ }
115
+ if (this.options.onError) {
116
+ this.$form.on('error.form.bv', function(e) {
117
+ $.fn.bootstrapValidator.helpers.call(that.options.onError, [e]);
118
+ });
119
+ }
120
+ },
121
+
122
+ /**
123
+ * Parse the validator options from HTML attributes
124
+ *
125
+ * @param {jQuery} $field The field element
126
+ * @returns {Object}
127
+ */
128
+ _parseOptions: function($field) {
129
+ var field = $field.attr('name') || $field.attr('data-bv-field'),
130
+ validators = {},
131
+ validator,
132
+ v, // Validator name
133
+ enabled,
134
+ optionName,
135
+ optionValue,
136
+ html5AttrName,
137
+ html5AttrMap;
138
+
139
+ for (v in $.fn.bootstrapValidator.validators) {
140
+ validator = $.fn.bootstrapValidator.validators[v];
141
+ enabled = $field.attr('data-bv-' + v.toLowerCase()) + '';
142
+ html5AttrMap = ('function' === typeof validator.enableByHtml5) ? validator.enableByHtml5($field) : null;
143
+
144
+ if ((html5AttrMap && enabled !== 'false')
145
+ || (html5AttrMap !== true && ('' === enabled || 'true' === enabled)))
146
+ {
147
+ // Try to parse the options via attributes
148
+ validator.html5Attributes = $.extend({}, { message: 'message', onerror: 'onError', onsuccess: 'onSuccess' }, validator.html5Attributes);
149
+ validators[v] = $.extend({}, html5AttrMap === true ? {} : html5AttrMap, validators[v]);
150
+
151
+ for (html5AttrName in validator.html5Attributes) {
152
+ optionName = validator.html5Attributes[html5AttrName];
153
+ optionValue = $field.attr('data-bv-' + v.toLowerCase() + '-' + html5AttrName);
154
+ if (optionValue) {
155
+ if ('true' === optionValue) {
156
+ optionValue = true;
157
+ } else if ('false' === optionValue) {
158
+ optionValue = false;
159
+ }
160
+ validators[v][optionName] = optionValue;
161
+ }
162
+ }
163
+ }
164
+ }
165
+
166
+ var opts = {
167
+ excluded: $field.attr('data-bv-excluded'),
168
+ feedbackIcons: $field.attr('data-bv-feedbackicons'),
169
+ trigger: $field.attr('data-bv-trigger'),
170
+ message: $field.attr('data-bv-message'),
171
+ container: $field.attr('data-bv-container'),
172
+ group: $field.attr('data-bv-group'),
173
+ selector: $field.attr('data-bv-selector'),
174
+ threshold: $field.attr('data-bv-threshold'),
175
+ onStatus: $field.attr('data-bv-onstatus'),
176
+ onSuccess: $field.attr('data-bv-onsuccess'),
177
+ onError: $field.attr('data-bv-onerror'),
178
+ validators: validators
179
+ },
180
+ emptyOptions = $.isEmptyObject(opts), // Check if the field options are set using HTML attributes
181
+ emptyValidators = $.isEmptyObject(validators); // Check if the field validators are set using HTML attributes
182
+
183
+ if (!emptyValidators || (!emptyOptions && this.options.fields && this.options.fields[field])) {
184
+ opts.validators = validators;
185
+ return opts;
186
+ } else {
187
+ return null;
188
+ }
189
+ },
190
+
191
+ /**
192
+ * Init field
193
+ *
194
+ * @param {String|jQuery} field The field name or field element
195
+ */
196
+ _initField: function(field) {
197
+ var fields = $([]);
198
+ switch (typeof field) {
199
+ case 'object':
200
+ fields = field;
201
+ field = field.attr('data-bv-field');
202
+ break;
203
+ case 'string':
204
+ fields = this.getFieldElements(field);
205
+ fields.attr('data-bv-field', field);
206
+ break;
207
+ default:
208
+ break;
209
+ }
210
+
211
+ if (this.options.fields[field] === null || this.options.fields[field].validators === null) {
212
+ return;
213
+ }
214
+
215
+ // We don't need to validate non-existing fields
216
+ if (fields.length === 0) {
217
+ delete this.options.fields[field];
218
+ return;
219
+ }
220
+ var validatorName;
221
+ for (validatorName in this.options.fields[field].validators) {
222
+ if (!$.fn.bootstrapValidator.validators[validatorName]) {
223
+ delete this.options.fields[field].validators[validatorName];
224
+ }
225
+ }
226
+ if (this.options.fields[field].enabled === null) {
227
+ this.options.fields[field].enabled = true;
228
+ }
229
+
230
+ var that = this,
231
+ total = fields.length,
232
+ type = fields.attr('type'),
233
+ updateAll = (total === 1) || ('radio' === type) || ('checkbox' === type),
234
+ event = ('radio' === type || 'checkbox' === type || 'file' === type || 'SELECT' === fields.eq(0).get(0).tagName) ? 'change' : this._changeEvent,
235
+ trigger = (this.options.fields[field].trigger || this.options.trigger || event).split(' '),
236
+ events = $.map(trigger, function(item) {
237
+ return item + '.update.bv';
238
+ }).join(' ');
239
+
240
+ for (var i = 0; i < total; i++) {
241
+ var $field = fields.eq(i),
242
+ group = this.options.fields[field].group || this.options.group,
243
+ $parent = $field.parents(group),
244
+ // Allow user to indicate where the error messages are shown
245
+ container = this.options.fields[field].container || this.options.container,
246
+ $message = (container && container !== 'tooltip' && container !== 'popover') ? $(container) : this._getMessageContainer($field, group);
247
+
248
+ if (container && container !== 'tooltip' && container !== 'popover') {
249
+ $message.addClass('has-error');
250
+ }
251
+
252
+ // Remove all error messages and feedback icons
253
+ $message.find('.help-block[data-bv-validator][data-bv-for="' + field + '"]').remove();
254
+ $parent.find('i[data-bv-icon-for="' + field + '"]').remove();
255
+
256
+ // Whenever the user change the field value, mark it as not validated yet
257
+ $field.off(events).on(events, function() {
258
+ that.updateStatus($(this), that.STATUS_NOT_VALIDATED);
259
+ });
260
+
261
+ // Create help block elements for showing the error messages
262
+ $field.data('bv.messages', $message);
263
+ for (validatorName in this.options.fields[field].validators) {
264
+ $field.data('bv.result.' + validatorName, this.STATUS_NOT_VALIDATED);
265
+
266
+ if (!updateAll || i === total - 1) {
267
+ $('<small/>')
268
+ .css('display', 'none')
269
+ .addClass('help-block')
270
+ .attr('data-bv-validator', validatorName)
271
+ .attr('data-bv-for', field)
272
+ .attr('data-bv-result', this.STATUS_NOT_VALIDATED)
273
+ .html(this._getMessage(field, validatorName))
274
+ .appendTo($message);
275
+ }
276
+
277
+ // Prepare the validator events
278
+ if (this.options.fields[field].validators[validatorName].onSuccess) {
279
+ $field.on('success.validator.bv', function(e, data) {
280
+ $.fn.bootstrapValidator.helpers.call(that.options.fields[field].validators[validatorName].onSuccess, [e, data]);
281
+ });
282
+ }
283
+ if (this.options.fields[field].validators[validatorName].onError) {
284
+ $field.on('error.validator.bv', function(e, data) {
285
+ $.fn.bootstrapValidator.helpers.call(that.options.fields[field].validators[validatorName].onError, [e, data]);
286
+ });
287
+ }
288
+ }
289
+
290
+ // Prepare the feedback icons
291
+ // Available from Bootstrap 3.1 (http://getbootstrap.com/css/#forms-control-validation)
292
+ if (this.options.fields[field].feedbackIcons !== false && this.options.fields[field].feedbackIcons !== 'false'
293
+ && this.options.feedbackIcons
294
+ && this.options.feedbackIcons.validating && this.options.feedbackIcons.invalid && this.options.feedbackIcons.valid
295
+ && (!updateAll || i === total - 1))
296
+ {
297
+ $parent.removeClass('has-success').removeClass('has-error').addClass('has-feedback');
298
+ var $icon = $('<i/>')
299
+ .css('display', 'none')
300
+ .addClass('form-control-feedback')
301
+ .attr('data-bv-icon-for', field)
302
+ // Place it after the label containing the checkbox/radio
303
+ // so when clicking the icon, it doesn't effect to the checkbox/radio element
304
+ .insertAfter(('checkbox' === type || 'radio' === type) ? $field.parent() : $field);
305
+
306
+ // The feedback icon does not render correctly if there is no label
307
+ // https://github.com/twbs/bootstrap/issues/12873
308
+ if ($parent.find('label').length === 0) {
309
+ $icon.css('top', 0);
310
+ }
311
+ // Fix feedback icons in input-group
312
+ if ($parent.find('.input-group').length !== 0) {
313
+ $icon.css({
314
+ 'top': 0,
315
+ 'z-index': 100
316
+ }).insertAfter($parent.find('.input-group').eq(0));
317
+ }
318
+ }
319
+ }
320
+
321
+ // Prepare the events
322
+ if (this.options.fields[field].onSuccess) {
323
+ fields.on('success.field.bv', function(e, data) {
324
+ $.fn.bootstrapValidator.helpers.call(that.options.fields[field].onSuccess, [e, data]);
325
+ });
326
+ }
327
+ if (this.options.fields[field].onError) {
328
+ fields.on('error.field.bv', function(e, data) {
329
+ $.fn.bootstrapValidator.helpers.call(that.options.fields[field].onError, [e, data]);
330
+ });
331
+ }
332
+ if (this.options.fields[field].onStatus) {
333
+ fields.on('status.field.bv', function(e, data) {
334
+ $.fn.bootstrapValidator.helpers.call(that.options.fields[field].onStatus, [e, data]);
335
+ });
336
+ }
337
+
338
+ // Set live mode
339
+ events = $.map(trigger, function(item) {
340
+ return item + '.live.bv';
341
+ }).join(' ');
342
+ switch (this.options.live) {
343
+ case 'submitted':
344
+ break;
345
+ case 'disabled':
346
+ fields.off(events);
347
+ break;
348
+ case 'enabled':
349
+ /* falls through */
350
+ default:
351
+ fields.off(events).on(events, function() {
352
+ if (that._exceedThreshold($(this))) {
353
+ that.validateField($(this));
354
+ }
355
+ });
356
+ break;
357
+ }
358
+
359
+ fields.trigger($.Event('init.field.bv'), {
360
+ bv: this,
361
+ field: field,
362
+ element: fields
363
+ });
364
+ },
365
+
366
+ /**
367
+ * Get the error message for given field and validator
368
+ *
369
+ * @param {String} field The field name
370
+ * @param {String} validatorName The validator name
371
+ * @returns {String}
372
+ */
373
+ _getMessage: function(field, validatorName) {
374
+ if (!this.options.fields[field] || !$.fn.bootstrapValidator.validators[validatorName]
375
+ || !this.options.fields[field].validators || !this.options.fields[field].validators[validatorName])
376
+ {
377
+ return '';
378
+ }
379
+
380
+ var options = this.options.fields[field].validators[validatorName];
381
+ switch (true) {
382
+ case (!!options.message):
383
+ return options.message;
384
+ case (!!this.options.fields[field].message):
385
+ return this.options.fields[field].message;
386
+ case (!!$.fn.bootstrapValidator.i18n[validatorName]):
387
+ return $.fn.bootstrapValidator.i18n[validatorName]['default'];
388
+ default:
389
+ return this.options.message;
390
+ }
391
+ },
392
+
393
+ /**
394
+ * Get the element to place the error messages
395
+ *
396
+ * @param {jQuery} $field The field element
397
+ * @param {String} group
398
+ * @returns {jQuery}
399
+ */
400
+ _getMessageContainer: function($field, group) {
401
+ var $parent = $field.parent();
402
+ if ($parent.is(group)) {
403
+ return $parent;
404
+ }
405
+
406
+ var cssClasses = $parent.attr('class');
407
+ if (!cssClasses) {
408
+ return this._getMessageContainer($parent, group);
409
+ }
410
+
411
+ cssClasses = cssClasses.split(' ');
412
+ var n = cssClasses.length;
413
+ for (var i = 0; i < n; i++) {
414
+ if (/^col-(xs|sm|md|lg)-\d+$/.test(cssClasses[i]) || /^col-(xs|sm|md|lg)-offset-\d+$/.test(cssClasses[i])) {
415
+ return $parent;
416
+ }
417
+ }
418
+
419
+ return this._getMessageContainer($parent, group);
420
+ },
421
+
422
+ /**
423
+ * Called when all validations are completed
424
+ */
425
+ _submit: function() {
426
+ var isValid = this.isValid(),
427
+ eventType = isValid ? 'success.form.bv' : 'error.form.bv',
428
+ e = $.Event(eventType);
429
+
430
+ this.$form.trigger(e);
431
+
432
+ // Call default handler
433
+ // Check if whether the submit button is clicked
434
+ if (this.$submitButton) {
435
+ isValid ? this._onSuccess(e) : this._onError(e);
436
+ }
437
+ },
438
+
439
+ /**
440
+ * Check if the field is excluded.
441
+ * Returning true means that the field will not be validated
442
+ *
443
+ * @param {jQuery} $field The field element
444
+ * @returns {Boolean}
445
+ */
446
+ _isExcluded: function($field) {
447
+ var excludedAttr = $field.attr('data-bv-excluded'),
448
+ // I still need to check the 'name' attribute while initializing the field
449
+ field = $field.attr('data-bv-field') || $field.attr('name');
450
+
451
+ switch (true) {
452
+ case (!!field && this.options.fields && this.options.fields[field] && (this.options.fields[field].excluded === 'true' || this.options.fields[field].excluded === true)):
453
+ case (excludedAttr === 'true'):
454
+ case (excludedAttr === ''):
455
+ return true;
456
+
457
+ case (!!field && this.options.fields && this.options.fields[field] && (this.options.fields[field].excluded === 'false' || this.options.fields[field].excluded === false)):
458
+ case (excludedAttr === 'false'):
459
+ return false;
460
+
461
+ default:
462
+ if (this.options.excluded) {
463
+ // Convert to array first
464
+ if ('string' === typeof this.options.excluded) {
465
+ this.options.excluded = $.map(this.options.excluded.split(','), function(item) {
466
+ // Trim the spaces
467
+ return $.trim(item);
468
+ });
469
+ }
470
+
471
+ var length = this.options.excluded.length;
472
+ for (var i = 0; i < length; i++) {
473
+ if (('string' === typeof this.options.excluded[i] && $field.is(this.options.excluded[i]))
474
+ || ('function' === typeof this.options.excluded[i] && this.options.excluded[i].call(this, $field, this) === true))
475
+ {
476
+ return true;
477
+ }
478
+ }
479
+ }
480
+ return false;
481
+ }
482
+ },
483
+
484
+ /**
485
+ * Check if the number of characters of field value exceed the threshold or not
486
+ *
487
+ * @param {jQuery} $field The field element
488
+ * @returns {Boolean}
489
+ */
490
+ _exceedThreshold: function($field) {
491
+ var field = $field.attr('data-bv-field'),
492
+ threshold = this.options.fields[field].threshold || this.options.threshold;
493
+ if (!threshold) {
494
+ return true;
495
+ }
496
+ var cannotType = $.inArray($field.attr('type'), ['button', 'checkbox', 'file', 'hidden', 'image', 'radio', 'reset', 'submit']) !== -1;
497
+ return (cannotType || $field.val().length >= threshold);
498
+ },
499
+
500
+ // ---
501
+ // Events
502
+ // ---
503
+
504
+ /**
505
+ * The default handler of error.form.bv event.
506
+ * It will be called when there is a invalid field
507
+ *
508
+ * @param {jQuery.Event} e The jQuery event object
509
+ */
510
+ _onError: function(e) {
511
+ if (e.isDefaultPrevented()) {
512
+ return;
513
+ }
514
+
515
+ if ('submitted' === this.options.live) {
516
+ // Enable live mode
517
+ this.options.live = 'enabled';
518
+ var that = this;
519
+ for (var field in this.options.fields) {
520
+ (function(f) {
521
+ var fields = that.getFieldElements(f);
522
+ if (fields.length) {
523
+ var type = $(fields[0]).attr('type'),
524
+ event = ('radio' === type || 'checkbox' === type || 'file' === type || 'SELECT' === $(fields[0]).get(0).tagName) ? 'change' : that._changeEvent,
525
+ trigger = that.options.fields[field].trigger || that.options.trigger || event,
526
+ events = $.map(trigger.split(' '), function(item) {
527
+ return item + '.live.bv';
528
+ }).join(' ');
529
+
530
+ fields.off(events).on(events, function() {
531
+ if (that._exceedThreshold($(this))) {
532
+ that.validateField($(this));
533
+ }
534
+ });
535
+ }
536
+ })(field);
537
+ }
538
+ }
539
+
540
+ var $invalidField = this.$invalidFields.eq(0);
541
+ if ($invalidField) {
542
+ // Activate the tab containing the invalid field if exists
543
+ var $tabPane = $invalidField.parents('.tab-pane'), tabId;
544
+ if ($tabPane && (tabId = $tabPane.attr('id'))) {
545
+ $('a[href="#' + tabId + '"][data-toggle="tab"]').tab('show');
546
+ }
547
+
548
+ // Focus to the first invalid field
549
+ $invalidField.focus();
550
+ }
551
+ },
552
+
553
+ /**
554
+ * The default handler of success.form.bv event.
555
+ * It will be called when all the fields are valid
556
+ *
557
+ * @param {jQuery.Event} e The jQuery event object
558
+ */
559
+ _onSuccess: function(e) {
560
+ if (e.isDefaultPrevented()) {
561
+ return;
562
+ }
563
+
564
+ // Submit the form
565
+ this.disableSubmitButtons(true).defaultSubmit();
566
+ },
567
+
568
+ /**
569
+ * Called after validating a field element
570
+ *
571
+ * @param {jQuery} $field The field element
572
+ * @param {String} [validatorName] The validator name
573
+ */
574
+ _onFieldValidated: function($field, validatorName) {
575
+ var field = $field.attr('data-bv-field'),
576
+ validators = this.options.fields[field].validators,
577
+ counter = {},
578
+ numValidators = 0,
579
+ data = {
580
+ bv: this,
581
+ field: field,
582
+ element: $field,
583
+ validator: validatorName
584
+ };
585
+
586
+ // Trigger an event after given validator completes
587
+ if (validatorName) {
588
+ switch ($field.data('bv.result.' + validatorName)) {
589
+ case this.STATUS_INVALID:
590
+ $field.trigger($.Event('error.validator.bv'), data);
591
+ break;
592
+ case this.STATUS_VALID:
593
+ $field.trigger($.Event('success.validator.bv'), data);
594
+ break;
595
+ default:
596
+ break;
597
+ }
598
+ }
599
+
600
+ counter[this.STATUS_NOT_VALIDATED] = 0;
601
+ counter[this.STATUS_VALIDATING] = 0;
602
+ counter[this.STATUS_INVALID] = 0;
603
+ counter[this.STATUS_VALID] = 0;
604
+
605
+ for (var v in validators) {
606
+ if (validators[v].enabled === false) {
607
+ continue;
608
+ }
609
+
610
+ numValidators++;
611
+ var result = $field.data('bv.result.' + v);
612
+ if (result) {
613
+ counter[result]++;
614
+ }
615
+ }
616
+
617
+ if (counter[this.STATUS_VALID] === numValidators) {
618
+ // Remove from the list of invalid fields
619
+ this.$invalidFields = this.$invalidFields.not($field);
620
+
621
+ $field.trigger($.Event('success.field.bv'), data);
622
+ }
623
+ // If all validators are completed and there is at least one validator which doesn't pass
624
+ else if (counter[this.STATUS_NOT_VALIDATED] === 0 && counter[this.STATUS_VALIDATING] === 0 && counter[this.STATUS_INVALID] > 0) {
625
+ // Add to the list of invalid fields
626
+ this.$invalidFields = this.$invalidFields.add($field);
627
+
628
+ $field.trigger($.Event('error.field.bv'), data);
629
+ }
630
+ },
631
+
632
+ // ---
633
+ // Public methods
634
+ // ---
635
+
636
+ /**
637
+ * Retrieve the field elements by given name
638
+ *
639
+ * @param {String} field The field name
640
+ * @returns {null|jQuery[]}
641
+ */
642
+ getFieldElements: function(field) {
643
+ if (!this._cacheFields[field]) {
644
+ this._cacheFields[field] = (this.options.fields[field] && this.options.fields[field].selector)
645
+ ? $(this.options.fields[field].selector)
646
+ : this.$form.find('[name="' + field + '"]');
647
+ }
648
+
649
+ return this._cacheFields[field];
650
+ },
651
+
652
+ /**
653
+ * Disable/enable submit buttons
654
+ *
655
+ * @param {Boolean} disabled Can be true or false
656
+ * @returns {BootstrapValidator}
657
+ */
658
+ disableSubmitButtons: function(disabled) {
659
+ if (!disabled) {
660
+ this.$form.find(this.options.submitButtons).removeAttr('disabled');
661
+ } else if (this.options.live !== 'disabled') {
662
+ // Don't disable if the live validating mode is disabled
663
+ this.$form.find(this.options.submitButtons).attr('disabled', 'disabled');
664
+ }
665
+
666
+ return this;
667
+ },
668
+
669
+ /**
670
+ * Validate the form
671
+ *
672
+ * @returns {BootstrapValidator}
673
+ */
674
+ validate: function() {
675
+ if (!this.options.fields) {
676
+ return this;
677
+ }
678
+ this.disableSubmitButtons(true);
679
+
680
+ for (var field in this.options.fields) {
681
+ this.validateField(field);
682
+ }
683
+
684
+ this._submit();
685
+
686
+ return this;
687
+ },
688
+
689
+ /**
690
+ * Validate given field
691
+ *
692
+ * @param {String|jQuery} field The field name or field element
693
+ * @returns {BootstrapValidator}
694
+ */
695
+ validateField: function(field) {
696
+ var fields = $([]);
697
+ switch (typeof field) {
698
+ case 'object':
699
+ fields = field;
700
+ field = field.attr('data-bv-field');
701
+ break;
702
+ case 'string':
703
+ fields = this.getFieldElements(field);
704
+ break;
705
+ default:
706
+ break;
707
+ }
708
+
709
+ if (this.options.fields[field] && this.options.fields[field].enabled === false) {
710
+ return this;
711
+ }
712
+
713
+ var that = this,
714
+ type = fields.attr('type'),
715
+ total = ('radio' === type || 'checkbox' === type) ? 1 : fields.length,
716
+ updateAll = ('radio' === type || 'checkbox' === type),
717
+ validators = this.options.fields[field].validators,
718
+ validatorName,
719
+ validateResult;
720
+
721
+ for (var i = 0; i < total; i++) {
722
+ var $field = fields.eq(i);
723
+ if (this._isExcluded($field)) {
724
+ continue;
725
+ }
726
+
727
+ for (validatorName in validators) {
728
+ if ($field.data('bv.dfs.' + validatorName)) {
729
+ $field.data('bv.dfs.' + validatorName).reject();
730
+ }
731
+
732
+ // Don't validate field if it is already done
733
+ var result = $field.data('bv.result.' + validatorName);
734
+ if (result === this.STATUS_VALID || result === this.STATUS_INVALID || validators[validatorName].enabled === false) {
735
+ this._onFieldValidated($field, validatorName);
736
+ continue;
737
+ }
738
+
739
+ $field.data('bv.result.' + validatorName, this.STATUS_VALIDATING);
740
+ validateResult = $.fn.bootstrapValidator.validators[validatorName].validate(this, $field, validators[validatorName]);
741
+
742
+ // validateResult can be a $.Deferred object ...
743
+ if ('object' === typeof validateResult && validateResult.resolve) {
744
+ this.updateStatus(updateAll ? field : $field, this.STATUS_VALIDATING, validatorName);
745
+ $field.data('bv.dfs.' + validatorName, validateResult);
746
+
747
+ validateResult.done(function($f, v, isValid, message) {
748
+ // v is validator name
749
+ $f.removeData('bv.dfs.' + v);
750
+ if (message) {
751
+ that.updateMessage($f, v, message);
752
+ }
753
+
754
+ that.updateStatus(updateAll ? $f.attr('data-bv-field') : $f, isValid ? that.STATUS_VALID : that.STATUS_INVALID, v);
755
+
756
+ if (isValid && that._submitIfValid === true) {
757
+ // If a remote validator returns true and the form is ready to submit, then do it
758
+ that._submit();
759
+ }
760
+ });
761
+ }
762
+ // ... or object { valid: true/false, message: 'dynamic message' }
763
+ else if ('object' === typeof validateResult && validateResult.valid !== undefined && validateResult.message !== undefined) {
764
+ this.updateMessage(updateAll ? field : $field, validatorName, validateResult.message);
765
+ this.updateStatus(updateAll ? field : $field, validateResult.valid ? this.STATUS_VALID : this.STATUS_INVALID, validatorName);
766
+ }
767
+ // ... or a boolean value
768
+ else if ('boolean' === typeof validateResult) {
769
+ this.updateStatus(updateAll ? field : $field, validateResult ? this.STATUS_VALID : this.STATUS_INVALID, validatorName);
770
+ }
771
+ }
772
+ }
773
+
774
+ return this;
775
+ },
776
+
777
+ /**
778
+ * Update the error message
779
+ *
780
+ * @param {String|jQuery} field The field name or field element
781
+ * @param {String} validator The validator name
782
+ * @param {String} message The message
783
+ * @returns {BootstrapValidator}
784
+ */
785
+ updateMessage: function(field, validator, message) {
786
+ var $fields = $([]);
787
+ switch (typeof field) {
788
+ case 'object':
789
+ $fields = field;
790
+ field = field.attr('data-bv-field');
791
+ break;
792
+ case 'string':
793
+ $fields = this.getFieldElements(field);
794
+ break;
795
+ default:
796
+ break;
797
+ }
798
+
799
+ $fields.each(function() {
800
+ $(this).data('bv.messages').find('.help-block[data-bv-validator="' + validator + '"][data-bv-for="' + field + '"]').html(message);
801
+ });
802
+ },
803
+
804
+ /**
805
+ * Update all validating results of field
806
+ *
807
+ * @param {String|jQuery} field The field name or field element
808
+ * @param {String} status The status. Can be 'NOT_VALIDATED', 'VALIDATING', 'INVALID' or 'VALID'
809
+ * @param {String} [validatorName] The validator name. If null, the method updates validity result for all validators
810
+ * @returns {BootstrapValidator}
811
+ */
812
+ updateStatus: function(field, status, validatorName) {
813
+ var fields = $([]);
814
+ switch (typeof field) {
815
+ case 'object':
816
+ fields = field;
817
+ field = field.attr('data-bv-field');
818
+ break;
819
+ case 'string':
820
+ fields = this.getFieldElements(field);
821
+ break;
822
+ default:
823
+ break;
824
+ }
825
+
826
+ if (status === this.STATUS_NOT_VALIDATED) {
827
+ // Reset the flag
828
+ this._submitIfValid = false;
829
+ }
830
+
831
+ var that = this,
832
+ type = fields.attr('type'),
833
+ group = this.options.fields[field].group || this.options.group,
834
+ total = ('radio' === type || 'checkbox' === type) ? 1 : fields.length;
835
+
836
+ for (var i = 0; i < total; i++) {
837
+ var $field = fields.eq(i);
838
+ if (this._isExcluded($field)) {
839
+ continue;
840
+ }
841
+
842
+ var $parent = $field.parents(group),
843
+ $message = $field.data('bv.messages'),
844
+ $allErrors = $message.find('.help-block[data-bv-validator][data-bv-for="' + field + '"]'),
845
+ $errors = validatorName ? $allErrors.filter('[data-bv-validator="' + validatorName + '"]') : $allErrors,
846
+ $icon = $parent.find('.form-control-feedback[data-bv-icon-for="' + field + '"]'),
847
+ container = this.options.fields[field].container || this.options.container,
848
+ isValidField = null;
849
+
850
+ // Update status
851
+ if (validatorName) {
852
+ $field.data('bv.result.' + validatorName, status);
853
+ } else {
854
+ for (var v in this.options.fields[field].validators) {
855
+ $field.data('bv.result.' + v, status);
856
+ }
857
+ }
858
+
859
+ // Show/hide error elements and feedback icons
860
+ $errors.attr('data-bv-result', status);
861
+
862
+ // Determine the tab containing the element
863
+ var $tabPane = $field.parents('.tab-pane'),
864
+ tabId, $tab;
865
+ if ($tabPane && (tabId = $tabPane.attr('id'))) {
866
+ $tab = $('a[href="#' + tabId + '"][data-toggle="tab"]').parent();
867
+ }
868
+
869
+ switch (status) {
870
+ case this.STATUS_VALIDATING:
871
+ isValidField = null;
872
+ this.disableSubmitButtons(true);
873
+ $parent.removeClass('has-success').removeClass('has-error');
874
+ if ($icon) {
875
+ $icon.removeClass(this.options.feedbackIcons.valid).removeClass(this.options.feedbackIcons.invalid).addClass(this.options.feedbackIcons.validating).show();
876
+ }
877
+ if ($tab) {
878
+ $tab.removeClass('bv-tab-success').removeClass('bv-tab-error');
879
+ }
880
+ break;
881
+
882
+ case this.STATUS_INVALID:
883
+ isValidField = false;
884
+ this.disableSubmitButtons(true);
885
+ $parent.removeClass('has-success').addClass('has-error');
886
+ if ($icon) {
887
+ $icon.removeClass(this.options.feedbackIcons.valid).removeClass(this.options.feedbackIcons.validating).addClass(this.options.feedbackIcons.invalid).show();
888
+ }
889
+ if ($tab) {
890
+ $tab.removeClass('bv-tab-success').addClass('bv-tab-error');
891
+ }
892
+ break;
893
+
894
+ case this.STATUS_VALID:
895
+ // If the field is valid (passes all validators)
896
+ isValidField = ($allErrors.filter('[data-bv-result="' + this.STATUS_NOT_VALIDATED +'"]').length === 0)
897
+ ? ($allErrors.filter('[data-bv-result="' + this.STATUS_VALID +'"]').length === $allErrors.length) // All validators are completed
898
+ : null; // There are some validators that have not done
899
+ if (isValidField !== null) {
900
+ this.disableSubmitButtons(this.$submitButton ? !this.isValid() : !isValidField);
901
+ if ($icon) {
902
+ $icon
903
+ .removeClass(this.options.feedbackIcons.invalid).removeClass(this.options.feedbackIcons.validating).removeClass(this.options.feedbackIcons.valid)
904
+ .addClass(isValidField ? this.options.feedbackIcons.valid : this.options.feedbackIcons.invalid)
905
+ .show();
906
+ }
907
+ }
908
+
909
+ $parent.removeClass('has-error has-success').addClass(this.isValidContainer($parent) ? 'has-success' : 'has-error');
910
+ if ($tab) {
911
+ $tab.removeClass('bv-tab-success').removeClass('bv-tab-error').addClass(this.isValidContainer($tabPane) ? 'bv-tab-success' : 'bv-tab-error');
912
+ }
913
+ break;
914
+
915
+ case this.STATUS_NOT_VALIDATED:
916
+ /* falls through */
917
+ default:
918
+ isValidField = null;
919
+ this.disableSubmitButtons(false);
920
+ $parent.removeClass('has-success').removeClass('has-error');
921
+ if ($icon) {
922
+ $icon.removeClass(this.options.feedbackIcons.valid).removeClass(this.options.feedbackIcons.invalid).removeClass(this.options.feedbackIcons.validating).hide();
923
+ }
924
+ if ($tab) {
925
+ $tab.removeClass('bv-tab-success').removeClass('bv-tab-error');
926
+ }
927
+ break;
928
+ }
929
+
930
+ switch (true) {
931
+ // Only show the first error message if it is placed inside a tooltip ...
932
+ case ($icon && 'tooltip' === container):
933
+ (isValidField === false)
934
+ ? $icon.css('cursor', 'pointer').tooltip('destroy').tooltip({
935
+ html: true,
936
+ placement: 'top',
937
+ title: $allErrors.filter('[data-bv-result="' + that.STATUS_INVALID + '"]').eq(0).html()
938
+ })
939
+ : $icon.css('cursor', '').tooltip('destroy');
940
+ break;
941
+ // ... or popover
942
+ case ($icon && 'popover' === container):
943
+ (isValidField === false)
944
+ ? $icon.css('cursor', 'pointer').popover('destroy').popover({
945
+ content: $allErrors.filter('[data-bv-result="' + that.STATUS_INVALID + '"]').eq(0).html(),
946
+ html: true,
947
+ placement: 'top',
948
+ trigger: 'hover click'
949
+ })
950
+ : $icon.css('cursor', '').popover('destroy');
951
+ break;
952
+ default:
953
+ (status === this.STATUS_INVALID) ? $errors.show() : $errors.hide();
954
+ break;
955
+ }
956
+
957
+ // Trigger an event
958
+ $field.trigger($.Event('status.field.bv'), {
959
+ bv: this,
960
+ field: field,
961
+ element: $field,
962
+ status: status
963
+ });
964
+ this._onFieldValidated($field, validatorName);
965
+ }
966
+
967
+ return this;
968
+ },
969
+
970
+ /**
971
+ * Check the form validity
972
+ *
973
+ * @returns {Boolean}
974
+ */
975
+ isValid: function() {
976
+ for (var field in this.options.fields) {
977
+ if (!this.isValidField(field)) {
978
+ return false;
979
+ }
980
+ }
981
+
982
+ return true;
983
+ },
984
+
985
+ /**
986
+ * Check if the field is valid or not
987
+ *
988
+ * @param {String|jQuery} field The field name or field element
989
+ * @returns {Boolean}
990
+ */
991
+ isValidField: function(field) {
992
+ var fields = $([]);
993
+ switch (typeof field) {
994
+ case 'object':
995
+ fields = field;
996
+ field = field.attr('data-bv-field');
997
+ break;
998
+ case 'string':
999
+ fields = this.getFieldElements(field);
1000
+ break;
1001
+ default:
1002
+ break;
1003
+ }
1004
+ if (fields.length === 0 || this.options.fields[field] === null || this.options.fields[field].enabled === false) {
1005
+ return true;
1006
+ }
1007
+
1008
+ var type = fields.attr('type'),
1009
+ total = ('radio' === type || 'checkbox' === type) ? 1 : fields.length,
1010
+ $field, validatorName, status;
1011
+ for (var i = 0; i < total; i++) {
1012
+ $field = fields.eq(i);
1013
+ if (this._isExcluded($field)) {
1014
+ continue;
1015
+ }
1016
+
1017
+ for (validatorName in this.options.fields[field].validators) {
1018
+ if (this.options.fields[field].validators[validatorName].enabled === false) {
1019
+ continue;
1020
+ }
1021
+
1022
+ status = $field.data('bv.result.' + validatorName);
1023
+ if (status !== this.STATUS_VALID) {
1024
+ return false;
1025
+ }
1026
+ }
1027
+ }
1028
+
1029
+ return true;
1030
+ },
1031
+
1032
+ /**
1033
+ * Check if all fields inside a given container are valid.
1034
+ * It's useful when working with a wizard-like such as tab, collapse
1035
+ *
1036
+ * @param {String|jQuery} container The container selector or element
1037
+ * @returns {Boolean}
1038
+ */
1039
+ isValidContainer: function(container) {
1040
+ var that = this,
1041
+ map = {},
1042
+ $container = ('string' === typeof container) ? $(container) : container;
1043
+ if ($container.length === 0) {
1044
+ return true;
1045
+ }
1046
+
1047
+ $container.find('[data-bv-field]').each(function() {
1048
+ var $field = $(this),
1049
+ field = $field.attr('data-bv-field');
1050
+ if (!that._isExcluded($field) && !map[field]) {
1051
+ map[field] = $field;
1052
+ }
1053
+ });
1054
+
1055
+ for (var field in map) {
1056
+ var $f = map[field];
1057
+ if ($f.data('bv.messages')
1058
+ .find('.help-block[data-bv-validator][data-bv-for="' + field + '"]')
1059
+ .filter(function() {
1060
+ var v = $(this).attr('data-bv-validator'),
1061
+ f = $(this).attr('data-bv-for');
1062
+ return (that.options.fields[f].validators[v].enabled !== false
1063
+ && $f.data('bv.result.' + v) && $f.data('bv.result.' + v) !== that.STATUS_VALID);
1064
+ })
1065
+ .length !== 0)
1066
+ {
1067
+ // The field is not valid
1068
+ return false;
1069
+ }
1070
+ }
1071
+
1072
+ return true;
1073
+ },
1074
+
1075
+ /**
1076
+ * Submit the form using default submission.
1077
+ * It also does not perform any validations when submitting the form
1078
+ */
1079
+ defaultSubmit: function() {
1080
+ if (this.$submitButton) {
1081
+ // Create hidden input to send the submit buttons
1082
+ $('<input/>')
1083
+ .attr('type', 'hidden')
1084
+ .attr('data-bv-submit-hidden', '')
1085
+ .attr('name', this.$submitButton.attr('name'))
1086
+ .val(this.$submitButton.val())
1087
+ .appendTo(this.$form);
1088
+ }
1089
+
1090
+ // Submit form
1091
+ this.$form.off('submit.bv').submit();
1092
+ },
1093
+
1094
+ // ---
1095
+ // Useful APIs which aren't used internally
1096
+ // ---
1097
+
1098
+ /**
1099
+ * Get the list of invalid fields
1100
+ *
1101
+ * @returns {jQuery[]}
1102
+ */
1103
+ getInvalidFields: function() {
1104
+ return this.$invalidFields;
1105
+ },
1106
+
1107
+ /**
1108
+ * Returns the clicked submit button
1109
+ *
1110
+ * @returns {jQuery}
1111
+ */
1112
+ getSubmitButton: function() {
1113
+ return this.$submitButton;
1114
+ },
1115
+
1116
+ /**
1117
+ * Get the error messages
1118
+ *
1119
+ * @param {String|jQuery} [field] The field name or field element
1120
+ * If the field is not defined, the method returns all error messages of all fields
1121
+ * @param {String} [validator] The name of validator
1122
+ * If the validator is not defined, the method returns error messages of all validators
1123
+ * @returns {String[]}
1124
+ */
1125
+ getMessages: function(field, validator) {
1126
+ var that = this,
1127
+ messages = [],
1128
+ $fields = $([]);
1129
+
1130
+ switch (true) {
1131
+ case (field && 'object' === typeof field):
1132
+ $fields = field;
1133
+ break;
1134
+ case (field && 'string' === typeof field):
1135
+ var f = this.getFieldElements(field);
1136
+ if (f.length > 0) {
1137
+ var type = f.attr('type');
1138
+ $fields = ('radio' === type || 'checkbox' === type) ? f.eq(0) : f;
1139
+ }
1140
+ break;
1141
+ default:
1142
+ $fields = this.$invalidFields;
1143
+ break;
1144
+ }
1145
+
1146
+ var filter = validator ? '[data-bv-validator="' + validator + '"]' : '';
1147
+ $fields.each(function() {
1148
+ messages = messages.concat(
1149
+ $(this)
1150
+ .data('bv.messages')
1151
+ .find('.help-block[data-bv-for="' + $(this).attr('data-bv-field') + '"][data-bv-result="' + that.STATUS_INVALID + '"]' + filter)
1152
+ .map(function() {
1153
+ var v = $(this).attr('data-bv-validator'),
1154
+ f = $(this).attr('data-bv-for');
1155
+ return (that.options.fields[f].validators[v].enabled === false) ? '' : $(this).html();
1156
+ })
1157
+ .get()
1158
+ );
1159
+ });
1160
+
1161
+ return messages;
1162
+ },
1163
+
1164
+ /**
1165
+ * Get the field options
1166
+ *
1167
+ * @param {String|jQuery} [field] The field name or field element. If it is not set, the method returns the form options
1168
+ * @param {String} [validator] The name of validator. It null, the method returns form options
1169
+ * @param {String} [option] The option name
1170
+ * @return {String|Object}
1171
+ */
1172
+ getOptions: function(field, validator, option) {
1173
+ if (!field) {
1174
+ return this.options;
1175
+ }
1176
+ if ('object' === typeof field) {
1177
+ field = field.attr('data-bv-field');
1178
+ }
1179
+ if (!this.options.fields[field]) {
1180
+ return null;
1181
+ }
1182
+
1183
+ var options = this.options.fields[field];
1184
+ if (!validator) {
1185
+ return options;
1186
+ }
1187
+ if (!options.validators || !options.validators[validator]) {
1188
+ return null;
1189
+ }
1190
+
1191
+ return option ? options.validators[validator][option] : options.validators[validator];
1192
+ },
1193
+
1194
+ /**
1195
+ * Update the option of a specific validator
1196
+ *
1197
+ * @param {String|jQuery} field The field name or field element
1198
+ * @param {String} validator The validator name
1199
+ * @param {String} option The option name
1200
+ * @param {String} value The value to set
1201
+ * @returns {BootstrapValidator}
1202
+ */
1203
+ updateOption: function(field, validator, option, value) {
1204
+ if ('object' === typeof field) {
1205
+ field = field.attr('data-bv-field');
1206
+ }
1207
+ if (this.options.fields[field] && this.options.fields[field].validators[validator]) {
1208
+ this.options.fields[field].validators[validator][option] = value;
1209
+ this.updateStatus(field, this.STATUS_NOT_VALIDATED, validator);
1210
+ }
1211
+
1212
+ return this;
1213
+ },
1214
+
1215
+ /**
1216
+ * Add a new field
1217
+ *
1218
+ * @param {String|jQuery} field The field name or field element
1219
+ * @param {Object} [options] The validator rules
1220
+ * @returns {BootstrapValidator}
1221
+ */
1222
+ addField: function(field, options) {
1223
+ var fields = $([]);
1224
+ switch (typeof field) {
1225
+ case 'object':
1226
+ fields = field;
1227
+ field = field.attr('data-bv-field') || field.attr('name');
1228
+ break;
1229
+ case 'string':
1230
+ delete this._cacheFields[field];
1231
+ fields = this.getFieldElements(field);
1232
+ break;
1233
+ default:
1234
+ break;
1235
+ }
1236
+
1237
+ fields.attr('data-bv-field', field);
1238
+
1239
+ var type = fields.attr('type'),
1240
+ total = ('radio' === type || 'checkbox' === type) ? 1 : fields.length;
1241
+
1242
+ for (var i = 0; i < total; i++) {
1243
+ var $field = fields.eq(i);
1244
+
1245
+ // Try to parse the options from HTML attributes
1246
+ var opts = this._parseOptions($field);
1247
+ opts = (opts === null) ? options : $.extend(true, options, opts);
1248
+
1249
+ this.options.fields[field] = $.extend(true, this.options.fields[field], opts);
1250
+
1251
+ // Update the cache
1252
+ this._cacheFields[field] = this._cacheFields[field] ? this._cacheFields[field].add($field) : $field;
1253
+
1254
+ // Init the element
1255
+ this._initField(('checkbox' === type || 'radio' === type) ? field : $field);
1256
+ }
1257
+
1258
+ this.disableSubmitButtons(false);
1259
+ // Trigger an event
1260
+ this.$form.trigger($.Event('added.field.bv'), {
1261
+ field: field,
1262
+ element: fields,
1263
+ options: this.options.fields[field]
1264
+ });
1265
+
1266
+ return this;
1267
+ },
1268
+
1269
+ /**
1270
+ * Remove a given field
1271
+ *
1272
+ * @param {String|jQuery} field The field name or field element
1273
+ * @returns {BootstrapValidator}
1274
+ */
1275
+ removeField: function(field) {
1276
+ var fields = $([]);
1277
+ switch (typeof field) {
1278
+ case 'object':
1279
+ fields = field;
1280
+ field = field.attr('data-bv-field') || field.attr('name');
1281
+ fields.attr('data-bv-field', field);
1282
+ break;
1283
+ case 'string':
1284
+ fields = this.getFieldElements(field);
1285
+ break;
1286
+ default:
1287
+ break;
1288
+ }
1289
+
1290
+ if (fields.length === 0) {
1291
+ return this;
1292
+ }
1293
+
1294
+ var type = fields.attr('type'),
1295
+ total = ('radio' === type || 'checkbox' === type) ? 1 : fields.length;
1296
+
1297
+ for (var i = 0; i < total; i++) {
1298
+ var $field = fields.eq(i);
1299
+
1300
+ // Remove from the list of invalid fields
1301
+ this.$invalidFields = this.$invalidFields.not($field);
1302
+
1303
+ // Update the cache
1304
+ this._cacheFields[field] = this._cacheFields[field].not($field);
1305
+ }
1306
+
1307
+ if (!this._cacheFields[field] || this._cacheFields[field].length === 0) {
1308
+ delete this.options.fields[field];
1309
+ }
1310
+ if ('checkbox' === type || 'radio' === type) {
1311
+ this._initField(field);
1312
+ }
1313
+
1314
+ this.disableSubmitButtons(false);
1315
+ // Trigger an event
1316
+ this.$form.trigger($.Event('removed.field.bv'), {
1317
+ field: field,
1318
+ element: fields
1319
+ });
1320
+
1321
+ return this;
1322
+ },
1323
+
1324
+ /**
1325
+ * Reset given field
1326
+ *
1327
+ * @param {String|jQuery} field The field name or field element
1328
+ * @param {Boolean} [resetValue] If true, the method resets field value to empty or remove checked/selected attribute (for radio/checkbox)
1329
+ * @returns {BootstrapValidator}
1330
+ */
1331
+ resetField: function(field, resetValue) {
1332
+ var $fields = $([]);
1333
+ switch (typeof field) {
1334
+ case 'object':
1335
+ $fields = field;
1336
+ field = field.attr('data-bv-field');
1337
+ break;
1338
+ case 'string':
1339
+ $fields = this.getFieldElements(field);
1340
+ break;
1341
+ default:
1342
+ break;
1343
+ }
1344
+
1345
+ var total = $fields.length;
1346
+ if (this.options.fields[field]) {
1347
+ for (var i = 0; i < total; i++) {
1348
+ for (var validator in this.options.fields[field].validators) {
1349
+ $fields.eq(i).removeData('bv.dfs.' + validator);
1350
+ }
1351
+ }
1352
+ }
1353
+
1354
+ // Mark field as not validated yet
1355
+ this.updateStatus(field, this.STATUS_NOT_VALIDATED);
1356
+
1357
+ if (resetValue) {
1358
+ var type = $fields.attr('type');
1359
+ ('radio' === type || 'checkbox' === type) ? $fields.removeAttr('checked').removeAttr('selected') : $fields.val('');
1360
+ }
1361
+
1362
+ return this;
1363
+ },
1364
+
1365
+ /**
1366
+ * Reset the form
1367
+ *
1368
+ * @param {Boolean} [resetValue] If true, the method resets field value to empty or remove checked/selected attribute (for radio/checkbox)
1369
+ * @returns {BootstrapValidator}
1370
+ */
1371
+ resetForm: function(resetValue) {
1372
+ for (var field in this.options.fields) {
1373
+ this.resetField(field, resetValue);
1374
+ }
1375
+
1376
+ this.$invalidFields = $([]);
1377
+ this.$submitButton = null;
1378
+
1379
+ // Enable submit buttons
1380
+ this.disableSubmitButtons(false);
1381
+
1382
+ return this;
1383
+ },
1384
+
1385
+ /**
1386
+ * Revalidate given field
1387
+ * It's used when you need to revalidate the field which its value is updated by other plugin
1388
+ *
1389
+ * @param {String|jQuery} field The field name of field element
1390
+ * @returns {BootstrapValidator}
1391
+ */
1392
+ revalidateField: function(field) {
1393
+ this.updateStatus(field, this.STATUS_NOT_VALIDATED)
1394
+ .validateField(field);
1395
+
1396
+ return this;
1397
+ },
1398
+
1399
+ /**
1400
+ * Enable/Disable all validators to given field
1401
+ *
1402
+ * @param {String} field The field name
1403
+ * @param {Boolean} enabled Enable/Disable field validators
1404
+ * @param {String} [validatorName] The validator name. If null, all validators will be enabled/disabled
1405
+ * @returns {BootstrapValidator}
1406
+ */
1407
+ enableFieldValidators: function(field, enabled, validatorName) {
1408
+ var validators = this.options.fields[field].validators;
1409
+
1410
+ // Enable/disable particular validator
1411
+ if (validatorName
1412
+ && validators
1413
+ && validators[validatorName] && validators[validatorName].enabled !== enabled)
1414
+ {
1415
+ this.options.fields[field].validators[validatorName].enabled = enabled;
1416
+ this.updateStatus(field, this.STATUS_NOT_VALIDATED, validatorName);
1417
+ }
1418
+ // Enable/disable all validators
1419
+ else if (!validatorName && this.options.fields[field].enabled !== enabled) {
1420
+ this.options.fields[field].enabled = enabled;
1421
+ for (var v in validators) {
1422
+ this.enableFieldValidators(field, enabled, v);
1423
+ }
1424
+ }
1425
+
1426
+ return this;
1427
+ },
1428
+
1429
+ /**
1430
+ * Some validators have option which its value is dynamic.
1431
+ * For example, the zipCode validator has the country option which might be changed dynamically by a select element.
1432
+ *
1433
+ * @param {jQuery|String} field The field name or element
1434
+ * @param {String|Function} option The option which can be determined by:
1435
+ * - a string
1436
+ * - name of field which defines the value
1437
+ * - name of function which returns the value
1438
+ * - a function returns the value
1439
+ *
1440
+ * The callback function has the format of
1441
+ * callback: function(value, validator, $field) {
1442
+ * // value is the value of field
1443
+ * // validator is the BootstrapValidator instance
1444
+ * // $field is the field element
1445
+ * }
1446
+ *
1447
+ * @returns {String}
1448
+ */
1449
+ getDynamicOption: function(field, option) {
1450
+ var $field = ('string' === typeof field) ? this.getFieldElements(field) : field,
1451
+ value = $field.val();
1452
+
1453
+ // Option can be determined by
1454
+ // ... a function
1455
+ if ('function' === typeof option) {
1456
+ return $.fn.bootstrapValidator.helpers.call(option, [value, this, $field]);
1457
+ }
1458
+ // ... value of other field
1459
+ else if ('string' === typeof option) {
1460
+ var $f = this.getFieldElements(option);
1461
+ if ($f.length) {
1462
+ return $f.val();
1463
+ }
1464
+ // ... return value of callback
1465
+ else {
1466
+ return $.fn.bootstrapValidator.helpers.call(option, [value, this, $field]);
1467
+ }
1468
+ }
1469
+
1470
+ return null;
1471
+ },
1472
+
1473
+ /**
1474
+ * Destroy the plugin
1475
+ * It will remove all error messages, feedback icons and turn off the events
1476
+ */
1477
+ destroy: function() {
1478
+ var field, fields, $field, validator, $icon, container, group;
1479
+ for (field in this.options.fields) {
1480
+ fields = this.getFieldElements(field);
1481
+ container = this.options.fields[field].container || this.options.container,
1482
+ group = this.options.fields[field].group || this.options.group;
1483
+ for (var i = 0; i < fields.length; i++) {
1484
+ $field = fields.eq(i);
1485
+ $field
1486
+ // Remove all error messages
1487
+ .data('bv.messages')
1488
+ .find('.help-block[data-bv-validator][data-bv-for="' + field + '"]').remove().end()
1489
+ .end()
1490
+ .removeData('bv.messages')
1491
+ // Remove feedback classes
1492
+ .parents(group)
1493
+ .removeClass('has-feedback has-error has-success')
1494
+ .end()
1495
+ // Turn off events
1496
+ .off('.bv')
1497
+ .removeAttr('data-bv-field');
1498
+
1499
+ // Remove feedback icons, tooltip/popover container
1500
+ $icon = $field.parents(group).find('i[data-bv-icon-for="' + field + '"]');
1501
+ if ($icon) {
1502
+ switch (container) {
1503
+ case 'tooltip':
1504
+ $icon.tooltip('destroy').remove();
1505
+ break;
1506
+ case 'popover':
1507
+ $icon.popover('destroy').remove();
1508
+ break;
1509
+ default:
1510
+ $icon.remove();
1511
+ break;
1512
+ }
1513
+ }
1514
+
1515
+ for (validator in this.options.fields[field].validators) {
1516
+ if ($field.data('bv.dfs.' + validator)) {
1517
+ $field.data('bv.dfs.' + validator).reject();
1518
+ }
1519
+ $field.removeData('bv.result.' + validator).removeData('bv.dfs.' + validator);
1520
+ }
1521
+ }
1522
+ }
1523
+
1524
+ // Enable submit buttons
1525
+ this.disableSubmitButtons(false);
1526
+
1527
+ this.$form
1528
+ .removeClass(this.options.elementClass)
1529
+ .off('.bv')
1530
+ .removeData('bootstrapValidator')
1531
+ // Remove generated hidden elements
1532
+ .find('[data-bv-submit-hidden]').remove();
1533
+ }
1534
+ };
1535
+
1536
+ // Plugin definition
1537
+ $.fn.bootstrapValidator = function(option) {
1538
+ var params = arguments;
1539
+ return this.each(function() {
1540
+ var $this = $(this),
1541
+ data = $this.data('bootstrapValidator'),
1542
+ options = 'object' === typeof option && option;
1543
+ if (!data) {
1544
+ data = new BootstrapValidator(this, options);
1545
+ $this.data('bootstrapValidator', data);
1546
+ }
1547
+
1548
+ // Allow to call plugin method
1549
+ if ('string' === typeof option) {
1550
+ data[option].apply(data, Array.prototype.slice.call(params, 1));
1551
+ }
1552
+ });
1553
+ };
1554
+
1555
+ // The default options
1556
+ $.fn.bootstrapValidator.DEFAULT_OPTIONS = {
1557
+ // The form CSS class
1558
+ elementClass: 'bv-form',
1559
+
1560
+ // Default invalid message
1561
+ message: 'This value is not valid',
1562
+
1563
+ // The CSS selector for indicating the element consists the field
1564
+ // By default, each field is placed inside the <div class="form-group"></div>
1565
+ // You should adjust this option if your form group consists of many fields which not all of them need to be validated
1566
+ group: '.form-group',
1567
+
1568
+ //The error messages container. It can be:
1569
+ // - 'tooltip' if you want to use Bootstrap tooltip to show error messages
1570
+ // - 'popover' if you want to use Bootstrap popover to show error messages
1571
+ // - a CSS selector indicating the container
1572
+ // In the first two cases, since the tooltip/popover should be small enough, the plugin only shows only one error message
1573
+ // You also can define the message container for particular field
1574
+ container: null,
1575
+
1576
+ // The field will not be live validated if its length is less than this number of characters
1577
+ threshold: null,
1578
+
1579
+ // Indicate fields which won't be validated
1580
+ // By default, the plugin will not validate the following kind of fields:
1581
+ // - disabled
1582
+ // - hidden
1583
+ // - invisible
1584
+ //
1585
+ // The setting consists of jQuery filters. Accept 3 formats:
1586
+ // - A string. Use a comma to separate filter
1587
+ // - An array. Each element is a filter
1588
+ // - An array. Each element can be a callback function
1589
+ // function($field, validator) {
1590
+ // $field is jQuery object representing the field element
1591
+ // validator is the BootstrapValidator instance
1592
+ // return true or false;
1593
+ // }
1594
+ //
1595
+ // The 3 following settings are equivalent:
1596
+ //
1597
+ // 1) ':disabled, :hidden, :not(:visible)'
1598
+ // 2) [':disabled', ':hidden', ':not(:visible)']
1599
+ // 3) [':disabled', ':hidden', function($field) {
1600
+ // return !$field.is(':visible');
1601
+ // }]
1602
+ excluded: [':disabled', ':hidden', ':not(:visible)'],
1603
+
1604
+ // Shows ok/error/loading icons based on the field validity.
1605
+ // This feature requires Bootstrap v3.1.0 or later (http://getbootstrap.com/css/#forms-control-validation).
1606
+ // Since Bootstrap doesn't provide any methods to know its version, this option cannot be on/off automatically.
1607
+ // In other word, to use this feature you have to upgrade your Bootstrap to v3.1.0 or later.
1608
+ //
1609
+ // Examples:
1610
+ // - Use Glyphicons icons:
1611
+ // feedbackIcons: {
1612
+ // valid: 'glyphicon glyphicon-ok',
1613
+ // invalid: 'glyphicon glyphicon-remove',
1614
+ // validating: 'glyphicon glyphicon-refresh'
1615
+ // }
1616
+ // - Use FontAwesome icons:
1617
+ // feedbackIcons: {
1618
+ // valid: 'fa fa-check',
1619
+ // invalid: 'fa fa-times',
1620
+ // validating: 'fa fa-refresh'
1621
+ // }
1622
+ feedbackIcons: {
1623
+ valid: null,
1624
+ invalid: null,
1625
+ validating: null
1626
+ },
1627
+
1628
+ // The submit buttons selector
1629
+ // These buttons will be disabled to prevent the valid form from multiple submissions
1630
+ submitButtons: '[type="submit"]',
1631
+
1632
+ // Live validating option
1633
+ // Can be one of 3 values:
1634
+ // - enabled: The plugin validates fields as soon as they are changed
1635
+ // - disabled: Disable the live validating. The error messages are only shown after the form is submitted
1636
+ // - submitted: The live validating is enabled after the form is submitted
1637
+ live: 'enabled',
1638
+
1639
+ // Map the field name with validator rules
1640
+ fields: null
1641
+ };
1642
+
1643
+ // Available validators
1644
+ $.fn.bootstrapValidator.validators = {};
1645
+
1646
+ // i18n
1647
+ $.fn.bootstrapValidator.i18n = {};
1648
+
1649
+ $.fn.bootstrapValidator.Constructor = BootstrapValidator;
1650
+
1651
+ // Helper methods, which can be used in validator class
1652
+ $.fn.bootstrapValidator.helpers = {
1653
+ /**
1654
+ * Execute a callback function
1655
+ *
1656
+ * @param {String|Function} functionName Can be
1657
+ * - name of global function
1658
+ * - name of namespace function (such as A.B.C)
1659
+ * - a function
1660
+ * @param {Array} args The callback arguments
1661
+ */
1662
+ call: function(functionName, args) {
1663
+ if ('function' === typeof functionName) {
1664
+ return functionName.apply(this, args);
1665
+ } else if ('string' === typeof functionName) {
1666
+ if ('()' === functionName.substring(functionName.length - 2)) {
1667
+ functionName = functionName.substring(0, functionName.length - 2);
1668
+ }
1669
+ var ns = functionName.split('.'),
1670
+ func = ns.pop(),
1671
+ context = window;
1672
+ for (var i = 0; i < ns.length; i++) {
1673
+ context = context[ns[i]];
1674
+ }
1675
+ return context[func].apply(this, args);
1676
+ }
1677
+ },
1678
+
1679
+ /**
1680
+ * Format a string
1681
+ * It's used to format the error message
1682
+ * format('The field must between %s and %s', [10, 20]) = 'The field must between 10 and 20'
1683
+ *
1684
+ * @param {String} message
1685
+ * @param {Array} parameters
1686
+ * @returns {String}
1687
+ */
1688
+ format: function(message, parameters) {
1689
+ if (!$.isArray(parameters)) {
1690
+ parameters = [parameters];
1691
+ }
1692
+
1693
+ for (var i in parameters) {
1694
+ message = message.replace('%s', parameters[i]);
1695
+ }
1696
+
1697
+ return message;
1698
+ },
1699
+
1700
+ /**
1701
+ * Validate a date
1702
+ *
1703
+ * @param {Number} year The full year in 4 digits
1704
+ * @param {Number} month The month number
1705
+ * @param {Number} day The day number
1706
+ * @param {Boolean} [notInFuture] If true, the date must not be in the future
1707
+ * @returns {Boolean}
1708
+ */
1709
+ date: function(year, month, day, notInFuture) {
1710
+ if (isNaN(year) || isNaN(month) || isNaN(day)) {
1711
+ return false;
1712
+ }
1713
+
1714
+ day = parseInt(day, 10);
1715
+ month = parseInt(month, 10);
1716
+ year = parseInt(year, 10);
1717
+
1718
+ if (year < 1000 || year > 9999 || month <= 0 || month > 12) {
1719
+ return false;
1720
+ }
1721
+ var numDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
1722
+ // Update the number of days in Feb of leap year
1723
+ if (year % 400 === 0 || (year % 100 !== 0 && year % 4 === 0)) {
1724
+ numDays[1] = 29;
1725
+ }
1726
+
1727
+ // Check the day
1728
+ if (day <= 0 || day > numDays[month - 1]) {
1729
+ return false;
1730
+ }
1731
+
1732
+ if (notInFuture === true) {
1733
+ var currentDate = new Date(),
1734
+ currentYear = currentDate.getFullYear(),
1735
+ currentMonth = currentDate.getMonth(),
1736
+ currentDay = currentDate.getDate();
1737
+ return (year < currentYear
1738
+ || (year === currentYear && month - 1 < currentMonth)
1739
+ || (year === currentYear && month - 1 === currentMonth && day < currentDay));
1740
+ }
1741
+
1742
+ return true;
1743
+ },
1744
+
1745
+ /**
1746
+ * Implement Luhn validation algorithm
1747
+ * Credit to https://gist.github.com/ShirtlessKirk/2134376
1748
+ *
1749
+ * @see http://en.wikipedia.org/wiki/Luhn
1750
+ * @param {String} value
1751
+ * @returns {Boolean}
1752
+ */
1753
+ luhn: function(value) {
1754
+ var length = value.length,
1755
+ mul = 0,
1756
+ prodArr = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]],
1757
+ sum = 0;
1758
+
1759
+ while (length--) {
1760
+ sum += prodArr[mul][parseInt(value.charAt(length), 10)];
1761
+ mul ^= 1;
1762
+ }
1763
+
1764
+ return (sum % 10 === 0 && sum > 0);
1765
+ },
1766
+
1767
+ /**
1768
+ * Implement modulus 11, 10 (ISO 7064) algorithm
1769
+ *
1770
+ * @param {String} value
1771
+ * @returns {Boolean}
1772
+ */
1773
+ mod11And10: function(value) {
1774
+ var check = 5,
1775
+ length = value.length;
1776
+ for (var i = 0; i < length; i++) {
1777
+ check = (((check || 10) * 2) % 11 + parseInt(value.charAt(i), 10)) % 10;
1778
+ }
1779
+ return (check === 1);
1780
+ },
1781
+
1782
+ /**
1783
+ * Implements Mod 37, 36 (ISO 7064) algorithm
1784
+ * Usages:
1785
+ * mod37And36('A12425GABC1234002M')
1786
+ * mod37And36('002006673085', '0123456789')
1787
+ *
1788
+ * @param {String} value
1789
+ * @param {String} [alphabet]
1790
+ * @returns {Boolean}
1791
+ */
1792
+ mod37And36: function(value, alphabet) {
1793
+ alphabet = alphabet || '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
1794
+ var modulus = alphabet.length,
1795
+ length = value.length,
1796
+ check = Math.floor(modulus / 2);
1797
+ for (var i = 0; i < length; i++) {
1798
+ check = (((check || modulus) * 2) % (modulus + 1) + alphabet.indexOf(value.charAt(i))) % modulus;
1799
+ }
1800
+ return (check === 1);
1801
+ }
1802
+ };
1803
+ }(window.jQuery));