validator.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. /*!
  2. * Validator v0.8.1 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. var inputSelector = ':input:not([type="submit"], button,[data-no-validate], :checkbox):enabled:visible'
  11. // VALIDATOR CLASS DEFINITION
  12. // ==========================
  13. var Validator = function (element, options) {
  14. this.$element = $(element)
  15. this.options = options
  16. options.errors = $.extend({}, Validator.DEFAULTS.errors, options.errors)
  17. for (var custom in options.custom) {
  18. if (!options.errors[custom]) throw new Error('Missing default error message for custom validator: ' + custom)
  19. }
  20. $.extend(Validator.VALIDATORS, options.custom)
  21. this.$element.attr('novalidate', true) // disable automatic native validation
  22. this.toggleSubmit()
  23. this.$element.on('input.bs.validator change.bs.validator focusout.bs.validator', $.proxy(this.validateInput, this))
  24. this.$element.on('submit.bs.validator', $.proxy(this.onSubmit, this))
  25. this.$element.find('[data-match]').each(function () {
  26. var $this = $(this)
  27. var target = $this.data('match')
  28. $(target).on('input.bs.validator', function (e) {
  29. $this.val() && $this.trigger('input.bs.validator')
  30. })
  31. })
  32. }
  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-warning-sign'
  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.data('no-validate') !== undefined) { //$el.data('no-validate') !== undefined ||
  66. // return ;
  67. // }
  68. if ($el.is('[type="radio"]')) $el = this.$element.find('input[name="' + $el.attr('name') + '"]')
  69. this.$element.trigger(e = $.Event('validate.bs.validator', {relatedTarget: $el[0]}))
  70. if (e.isDefaultPrevented()) return
  71. var self = this
  72. this.runValidators($el).done(function (errors) {
  73. $el.data('bs.validator.errors', errors)
  74. errors.length ? self.showErrors($el) : self.clearErrors($el)
  75. if (!prevErrors || errors.toString() !== prevErrors.toString()) {
  76. e = errors.length
  77. ? $.Event('invalid.bs.validator', {relatedTarget: $el[0], detail: errors})
  78. : $.Event('valid.bs.validator', {relatedTarget: $el[0], detail: prevErrors})
  79. self.$element.trigger(e)
  80. }
  81. self.toggleSubmit()
  82. self.$element.trigger($.Event('validated.bs.validator', {relatedTarget: $el[0]}))
  83. })
  84. }
  85. Validator.prototype.runValidators = function ($el) {
  86. var errors = []
  87. var deferred = $.Deferred()
  88. var options = this.options
  89. if(!$el.is(inputSelector)) { //$el.data('no-validate') !== undefined ||
  90. deferred.resolve(errors);
  91. return deferred.promise();
  92. }
  93. $el.data('bs.validator.deferred') && $el.data('bs.validator.deferred').reject()
  94. $el.data('bs.validator.deferred', deferred)
  95. function getErrorMessage(key) {
  96. return $el.data(key + '-error')
  97. || $el.data('error')
  98. || key == 'native' && $el[0].validationMessage
  99. || options.errors[key]
  100. }
  101. $.each(Validator.VALIDATORS, $.proxy(function (key, validator) {
  102. if (($el.data(key) || key == 'native') && !validator.call(this, $el)) {
  103. var error = getErrorMessage(key)
  104. !~errors.indexOf(error) && errors.push(error)
  105. }
  106. }, this))
  107. if (!errors.length && $el.val() && $el.data('remote')) {
  108. this.defer($el, function () {
  109. var data = {}
  110. data[$el.attr('name')] = $el.val()
  111. $.get($el.data('remote'), data)
  112. .fail(function (jqXHR, textStatus, error) { errors.push(getErrorMessage('remote') || error) })
  113. .always(function () { deferred.resolve(errors)})
  114. })
  115. } else deferred.resolve(errors)
  116. return deferred.promise()
  117. }
  118. Validator.prototype.validate = function () {
  119. var delay = this.options.delay
  120. this.options.delay = 0
  121. var editedSelector = 'input[name^="add_"],input[name^="edit_"]:not([type="submit"], button,[data-no-validate]):enabled:visible'
  122. var selectorToBeUsed = (typeof MGVONLYEDITED !== 'undefined' && MGVONLYEDITED === true) ? editedSelector : inputSelector
  123. this.$element.find(selectorToBeUsed).trigger('input.bs.validator')
  124. this.$element.find(inputSelector).trigger('input.bs.validator')
  125. this.options.delay = delay
  126. return this
  127. }
  128. Validator.prototype.showErrors = function ($el) {
  129. var method = this.options.html ? 'html' : 'text'
  130. this.defer($el, function () {
  131. var $group = $el.closest('.form-group')
  132. var $block = $group.find('.help-block.with-errors')
  133. var $feedback = $group.find('.form-control-feedback')
  134. var errors = $el.data('bs.validator.errors')
  135. if (!errors.length) return
  136. errors = $('<ul/>')
  137. .addClass('list-unstyled')
  138. .append($.map(errors, function (error) { return $('<li/>')[method](error) }))
  139. $block.data('bs.validator.originalContent') === undefined && $block.data('bs.validator.originalContent', $block.html())
  140. $block.empty().append(errors)
  141. $group.addClass('has-error')
  142. $feedback.length
  143. && $feedback.removeClass(this.options.feedback.success)
  144. && $feedback.addClass(this.options.feedback.error)
  145. && $group.removeClass('has-success')
  146. })
  147. }
  148. Validator.prototype.clearErrors = function ($el) {
  149. var $group = $el.closest('.form-group')
  150. var $block = $group.find('.help-block.with-errors')
  151. var $feedback = $group.find('.form-control-feedback')
  152. $block.html($block.data('bs.validator.originalContent'))
  153. $group.removeClass('has-error')
  154. $feedback.length
  155. && $feedback.removeClass(this.options.feedback.error)
  156. && $feedback.addClass(this.options.feedback.success)
  157. && $group.addClass('has-success')
  158. }
  159. Validator.prototype.hasErrors = function () {
  160. function fieldErrors() {
  161. return !!($(this).data('bs.validator.errors') || []).length
  162. }
  163. return !!this.$element.find(inputSelector).filter(fieldErrors).length
  164. }
  165. Validator.prototype.isIncomplete = function () {
  166. function fieldIncomplete() {
  167. return this.type === 'checkbox' ? !this.checked :
  168. this.type === 'radio' ? !$('[name="' + this.name + '"]:checked').length :
  169. $.trim(this.value) === ''
  170. }
  171. return !!this.$element.find(inputSelector).filter('[required]').filter(fieldIncomplete).length
  172. }
  173. Validator.prototype.onSubmit = function (e) {
  174. this.validate()
  175. if (this.isIncomplete() || this.hasErrors()) e.preventDefault()
  176. }
  177. Validator.prototype.toggleSubmit = function () {
  178. if(!this.options.disable) return
  179. var $btn = $('button[type="submit"], input[type="submit"]')
  180. .filter('[form="' + this.$element.attr('id') + '"]')
  181. .add(this.$element.find('input[type="submit"], button[type="submit"]'))
  182. $btn.toggleClass('disabled', this.isIncomplete() || this.hasErrors())
  183. .css({'pointer-events': 'all', 'cursor': 'pointer'})
  184. }
  185. Validator.prototype.defer = function ($el, callback) {
  186. callback = $.proxy(callback, this)
  187. if (!this.options.delay) return callback()
  188. window.clearTimeout($el.data('bs.validator.timeout'))
  189. $el.data('bs.validator.timeout', window.setTimeout(callback, this.options.delay))
  190. }
  191. Validator.prototype.destroy = function () {
  192. this.$element
  193. .removeAttr('novalidate')
  194. .removeData('bs.validator')
  195. .off('.bs.validator')
  196. this.$element.find(inputSelector)
  197. .off('.bs.validator')
  198. .removeData(['bs.validator.errors', 'bs.validator.deferred'])
  199. .each(function () {
  200. var $this = $(this)
  201. var timeout = $this.data('bs.validator.timeout')
  202. window.clearTimeout(timeout) && $this.removeData('bs.validator.timeout')
  203. })
  204. this.$element.find('.help-block.with-errors').each(function () {
  205. var $this = $(this)
  206. var originalContent = $this.data('bs.validator.originalContent')
  207. $this
  208. .removeData('bs.validator.originalContent')
  209. .html(originalContent)
  210. })
  211. this.$element.find('input[type="submit"], button[type="submit"]').removeClass('disabled')
  212. this.$element.find('.has-error').removeClass('has-error')
  213. return this
  214. }
  215. // VALIDATOR PLUGIN DEFINITION
  216. // ===========================
  217. function Plugin(option) {
  218. return this.each(function () {
  219. var $this = $(this)
  220. var options = $.extend({}, Validator.DEFAULTS, $this.data(), typeof option == 'object' && option)
  221. var data = $this.data('bs.validator')
  222. if (!data && option == 'destroy') return
  223. if (!data) $this.data('bs.validator', (data = new Validator(this, options)))
  224. if (typeof option == 'string') data[option]()
  225. })
  226. }
  227. var old = $.fn.validator
  228. $.fn.validator = Plugin
  229. $.fn.validator.Constructor = Validator
  230. // VALIDATOR NO CONFLICT
  231. // =====================
  232. $.fn.validator.noConflict = function () {
  233. $.fn.validator = old
  234. return this
  235. }
  236. // VALIDATOR DATA-API
  237. // ==================
  238. $(window).on('load', function () {
  239. $('form[data-toggle="validator"]').each(function () {
  240. var $form = $(this)
  241. Plugin.call($form, $form.data())
  242. })
  243. })
  244. }(jQuery);