modal.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. /* ========================================================================
  2. * Bootstrap: modal.js v3.3.2
  3. * http://getbootstrap.com/javascript/#modals
  4. * ========================================================================
  5. * Copyright 2011-2015 Twitter, Inc.
  6. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
  7. * ======================================================================== */
  8. +function ($) {
  9. 'use strict';
  10. // MODAL CLASS DEFINITION
  11. // ======================
  12. var Modal = function (element, options) {
  13. this.options = options
  14. this.$body = $(document.body)
  15. this.$element = $(element)
  16. this.$backdrop =
  17. this.isShown = null
  18. this.scrollbarWidth = 0
  19. if (this.options.remote) {
  20. this.$element
  21. .find('.modal-content')
  22. .load(this.options.remote, $.proxy(function () {
  23. this.$element.trigger('loaded.bs.modal')
  24. }, this))
  25. }
  26. }
  27. Modal.VERSION = '3.3.2'
  28. Modal.TRANSITION_DURATION = 300
  29. Modal.BACKDROP_TRANSITION_DURATION = 150
  30. Modal.DEFAULTS = {
  31. backdrop: true,
  32. keyboard: true,
  33. show: true
  34. }
  35. Modal.prototype.toggle = function (_relatedTarget) {
  36. return this.isShown ? this.hide() : this.show(_relatedTarget)
  37. }
  38. Modal.prototype.show = function (_relatedTarget) {
  39. var that = this
  40. var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
  41. this.$element.trigger(e)
  42. if (this.isShown || e.isDefaultPrevented()) return
  43. this.isShown = true
  44. this.checkScrollbar()
  45. this.setScrollbar()
  46. this.$body.addClass('modal-open')
  47. this.escape()
  48. this.resize()
  49. this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
  50. this.backdrop(function () {
  51. var transition = $.support.transition && that.$element.hasClass('fade')
  52. if (!that.$element.parent().length) {
  53. that.$element.appendTo(that.$body) // don't move modals dom position
  54. }
  55. that.$element
  56. .show()
  57. .scrollTop(0)
  58. if (that.options.backdrop) that.adjustBackdrop()
  59. that.adjustDialog()
  60. if (transition) {
  61. that.$element[0].offsetWidth // force reflow
  62. }
  63. that.$element
  64. .addClass('in')
  65. .attr('aria-hidden', false)
  66. that.enforceFocus()
  67. var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
  68. transition ?
  69. that.$element.find('.modal-dialog') // wait for modal to slide in
  70. .one('bsTransitionEnd', function () {
  71. that.$element.trigger('focus').trigger(e)
  72. })
  73. .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
  74. that.$element.trigger('focus').trigger(e)
  75. })
  76. }
  77. Modal.prototype.hide = function (e) {
  78. if (e) e.preventDefault()
  79. e = $.Event('hide.bs.modal')
  80. this.$element.trigger(e)
  81. if (!this.isShown || e.isDefaultPrevented()) return
  82. this.isShown = false
  83. this.escape()
  84. this.resize()
  85. $(document).off('focusin.bs.modal')
  86. this.$element
  87. .removeClass('in')
  88. .attr('aria-hidden', true)
  89. .off('click.dismiss.bs.modal')
  90. $.support.transition && this.$element.hasClass('fade') ?
  91. this.$element
  92. .one('bsTransitionEnd', $.proxy(this.hideModal, this))
  93. .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
  94. this.hideModal()
  95. }
  96. Modal.prototype.enforceFocus = function () {
  97. $(document)
  98. .off('focusin.bs.modal') // guard against infinite focus loop
  99. .on('focusin.bs.modal', $.proxy(function (e) {
  100. if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
  101. this.$element.trigger('focus')
  102. }
  103. }, this))
  104. }
  105. Modal.prototype.escape = function () {
  106. if (this.isShown && this.options.keyboard) {
  107. this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
  108. e.which == 27 && this.hide()
  109. }, this))
  110. } else if (!this.isShown) {
  111. this.$element.off('keydown.dismiss.bs.modal')
  112. }
  113. }
  114. Modal.prototype.resize = function () {
  115. if (this.isShown) {
  116. $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
  117. } else {
  118. $(window).off('resize.bs.modal')
  119. }
  120. }
  121. Modal.prototype.hideModal = function () {
  122. var that = this
  123. this.$element.hide()
  124. this.backdrop(function () {
  125. that.$body.removeClass('modal-open')
  126. that.resetAdjustments()
  127. that.resetScrollbar()
  128. that.$element.trigger('hidden.bs.modal')
  129. })
  130. }
  131. Modal.prototype.removeBackdrop = function () {
  132. this.$backdrop && this.$backdrop.remove()
  133. this.$backdrop = null
  134. }
  135. Modal.prototype.backdrop = function (callback) {
  136. var that = this
  137. var animate = this.$element.hasClass('fade') ? 'fade' : ''
  138. if (this.isShown && this.options.backdrop) {
  139. var doAnimate = $.support.transition && animate
  140. this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
  141. .prependTo(this.$element)
  142. .on('click.dismiss.bs.modal', $.proxy(function (e) {
  143. if (e.target !== e.currentTarget) return
  144. this.options.backdrop == 'static'
  145. ? this.$element[0].focus.call(this.$element[0])
  146. : this.hide.call(this)
  147. }, this))
  148. if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
  149. this.$backdrop.addClass('in')
  150. if (!callback) return
  151. doAnimate ?
  152. this.$backdrop
  153. .one('bsTransitionEnd', callback)
  154. .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
  155. callback()
  156. } else if (!this.isShown && this.$backdrop) {
  157. this.$backdrop.removeClass('in')
  158. var callbackRemove = function () {
  159. that.removeBackdrop()
  160. callback && callback()
  161. }
  162. $.support.transition && this.$element.hasClass('fade') ?
  163. this.$backdrop
  164. .one('bsTransitionEnd', callbackRemove)
  165. .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
  166. callbackRemove()
  167. } else if (callback) {
  168. callback()
  169. }
  170. }
  171. // these following methods are used to handle overflowing modals
  172. Modal.prototype.handleUpdate = function () {
  173. if (this.options.backdrop) this.adjustBackdrop()
  174. this.adjustDialog()
  175. }
  176. Modal.prototype.adjustBackdrop = function () {
  177. this.$backdrop
  178. .css('height', 0)
  179. .css('height', this.$element[0].scrollHeight)
  180. }
  181. Modal.prototype.adjustDialog = function () {
  182. var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
  183. this.$element.css({
  184. paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
  185. paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
  186. })
  187. }
  188. Modal.prototype.resetAdjustments = function () {
  189. this.$element.css({
  190. paddingLeft: '',
  191. paddingRight: ''
  192. })
  193. }
  194. Modal.prototype.checkScrollbar = function () {
  195. this.bodyIsOverflowing = document.body.scrollHeight > document.documentElement.clientHeight
  196. this.scrollbarWidth = this.measureScrollbar()
  197. }
  198. Modal.prototype.setScrollbar = function () {
  199. var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
  200. if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
  201. }
  202. Modal.prototype.resetScrollbar = function () {
  203. this.$body.css('padding-right', '')
  204. }
  205. Modal.prototype.measureScrollbar = function () { // thx walsh
  206. var scrollDiv = document.createElement('div')
  207. scrollDiv.className = 'modal-scrollbar-measure'
  208. this.$body.append(scrollDiv)
  209. var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
  210. this.$body[0].removeChild(scrollDiv)
  211. return scrollbarWidth
  212. }
  213. // MODAL PLUGIN DEFINITION
  214. // =======================
  215. function Plugin(option, _relatedTarget) {
  216. return this.each(function () {
  217. var $this = $(this)
  218. var data = $this.data('bs.modal')
  219. var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
  220. if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
  221. if (typeof option == 'string') data[option](_relatedTarget)
  222. else if (options.show) data.show(_relatedTarget)
  223. })
  224. }
  225. var old = $.fn.modal
  226. $.fn.modal = Plugin
  227. $.fn.modal.Constructor = Modal
  228. // MODAL NO CONFLICT
  229. // =================
  230. $.fn.modal.noConflict = function () {
  231. $.fn.modal = old
  232. return this
  233. }
  234. // MODAL DATA-API
  235. // ==============
  236. $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
  237. var $this = $(this)
  238. var href = $this.attr('href')
  239. var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
  240. var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
  241. if ($this.is('a')) e.preventDefault()
  242. $target.one('show.bs.modal', function (showEvent) {
  243. if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
  244. $target.one('hidden.bs.modal', function () {
  245. $this.is(':visible') && $this.trigger('focus')
  246. })
  247. })
  248. Plugin.call($target, option, this)
  249. })
  250. }(jQuery);