bootstrapValidator.js 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
  1. /**
  2. * BootstrapValidator (https://github.com/nghuuphuoc/bootstrapvalidator)
  3. *
  4. * A jQuery plugin to validate form fields. Use with Bootstrap 3
  5. *
  6. * @author http://twitter.com/nghuuphuoc
  7. * @copyright (c) 2013 - 2014 Nguyen Huu Phuoc
  8. * @license MIT
  9. */
  10. (function($) {
  11. var BootstrapValidator = function(form, options) {
  12. this.$form = $(form);
  13. this.options = $.extend({}, BootstrapValidator.DEFAULT_OPTIONS, options);
  14. this.$invalidField = null; // First invalid field
  15. this.$submitButton = null; // The submit button which is clicked to submit form
  16. // Validating status
  17. this.STATUS_NOT_VALIDATED = 'NOT_VALIDATED';
  18. this.STATUS_VALIDATING = 'VALIDATING';
  19. this.STATUS_INVALID = 'INVALID';
  20. this.STATUS_VALID = 'VALID';
  21. // Determine the event that is fired when user change the field value
  22. // Most modern browsers supports input event except IE 7, 8.
  23. // IE 9 supports input event but the event is still not fired if I press the backspace key.
  24. // In that case I will use the keydown event
  25. var el = document.createElement('div');
  26. this._changeEvent = ('oninput' in el) ? 'input' : 'keydown';
  27. // The flag to indicate that the form is ready to submit when a remote/callback validator returns
  28. this._submitIfValid = null;
  29. this._init();
  30. };
  31. // The default options
  32. BootstrapValidator.DEFAULT_OPTIONS = {
  33. // The form CSS class
  34. elementClass: 'bootstrap-validator-form',
  35. // Default invalid message
  36. message: 'This value is not valid',
  37. // Indicate fields which won't be validated
  38. // By default, the plugin will not validate the following kind of fields:
  39. // - disabled
  40. // - hidden
  41. // - invisible
  42. //
  43. // The setting consists of jQuery filters. Accept 3 formats:
  44. // - A string. Use a comma to separate filter
  45. // - An array. Each element is a filter
  46. // - An array. Each element can be a callback function
  47. // function($field, validator) {
  48. // $field is jQuery object representing the field element
  49. // validator is the BootstrapValidator instance
  50. // return true or false;
  51. // }
  52. //
  53. // The 3 following settings are equivalent:
  54. //
  55. // 1) ':disabled, :hidden, :not(:visible)'
  56. // 2) [':disabled', ':hidden', ':not(:visible)']
  57. // 3) [':disabled', ':hidden', function($field) {
  58. // return !$field.is(':visible');
  59. // }]
  60. excluded: [':disabled', ':hidden', ':not(:visible)'],
  61. // Shows ok/error/loading icons based on the field validity.
  62. // This feature requires Bootstrap v3.1.0 or later (http://getbootstrap.com/css/#forms-control-validation).
  63. // Since Bootstrap doesn't provide any methods to know its version, this option cannot be on/off automatically.
  64. // In other word, to use this feature you have to upgrade your Bootstrap to v3.1.0 or later.
  65. //
  66. // Examples:
  67. // - Use Glyphicons icons:
  68. // feedbackIcons: {
  69. // valid: 'glyphicon glyphicon-ok',
  70. // invalid: 'glyphicon glyphicon-remove',
  71. // validating: 'glyphicon glyphicon-refresh'
  72. // }
  73. // - Use FontAwesome icons:
  74. // feedbackIcons: {
  75. // valid: 'fa fa-check',
  76. // invalid: 'fa fa-times',
  77. // validating: 'fa fa-refresh'
  78. // }
  79. feedbackIcons: {
  80. valid: null,
  81. invalid: null,
  82. validating: null
  83. },
  84. // The submit buttons selector
  85. // These buttons will be disabled to prevent the valid form from multiple submissions
  86. submitButtons: 'button[type="submit"]',
  87. // The custom submit handler
  88. // It will prevent the form from the default submission
  89. //
  90. // submitHandler: function(validator, form) {
  91. // - validator is the BootstrapValidator instance
  92. // - form is the jQuery object present the current form
  93. // }
  94. submitHandler: null,
  95. // Live validating option
  96. // Can be one of 3 values:
  97. // - enabled: The plugin validates fields as soon as they are changed
  98. // - disabled: Disable the live validating. The error messages are only shown after the form is submitted
  99. // - submitted: The live validating is enabled after the form is submitted
  100. live: 'enabled',
  101. // Map the field name with validator rules
  102. fields: null
  103. };
  104. BootstrapValidator.prototype = {
  105. constructor: BootstrapValidator,
  106. /**
  107. * Init form
  108. */
  109. _init: function() {
  110. var that = this,
  111. options = {
  112. excluded: this.$form.attr('data-bv-excluded'),
  113. trigger: this.$form.attr('data-bv-trigger'),
  114. message: this.$form.attr('data-bv-message'),
  115. submitButtons: this.$form.attr('data-bv-submitbuttons'),
  116. live: this.$form.attr('data-bv-live'),
  117. fields: {},
  118. feedbackIcons: {
  119. valid: this.$form.attr('data-bv-feedbackicons-valid'),
  120. invalid: this.$form.attr('data-bv-feedbackicons-invalid'),
  121. validating: this.$form.attr('data-bv-feedbackicons-validating')
  122. }
  123. },
  124. validator,
  125. v, // Validator name
  126. enabled,
  127. optionName,
  128. optionValue,
  129. html5AttrName,
  130. html5Attrs;
  131. this.$form
  132. // Disable client side validation in HTML 5
  133. .attr('novalidate', 'novalidate')
  134. .addClass(this.options.elementClass)
  135. // Disable the default submission first
  136. .on('submit.bv', function(e) {
  137. e.preventDefault();
  138. that.validate();
  139. })
  140. .on('click', this.options.submitButtons, function() {
  141. that.$submitButton = $(this);
  142. // The user just click the submit button
  143. that._submitIfValid = true;
  144. })
  145. // Find all fields which have either "name" or "data-bv-field" attribute
  146. .find('[name], [data-bv-field]').each(function() {
  147. var $field = $(this);
  148. // Don't initialize hidden input
  149. if ('hidden' == $field.attr('type')) {
  150. return;
  151. }
  152. var field = $field.attr('name') || $field.attr('data-bv-field');
  153. $field.attr('data-bv-field', field);
  154. options.fields[field] = $.extend({}, {
  155. trigger: $field.attr('data-bv-trigger'),
  156. message: $field.attr('data-bv-message'),
  157. container: $field.attr('data-bv-container'),
  158. selector: $field.attr('data-bv-selector'),
  159. validators: {}
  160. }, options.fields[field]);
  161. for (v in $.fn.bootstrapValidator.validators) {
  162. validator = $.fn.bootstrapValidator.validators[v];
  163. enabled = $field.attr('data-bv-' + v.toLowerCase()) + '';
  164. html5Attrs = ('function' == typeof validator.enableByHtml5) ? validator.enableByHtml5($(this)) : null;
  165. if ((html5Attrs && enabled != 'false')
  166. || (html5Attrs !== true && ('' == enabled || 'true' == enabled)))
  167. {
  168. // Try to parse the options via attributes
  169. validator.html5Attributes = validator.html5Attributes || { message: 'message' };
  170. options.fields[field]['validators'][v] = $.extend({}, html5Attrs == true ? {} : html5Attrs, options.fields[field]['validators'][v]);
  171. for (html5AttrName in validator.html5Attributes) {
  172. optionName = validator.html5Attributes[html5AttrName];
  173. optionValue = $field.attr('data-bv-' + v.toLowerCase() + '-' + html5AttrName);
  174. if (optionValue) {
  175. if ('true' == optionValue) {
  176. optionValue = true;
  177. } else if ('false' == optionValue) {
  178. optionValue = false;
  179. }
  180. options.fields[field]['validators'][v][optionName] = optionValue;
  181. }
  182. }
  183. }
  184. }
  185. });
  186. this.options = $.extend(true, this.options, options);
  187. if ('string' == typeof this.options.excluded) {
  188. this.options.excluded = $.map(this.options.excluded.split(','), function(item) {
  189. // Trim the spaces
  190. return item.trim();
  191. });
  192. }
  193. for (var field in this.options.fields) {
  194. this._initField(field);
  195. }
  196. this.setLiveMode(this.options.live);
  197. },
  198. /**
  199. * Init field
  200. *
  201. * @param {String} field The field name
  202. */
  203. _initField: function(field) {
  204. if (this.options.fields[field] == null || this.options.fields[field].validators == null) {
  205. return;
  206. }
  207. var fields = this.getFieldElements(field);
  208. // We don't need to validate non-existing fields
  209. if (fields == null) {
  210. delete this.options.fields[field];
  211. return;
  212. }
  213. for (var validatorName in this.options.fields[field].validators) {
  214. if (!$.fn.bootstrapValidator.validators[validatorName]) {
  215. delete this.options.fields[field].validators[validatorName];
  216. }
  217. }
  218. var that = this,
  219. type = fields.attr('type'),
  220. event = ('radio' == type || 'checkbox' == type || 'file' == type || 'SELECT' == fields[0].tagName) ? 'change' : that._changeEvent,
  221. total = fields.length,
  222. updateAll = (total == 1) || ('radio' == type) || ('checkbox' == type);
  223. for (var i = 0; i < total; i++) {
  224. var $field = $(fields[i]),
  225. $parent = $field.parents('.form-group'),
  226. // Allow user to indicate where the error messages are shown
  227. $message = this.options.fields[field].container ? $parent.find(this.options.fields[field].container) : this._getMessageContainer($field);
  228. // Set the attribute to indicate the fields which are defined by selector
  229. if (!$field.attr('data-bv-field')) {
  230. $field.attr('data-bv-field', field);
  231. }
  232. // Whenever the user change the field value, mark it as not validated yet
  233. $field.on(event + '.update.bv', function() {
  234. // Reset the flag
  235. that._submitIfValid = false;
  236. updateAll ? that.updateStatus(field, that.STATUS_NOT_VALIDATED, null)
  237. : that.updateElementStatus($(this), that.STATUS_NOT_VALIDATED, null);
  238. });
  239. // Create help block elements for showing the error messages
  240. $field.data('bv.messages', $message);
  241. for (validatorName in this.options.fields[field].validators) {
  242. $field.data('bv.result.' + validatorName, this.STATUS_NOT_VALIDATED);
  243. if (!updateAll || i == total - 1) {
  244. $('<small/>')
  245. .css('display', 'none')
  246. .attr('data-bv-validator', validatorName)
  247. .html(this.options.fields[field].validators[validatorName].message || this.options.fields[field].message || this.options.message)
  248. .addClass('help-block')
  249. .appendTo($message);
  250. }
  251. }
  252. // Prepare the feedback icons
  253. // Available from Bootstrap 3.1 (http://getbootstrap.com/css/#forms-control-validation)
  254. if (this.options.feedbackIcons
  255. && this.options.feedbackIcons.validating && this.options.feedbackIcons.invalid && this.options.feedbackIcons.valid
  256. && (!updateAll || i == total - 1))
  257. {
  258. $parent.addClass('has-feedback');
  259. var $icon = $('<i/>').css('display', 'none').addClass('form-control-feedback').attr('data-bv-field', field).insertAfter($field);
  260. // The feedback icon does not render correctly if there is no label
  261. // https://github.com/twbs/bootstrap/issues/12873
  262. if ($parent.find('label').length == 0) {
  263. $icon.css('top', 0);
  264. }
  265. }
  266. }
  267. if (this.options.fields[field]['enabled'] == null) {
  268. this.options.fields[field]['enabled'] = true;
  269. }
  270. },
  271. /**
  272. * Get the element to place the error messages
  273. *
  274. * @param {jQuery} $field The field element
  275. * @returns {jQuery}
  276. */
  277. _getMessageContainer: function($field) {
  278. var $parent = $field.parent();
  279. if ($parent.hasClass('form-group')) {
  280. return $parent;
  281. }
  282. var cssClasses = $parent.attr('class');
  283. if (!cssClasses) {
  284. return this._getMessageContainer($parent);
  285. }
  286. cssClasses = cssClasses.split(' ');
  287. var n = cssClasses.length;
  288. for (var i = 0; i < n; i++) {
  289. if (/^col-(xs|sm|md|lg)-\d+$/.test(cssClasses[i]) || /^col-(xs|sm|md|lg)-offset-\d+$/.test(cssClasses[i])) {
  290. return $parent;
  291. }
  292. }
  293. return this._getMessageContainer($parent);
  294. },
  295. /**
  296. * Called when all validations are completed
  297. */
  298. _submit: function() {
  299. if (!this.isValid()) {
  300. if ('submitted' == this.options.live) {
  301. this.setLiveMode('enabled');
  302. }
  303. // Focus to the first invalid field
  304. if (this.$invalidField) {
  305. this.$invalidField.focus();
  306. // Activate the tab containing the invalid field if exists
  307. var $tab = this.$invalidField.parents('.tab-pane'),
  308. tabId;
  309. if ($tab && (tabId = $tab.attr('id'))) {
  310. $('a[href="#' + tabId + '"][data-toggle="tab"]').trigger('click.bs.tab.data-api');
  311. }
  312. }
  313. return;
  314. }
  315. // Call the custom submission if enabled
  316. if (this.options.submitHandler && 'function' == typeof this.options.submitHandler) {
  317. // If you want to submit the form inside your submit handler, please call defaultSubmit() method
  318. this.options.submitHandler.call(this, this, this.$form, this.$submitButton);
  319. } else {
  320. this.disableSubmitButtons(true).defaultSubmit();
  321. }
  322. },
  323. /**
  324. * Check if the field is excluded.
  325. * Returning true means that the field will not be validated
  326. *
  327. * @param {jQuery} $field The field element
  328. * @return {Boolean}
  329. */
  330. _isExcluded: function($field) {
  331. if (this.options.excluded) {
  332. for (var i in this.options.excluded) {
  333. if (('string' == typeof this.options.excluded[i] && $field.is(this.options.excluded[i]))
  334. || ('function' == typeof this.options.excluded[i] && this.options.excluded[i].call(this, $field, this) == true))
  335. {
  336. return true;
  337. }
  338. }
  339. }
  340. return false;
  341. },
  342. // --- Public methods ---
  343. /**
  344. * Retrieve the field elements by given name
  345. *
  346. * @param {String} field The field name
  347. * @returns {null|jQuery[]}
  348. */
  349. getFieldElements: function(field) {
  350. var fields = this.options.fields[field].selector ? $(this.options.fields[field].selector) : this.$form.find('[name="' + field + '"]');
  351. return (fields.length == 0) ? null : fields;
  352. },
  353. /**
  354. * Set live validating mode
  355. *
  356. * @param {String} mode Live validating mode. Can be 'enabled', 'disabled', 'submitted'
  357. * @returns {BootstrapValidator}
  358. */
  359. setLiveMode: function(mode) {
  360. this.options.live = mode;
  361. if ('submitted' == mode) {
  362. return this;
  363. }
  364. var that = this;
  365. for (var field in this.options.fields) {
  366. (function(f) {
  367. var fields = that.getFieldElements(f);
  368. if (fields) {
  369. var type = fields.attr('type'),
  370. total = fields.length,
  371. updateAll = (total == 1) || ('radio' == type) || ('checkbox' == type),
  372. trigger = that.options.fields[field].trigger
  373. || that.options.trigger
  374. || (('radio' == type || 'checkbox' == type || 'file' == type || 'SELECT' == fields[0].tagName) ? 'change' : that._changeEvent),
  375. events = $.map(trigger.split(' '), function(item) {
  376. return item + '.live.bv';
  377. }).join(' ');
  378. for (var i = 0; i < total; i++) {
  379. ('enabled' == mode)
  380. ? $(fields[i]).on(events, function() {
  381. updateAll ? that.validateField(f) : that.validateFieldElement($(this), false);
  382. })
  383. : $(fields[i]).off(events);
  384. }
  385. }
  386. })(field);
  387. }
  388. return this;
  389. },
  390. /**
  391. * Disable/enable submit buttons
  392. *
  393. * @param {Boolean} disabled Can be true or false
  394. * @returns {BootstrapValidator}
  395. */
  396. disableSubmitButtons: function(disabled) {
  397. if (!disabled) {
  398. this.$form.find(this.options.submitButtons).removeAttr('disabled');
  399. } else if (this.options.live != 'disabled') {
  400. // Don't disable if the live validating mode is disabled
  401. this.$form.find(this.options.submitButtons).attr('disabled', 'disabled');
  402. }
  403. return this;
  404. },
  405. /**
  406. * Validate the form
  407. *
  408. * @return {BootstrapValidator}
  409. */
  410. validate: function() {
  411. if (!this.options.fields) {
  412. return this;
  413. }
  414. this.disableSubmitButtons(true);
  415. for (var field in this.options.fields) {
  416. this.validateField(field);
  417. }
  418. // Check if whether the submit button is clicked
  419. if (this.$submitButton) {
  420. this._submit();
  421. }
  422. return this;
  423. },
  424. /**
  425. * Validate given field
  426. *
  427. * @param {String} field The field name
  428. * @returns {BootstrapValidator}
  429. */
  430. validateField: function(field) {
  431. var fields = this.getFieldElements(field),
  432. type = fields.attr('type'),
  433. n = (('radio' == type) || ('checkbox' == type)) ? 1 : fields.length;
  434. for (var i = 0; i < n; i++) {
  435. this.validateFieldElement($(fields[i]), (n == 1));
  436. }
  437. return this;
  438. },
  439. /**
  440. * Validate field element
  441. *
  442. * @param {jQuery} $field The field element
  443. * @param {Boolean} updateAll If true, update status of all elements which have the same name
  444. * @returns {BootstrapValidator}
  445. */
  446. validateFieldElement: function($field, updateAll) {
  447. var that = this,
  448. field = $field.attr('data-bv-field'),
  449. validators = this.options.fields[field].validators,
  450. validatorName,
  451. validateResult;
  452. if (!this.options.fields[field]['enabled'] || this._isExcluded($field)) {
  453. return this;
  454. }
  455. for (validatorName in validators) {
  456. if ($field.data('bv.dfs.' + validatorName)) {
  457. $field.data('bv.dfs.' + validatorName).reject();
  458. }
  459. // Don't validate field if it is already done
  460. var result = $field.data('bv.result.' + validatorName);
  461. if (result == this.STATUS_VALID || result == this.STATUS_INVALID) {
  462. continue;
  463. }
  464. $field.data('bv.result.' + validatorName, this.STATUS_VALIDATING);
  465. validateResult = $.fn.bootstrapValidator.validators[validatorName].validate(this, $field, validators[validatorName]);
  466. if ('object' == typeof validateResult) {
  467. updateAll ? this.updateStatus(field, this.STATUS_VALIDATING, validatorName)
  468. : this.updateElementStatus($field, this.STATUS_VALIDATING, validatorName);
  469. $field.data('bv.dfs.' + validatorName, validateResult);
  470. validateResult.done(function($f, v, isValid) {
  471. // v is validator name
  472. $f.removeData('bv.dfs.' + v);
  473. updateAll ? that.updateStatus($f.attr('data-bv-field'), isValid ? that.STATUS_VALID : that.STATUS_INVALID, v)
  474. : that.updateElementStatus($f, isValid ? that.STATUS_VALID : that.STATUS_INVALID, v);
  475. if (isValid && that._submitIfValid == true) {
  476. // If a remote validator returns true and the form is ready to submit, then do it
  477. that._submit();
  478. }
  479. });
  480. } else if ('boolean' == typeof validateResult) {
  481. updateAll ? this.updateStatus(field, validateResult ? this.STATUS_VALID : this.STATUS_INVALID, validatorName)
  482. : this.updateElementStatus($field, validateResult ? this.STATUS_VALID : this.STATUS_INVALID, validatorName);
  483. }
  484. }
  485. return this;
  486. },
  487. /**
  488. * Update all validating results of elements which have the same field name
  489. *
  490. * @param {String} field The field name
  491. * @param {String} status The status. Can be 'NOT_VALIDATED', 'VALIDATING', 'INVALID' or 'VALID'
  492. * @param {String} [validatorName] The validator name. If null, the method updates validity result for all validators
  493. * @return {BootstrapValidator}
  494. */
  495. updateStatus: function(field, status, validatorName) {
  496. var fields = this.getFieldElements(field),
  497. type = fields.attr('type'),
  498. n = (('radio' == type) || ('checkbox' == type)) ? 1 : fields.length;
  499. for (var i = 0; i < n; i++) {
  500. this.updateElementStatus($(fields[i]), status, validatorName);
  501. }
  502. return this;
  503. },
  504. /**
  505. * Update validating result of given element
  506. *
  507. * @param {jQuery} $field The field element
  508. * @param {String} status The status. Can be 'NOT_VALIDATED', 'VALIDATING', 'INVALID' or 'VALID'
  509. * @param {String} [validatorName] The validator name. If null, the method updates validity result for all validators
  510. * @return {BootstrapValidator}
  511. */
  512. updateElementStatus: function($field, status, validatorName) {
  513. var that = this,
  514. field = $field.attr('data-bv-field'),
  515. $parent = $field.parents('.form-group'),
  516. $message = $field.data('bv.messages'),
  517. $rowErrors = $parent.find('.help-block[data-bv-validator]'),
  518. $errors = $message.find('.help-block[data-bv-validator]'),
  519. $icon = $parent.find('.form-control-feedback[data-bv-field="' + field + '"]');
  520. // Update status
  521. if (validatorName) {
  522. $field.data('bv.result.' + validatorName, status);
  523. } else {
  524. for (var v in this.options.fields[field].validators) {
  525. $field.data('bv.result.' + v, status);
  526. }
  527. }
  528. // Show/hide error elements and feedback icons
  529. switch (status) {
  530. case this.STATUS_VALIDATING:
  531. this.disableSubmitButtons(true);
  532. $parent.removeClass('has-success').removeClass('has-error');
  533. // TODO: Show validating message
  534. validatorName ? $errors.filter('.help-block[data-bv-validator="' + validatorName + '"]').hide() : $errors.hide();
  535. if ($icon) {
  536. $icon.removeClass(this.options.feedbackIcons.valid).removeClass(this.options.feedbackIcons.invalid).addClass(this.options.feedbackIcons.validating).show();
  537. }
  538. break;
  539. case this.STATUS_INVALID:
  540. this.disableSubmitButtons(true);
  541. $parent.removeClass('has-success').addClass('has-error');
  542. validatorName ? $errors.filter('[data-bv-validator="' + validatorName + '"]').show() : $errors.show();
  543. if ($icon) {
  544. $icon.removeClass(this.options.feedbackIcons.valid).removeClass(this.options.feedbackIcons.validating).addClass(this.options.feedbackIcons.invalid).show();
  545. }
  546. break;
  547. case this.STATUS_VALID:
  548. validatorName ? $errors.filter('[data-bv-validator="' + validatorName + '"]').hide() : $errors.hide();
  549. // If the field is valid (passes all validators)
  550. var validField = ($errors.filter(function() {
  551. var display = $(this).css('display'), v = $(this).attr('data-bv-validator');
  552. return ('block' == display) || ($field.data('bv.result.' + v) != that.STATUS_VALID);
  553. }).length == 0);
  554. this.disableSubmitButtons(validField ? false : true);
  555. if ($icon) {
  556. $icon
  557. .removeClass(this.options.feedbackIcons.invalid).removeClass(this.options.feedbackIcons.validating).removeClass(this.options.feedbackIcons.valid)
  558. .addClass(validField ? this.options.feedbackIcons.valid : this.options.feedbackIcons.invalid)
  559. .show();
  560. }
  561. // Check if all fields in the same row are valid
  562. var validRow = ($rowErrors.filter(function() {
  563. var display = $(this).css('display'), v = $(this).attr('data-bv-validator');
  564. return ('block' == display) || ($field.data('bv.result.' + v) != that.STATUS_VALID);
  565. }).length == 0);
  566. $parent.removeClass('has-error has-success').addClass(validRow ? 'has-success' : 'has-error');
  567. break;
  568. case this.STATUS_NOT_VALIDATED:
  569. default:
  570. this.disableSubmitButtons(false);
  571. $parent.removeClass('has-success').removeClass('has-error');
  572. validatorName ? $errors.filter('.help-block[data-bv-validator="' + validatorName + '"]').hide() : $errors.hide();
  573. if ($icon) {
  574. $icon.removeClass(this.options.feedbackIcons.valid).removeClass(this.options.feedbackIcons.invalid).removeClass(this.options.feedbackIcons.validating).hide();
  575. }
  576. break;
  577. }
  578. return this;
  579. },
  580. /**
  581. * Check the form validity
  582. *
  583. * @returns {Boolean}
  584. */
  585. isValid: function() {
  586. var fields, field, $field,
  587. type, status, validatorName,
  588. n, i;
  589. for (field in this.options.fields) {
  590. if (this.options.fields[field] == null || !this.options.fields[field]['enabled']) {
  591. continue;
  592. }
  593. fields = this.getFieldElements(field);
  594. type = fields.attr('type');
  595. n = (('radio' == type) || ('checkbox' == type)) ? 1 : fields.length;
  596. for (i = 0; i < n; i++) {
  597. $field = $(fields[i]);
  598. if (this._isExcluded($field)) {
  599. continue;
  600. }
  601. for (validatorName in this.options.fields[field].validators) {
  602. status = $field.data('bv.result.' + validatorName);
  603. if (status == this.STATUS_NOT_VALIDATED || status == this.STATUS_VALIDATING) {
  604. return false;
  605. }
  606. if (status == this.STATUS_INVALID) {
  607. this.$invalidField = $field;
  608. return false;
  609. }
  610. }
  611. }
  612. }
  613. return true;
  614. },
  615. /**
  616. * Submit the form using default submission.
  617. * It also does not perform any validations when submitting the form
  618. *
  619. * It might be used when you want to submit the form right inside the submitHandler()
  620. */
  621. defaultSubmit: function() {
  622. this.$form.off('submit.bv').submit();
  623. },
  624. // Useful APIs which aren't used internally
  625. /**
  626. * Reset the form
  627. *
  628. * @param {Boolean} resetFormData Reset current form data
  629. * @return {BootstrapValidator}
  630. */
  631. resetForm: function(resetFormData) {
  632. var field, fields, total, type, validator;
  633. for (field in this.options.fields) {
  634. fields = this.getFieldElements(field);
  635. total = fields.length;
  636. for (var i = 0; i < total; i++) {
  637. for (validator in this.options.fields[field].validators) {
  638. $(fields[i]).removeData('bv.dfs.' + validator);
  639. }
  640. }
  641. // Mark field as not validated yet
  642. this.updateStatus(field, this.STATUS_NOT_VALIDATED, null);
  643. if (resetFormData) {
  644. type = fields.attr('type');
  645. ('radio' == type || 'checkbox' == type) ? fields.removeAttr('checked').removeAttr('selected') : fields.val('');
  646. }
  647. }
  648. this.$invalidField = null;
  649. this.$submitButton = null;
  650. // Enable submit buttons
  651. this.disableSubmitButtons(false);
  652. return this;
  653. },
  654. /**
  655. * Enable/Disable all validators to given field
  656. *
  657. * @param {String} field The field name
  658. * @param {Boolean} enabled Enable/Disable field validators
  659. * @return {BootstrapValidator}
  660. */
  661. enableFieldValidators: function(field, enabled) {
  662. this.options.fields[field]['enabled'] = enabled;
  663. this.updateStatus(field, this.STATUS_NOT_VALIDATED, null);
  664. return this;
  665. }
  666. };
  667. // Plugin definition
  668. $.fn.bootstrapValidator = function(option, params) {
  669. return this.each(function() {
  670. var $this = $(this),
  671. data = $this.data('bootstrapValidator'),
  672. options = 'object' == typeof option && option;
  673. if (!data) {
  674. data = new BootstrapValidator(this, options);
  675. $this.data('bootstrapValidator', data);
  676. }
  677. // Allow to call plugin method
  678. if ('string' == typeof option) {
  679. data[option](params);
  680. }
  681. });
  682. };
  683. // Available validators
  684. $.fn.bootstrapValidator.validators = {};
  685. $.fn.bootstrapValidator.Constructor = BootstrapValidator;
  686. // Helper methods, which can be used in validator class
  687. $.fn.bootstrapValidator.helpers = {
  688. /**
  689. * Implement Luhn validation algorithm ((http://en.wikipedia.org/wiki/Luhn))
  690. * Credit to https://gist.github.com/ShirtlessKirk/2134376
  691. *
  692. * @param {String} value
  693. * @returns {boolean}
  694. */
  695. luhn: function(value) {
  696. var length = value.length,
  697. mul = 0,
  698. prodArr = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]],
  699. sum = 0;
  700. while (length--) {
  701. sum += prodArr[mul][parseInt(value.charAt(length), 10)];
  702. mul ^= 1;
  703. }
  704. return (sum % 10 === 0 && sum > 0);
  705. }
  706. };
  707. }(window.jQuery));