bootstrap-validator.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. /*!
  2. * Validator v0.9.0 for Bootstrap 3, by @1000hz
  3. * Copyright 2015 Cina Saffary
  4. * Licensed under http://opensource.org/licenses/MIT
  5. *
  6. * https://github.com/1000hz/bootstrap-validator
  7. */
  8. +function ($) {
  9. 'use strict';
  10. // VALIDATOR CLASS DEFINITION
  11. // ==========================
  12. var Validator = function (element, options) {
  13. this.$element = $(element)
  14. this.options = options
  15. options.errors = $.extend({}, Validator.DEFAULTS.errors, options.errors)
  16. for (var custom in options.custom) {
  17. if (!options.errors[custom]) throw new Error('Missing default error message for custom validator: ' + custom)
  18. }
  19. $.extend(Validator.VALIDATORS, options.custom)
  20. this.$element.attr('novalidate', true) // disable automatic native validation
  21. this.toggleSubmit()
  22. this.$element.on('input.bs.validator change.bs.validator focusout.bs.validator', $.proxy(this.validateInput, this))
  23. this.$element.on('submit.bs.validator', $.proxy(this.onSubmit, this))
  24. this.$element.find('[data-match]').each(function () {
  25. var $this = $(this)
  26. var target = $this.data('match')
  27. $(target).on('input.bs.validator', function (e) {
  28. $this.val() && $this.trigger('input.bs.validator')
  29. })
  30. })
  31. }
  32. Validator.INPUT_SELECTOR = ':input:not([type="submit"], button):enabled:visible'
  33. Validator.DEFAULTS = {
  34. delay: 500,
  35. html: false,
  36. disable: true,
  37. custom: {},
  38. errors: {
  39. match: 'Does not match',
  40. minlength: 'Not long enough'
  41. },
  42. feedback: {
  43. success: 'glyphicon-ok',
  44. error: 'glyphicon-remove'
  45. }
  46. }
  47. Validator.VALIDATORS = {
  48. 'native': function ($el) {
  49. var el = $el[0]
  50. return el.checkValidity ? el.checkValidity() : true
  51. },
  52. 'match': function ($el) {
  53. var target = $el.data('match')
  54. return !$el.val() || $el.val() === $(target).val()
  55. },
  56. 'minlength': function ($el) {
  57. var minlength = $el.data('minlength')
  58. return !$el.val() || $el.val().length >= minlength
  59. }
  60. }
  61. Validator.prototype.validateInput = function (e) {
  62. var $el = $(e.target)
  63. var prevErrors = $el.data('bs.validator.errors')
  64. var errors
  65. if ($el.is('[type="radio"]')) $el = this.$element.find('input[name="' + $el.attr('name') + '"]')
  66. this.$element.trigger(e = $.Event('validate.bs.validator', {relatedTarget: $el[0]}))
  67. if (e.isDefaultPrevented()) return
  68. var self = this
  69. this.runValidators($el).done(function (errors) {
  70. $el.data('bs.validator.errors', errors)
  71. errors.length ? self.showErrors($el) : self.clearErrors($el)
  72. if (!prevErrors || errors.toString() !== prevErrors.toString()) {
  73. e = errors.length
  74. ? $.Event('invalid.bs.validator', {relatedTarget: $el[0], detail: errors})
  75. : $.Event('valid.bs.validator', {relatedTarget: $el[0], detail: prevErrors})
  76. self.$element.trigger(e)
  77. }
  78. self.toggleSubmit()
  79. self.$element.trigger($.Event('validated.bs.validator', {relatedTarget: $el[0]}))
  80. })
  81. }
  82. Validator.prototype.runValidators = function ($el) {
  83. var errors = []
  84. var deferred = $.Deferred()
  85. var options = this.options
  86. $el.data('bs.validator.deferred') && $el.data('bs.validator.deferred').reject()
  87. $el.data('bs.validator.deferred', deferred)
  88. function getErrorMessage(key) {
  89. return $el.data(key + '-error')
  90. || $el.data('error')
  91. || key == 'native' && $el[0].validationMessage
  92. || options.errors[key]
  93. }
  94. $.each(Validator.VALIDATORS, $.proxy(function (key, validator) {
  95. if (($el.data(key) || key == 'native') && !validator.call(this, $el)) {
  96. var error = getErrorMessage(key)
  97. !~errors.indexOf(error) && errors.push(error)
  98. }
  99. }, this))
  100. if (!errors.length && $el.val() && $el.data('remote')) {
  101. this.defer($el, function () {
  102. var data = {}
  103. data[$el.attr('name')] = $el.val()
  104. $.get($el.data('remote'), data)
  105. .fail(function (jqXHR, textStatus, error) { errors.push(getErrorMessage('remote') || error) })
  106. .always(function () { deferred.resolve(errors)})
  107. })
  108. } else deferred.resolve(errors)
  109. return deferred.promise()
  110. }
  111. Validator.prototype.validate = function () {
  112. var delay = this.options.delay
  113. this.options.delay = 0
  114. this.$element.find(Validator.INPUT_SELECTOR).trigger('input.bs.validator')
  115. this.options.delay = delay
  116. return this
  117. }
  118. Validator.prototype.showErrors = function ($el) {
  119. var method = this.options.html ? 'html' : 'text'
  120. this.defer($el, function () {
  121. var $group = $el.closest('.form-group')
  122. var $block = $group.find('.help-block.with-errors')
  123. var $feedback = $group.find('.form-control-feedback')
  124. var errors = $el.data('bs.validator.errors')
  125. if (!errors.length) return
  126. errors = $('<ul/>')
  127. .addClass('list-unstyled')
  128. .append($.map(errors, function (error) { return $('<li/>')[method](error) }))
  129. $block.data('bs.validator.originalContent') === undefined && $block.data('bs.validator.originalContent', $block.html())
  130. $block.empty().append(errors)
  131. $group.addClass('has-error')
  132. $feedback.length
  133. && $feedback.removeClass(this.options.feedback.success)
  134. && $feedback.addClass(this.options.feedback.error)
  135. && $group.removeClass('has-success')
  136. })
  137. }
  138. Validator.prototype.clearErrors = function ($el) {
  139. var $group = $el.closest('.form-group')
  140. var $block = $group.find('.help-block.with-errors')
  141. var $feedback = $group.find('.form-control-feedback')
  142. $block.html($block.data('bs.validator.originalContent'))
  143. $group.removeClass('has-error')
  144. $feedback.length
  145. && $feedback.removeClass(this.options.feedback.error)
  146. && $feedback.addClass(this.options.feedback.success)
  147. && $group.addClass('has-success')
  148. }
  149. Validator.prototype.hasErrors = function () {
  150. function fieldErrors() {
  151. return !!($(this).data('bs.validator.errors') || []).length
  152. }
  153. return !!this.$element.find(Validator.INPUT_SELECTOR).filter(fieldErrors).length
  154. }
  155. Validator.prototype.isIncomplete = function () {
  156. function fieldIncomplete() {
  157. return this.type === 'checkbox' ? !this.checked :
  158. this.type === 'radio' ? !$('[name="' + this.name + '"]:checked').length :
  159. $.trim(this.value) === ''
  160. }
  161. return !!this.$element.find(Validator.INPUT_SELECTOR).filter('[required]').filter(fieldIncomplete).length
  162. }
  163. Validator.prototype.onSubmit = function (e) {
  164. this.validate()
  165. if (this.isIncomplete() || this.hasErrors()) e.preventDefault()
  166. }
  167. Validator.prototype.toggleSubmit = function () {
  168. if(!this.options.disable) return
  169. var $btn = $('button[type="submit"], input[type="submit"]')
  170. .filter('[form="' + this.$element.attr('id') + '"]')
  171. .add(this.$element.find('input[type="submit"], button[type="submit"]'))
  172. $btn.toggleClass('disabled', this.isIncomplete() || this.hasErrors())
  173. }
  174. Validator.prototype.defer = function ($el, callback) {
  175. callback = $.proxy(callback, this)
  176. if (!this.options.delay) return callback()
  177. window.clearTimeout($el.data('bs.validator.timeout'))
  178. $el.data('bs.validator.timeout', window.setTimeout(callback, this.options.delay))
  179. }
  180. Validator.prototype.destroy = function () {
  181. this.$element
  182. .removeAttr('novalidate')
  183. .removeData('bs.validator')
  184. .off('.bs.validator')
  185. this.$element.find(Validator.INPUT_SELECTOR)
  186. .off('.bs.validator')
  187. .removeData(['bs.validator.errors', 'bs.validator.deferred'])
  188. .each(function () {
  189. var $this = $(this)
  190. var timeout = $this.data('bs.validator.timeout')
  191. window.clearTimeout(timeout) && $this.removeData('bs.validator.timeout')
  192. })
  193. this.$element.find('.help-block.with-errors').each(function () {
  194. var $this = $(this)
  195. var originalContent = $this.data('bs.validator.originalContent')
  196. $this
  197. .removeData('bs.validator.originalContent')
  198. .html(originalContent)
  199. })
  200. this.$element.find('input[type="submit"], button[type="submit"]').removeClass('disabled')
  201. this.$element.find('.has-error').removeClass('has-error')
  202. return this
  203. }
  204. // VALIDATOR PLUGIN DEFINITION
  205. // ===========================
  206. function Plugin(option) {
  207. return this.each(function () {
  208. var $this = $(this)
  209. var options = $.extend({}, Validator.DEFAULTS, $this.data(), typeof option == 'object' && option)
  210. var data = $this.data('bs.validator')
  211. if (!data && option == 'destroy') return
  212. if (!data) $this.data('bs.validator', (data = new Validator(this, options)))
  213. if (typeof option == 'string') data[option]()
  214. })
  215. }
  216. var old = $.fn.validator
  217. $.fn.validator = Plugin
  218. $.fn.validator.Constructor = Validator
  219. // VALIDATOR NO CONFLICT
  220. // =====================
  221. $.fn.validator.noConflict = function () {
  222. $.fn.validator = old
  223. return this
  224. }
  225. // VALIDATOR DATA-API
  226. // ==================
  227. $(window).on('load', function () {
  228. $('form[data-toggle="validator"]').each(function () {
  229. var $form = $(this)
  230. Plugin.call($form, $form.data())
  231. })
  232. })
  233. }(jQuery);