validator.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  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]):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. var editedSelector = ':input[name^="edit_"]:not([type="submit"], button,[data-no-validate]):enabled:visible';
  121. var selectorToBeUsed = (typeof MGVONLYEDITED !== 'undefined' && MGVONLYEDITED === true) ? editedSelector : inputSelector;
  122. this.options.delay = 0
  123. this.$element.find(selectorToBeUsed).trigger('input.bs.validator')
  124. this.options.delay = delay
  125. return this
  126. }
  127. Validator.prototype.showErrors = function ($el) {
  128. var method = this.options.html ? 'html' : 'text'
  129. this.defer($el, function () {
  130. var $group = $el.closest('.form-group')
  131. var $block = $group.find('.help-block.with-errors')
  132. var $feedback = $group.find('.form-control-feedback')
  133. var errors = $el.data('bs.validator.errors')
  134. if (!errors.length) return
  135. errors = $('<ul/>')
  136. .addClass('list-unstyled')
  137. .append($.map(errors, function (error) { return $('<li/>')[method](error) }))
  138. $block.data('bs.validator.originalContent') === undefined && $block.data('bs.validator.originalContent', $block.html())
  139. $block.empty().append(errors)
  140. $group.addClass('has-error')
  141. $feedback.length
  142. && $feedback.removeClass(this.options.feedback.success)
  143. && $feedback.addClass(this.options.feedback.error)
  144. && $group.removeClass('has-success')
  145. })
  146. }
  147. Validator.prototype.clearErrors = function ($el) {
  148. var $group = $el.closest('.form-group')
  149. var $block = $group.find('.help-block.with-errors')
  150. var $feedback = $group.find('.form-control-feedback')
  151. $block.html($block.data('bs.validator.originalContent'))
  152. $group.removeClass('has-error')
  153. $feedback.length
  154. && $feedback.removeClass(this.options.feedback.error)
  155. && $feedback.addClass(this.options.feedback.success)
  156. && $group.addClass('has-success')
  157. }
  158. Validator.prototype.hasErrors = function () {
  159. function fieldErrors() {
  160. return !!($(this).data('bs.validator.errors') || []).length
  161. }
  162. return !!this.$element.find(inputSelector).filter(fieldErrors).length
  163. }
  164. Validator.prototype.isIncomplete = function () {
  165. function fieldIncomplete() {
  166. return this.type === 'checkbox' ? !this.checked :
  167. this.type === 'radio' ? !$('[name="' + this.name + '"]:checked').length :
  168. $.trim(this.value) === ''
  169. }
  170. return !!this.$element.find(inputSelector).filter('[required]').filter(fieldIncomplete).length
  171. }
  172. Validator.prototype.onSubmit = function (e) {
  173. this.validate()
  174. if (this.isIncomplete() || this.hasErrors()) e.preventDefault()
  175. }
  176. Validator.prototype.toggleSubmit = function () {
  177. if(!this.options.disable) return
  178. var $btn = $('button[type="submit"], input[type="submit"]')
  179. .filter('[form="' + this.$element.attr('id') + '"]')
  180. .add(this.$element.find('input[type="submit"], button[type="submit"]'))
  181. $btn.toggleClass('disabled', this.isIncomplete() || this.hasErrors())
  182. .css({'pointer-events': 'all', 'cursor': 'pointer'})
  183. }
  184. Validator.prototype.defer = function ($el, callback) {
  185. callback = $.proxy(callback, this)
  186. if (!this.options.delay) return callback()
  187. window.clearTimeout($el.data('bs.validator.timeout'))
  188. $el.data('bs.validator.timeout', window.setTimeout(callback, this.options.delay))
  189. }
  190. Validator.prototype.destroy = function () {
  191. this.$element
  192. .removeAttr('novalidate')
  193. .removeData('bs.validator')
  194. .off('.bs.validator')
  195. this.$element.find(inputSelector)
  196. .off('.bs.validator')
  197. .removeData(['bs.validator.errors', 'bs.validator.deferred'])
  198. .each(function () {
  199. var $this = $(this)
  200. var timeout = $this.data('bs.validator.timeout')
  201. window.clearTimeout(timeout) && $this.removeData('bs.validator.timeout')
  202. })
  203. this.$element.find('.help-block.with-errors').each(function () {
  204. var $this = $(this)
  205. var originalContent = $this.data('bs.validator.originalContent')
  206. $this
  207. .removeData('bs.validator.originalContent')
  208. .html(originalContent)
  209. })
  210. this.$element.find('input[type="submit"], button[type="submit"]').removeClass('disabled')
  211. this.$element.find('.has-error').removeClass('has-error')
  212. return this
  213. }
  214. // VALIDATOR PLUGIN DEFINITION
  215. // ===========================
  216. function Plugin(option) {
  217. return this.each(function () {
  218. var $this = $(this)
  219. var options = $.extend({}, Validator.DEFAULTS, $this.data(), typeof option == 'object' && option)
  220. var data = $this.data('bs.validator')
  221. if (!data && option == 'destroy') return
  222. if (!data) $this.data('bs.validator', (data = new Validator(this, options)))
  223. if (typeof option == 'string') data[option]()
  224. })
  225. }
  226. var old = $.fn.validator
  227. $.fn.validator = Plugin
  228. $.fn.validator.Constructor = Validator
  229. // VALIDATOR NO CONFLICT
  230. // =====================
  231. $.fn.validator.noConflict = function () {
  232. $.fn.validator = old
  233. return this
  234. }
  235. // VALIDATOR DATA-API
  236. // ==================
  237. $(window).on('load', function () {
  238. $('form[data-toggle="validator"]').each(function () {
  239. var $form = $(this)
  240. Plugin.call($form, $form.data())
  241. })
  242. })
  243. }(jQuery);