diff options
| author | fat <[email protected]> | 2015-05-06 13:34:14 -0700 |
|---|---|---|
| committer | fat <[email protected]> | 2015-05-06 13:34:14 -0700 |
| commit | d1fbe200f46002431cdeebf965c4b789ef7ed267 (patch) | |
| tree | 43a7cc7667492e519b906f8a428935da2972ac14 /js/modal.js | |
| parent | 09fb80568a52af6c440db971cdc6fd88eab8f8b5 (diff) | |
| download | bootstrap-d1fbe200f46002431cdeebf965c4b789ef7ed267.tar.xz bootstrap-d1fbe200f46002431cdeebf965c4b789ef7ed267.zip | |
remove closureness from plugins
Diffstat (limited to 'js/modal.js')
| -rw-r--r-- | js/modal.js | 771 |
1 files changed, 255 insertions, 516 deletions
diff --git a/js/modal.js b/js/modal.js index fb4cd0329..0426561af 100644 --- a/js/modal.js +++ b/js/modal.js @@ -1,598 +1,337 @@ -/** ======================================================================= - * Bootstrap: modal.js v4.0.0 - * http://getbootstrap.com/javascript/#modal +/* ======================================================================== + * Bootstrap: modal.js v3.3.4 + * http://getbootstrap.com/javascript/#modals * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== - * @fileoverview - Bootstrap's modal plugin. Modals are streamlined, but - * flexible, dialog prompts with the minimum required functionality and - * smart defaults. - * - * Public Methods & Properties: - * - * + $.modal - * + $.modal.noConflict - * + $.modal.Constructor - * + $.modal.Constructor.VERSION - * + $.modal.Constructor.Defaults - * + $.modal.Constructor.Defaults.backdrop - * + $.modal.Constructor.Defaults.keyboard - * + $.modal.Constructor.Defaults.show - * + $.modal.Constructor.prototype.toggle - * + $.modal.Constructor.prototype.show - * + $.modal.Constructor.prototype.hide - * - * ======================================================================== - */ - -'use strict'; - - -/** - * Our modal class. - * @param {Element} element - * @param {Object} config - * @constructor - */ -var Modal = function (element, config) { - - /** @private {Object} */ - this._config = config - - /** @private {Element} */ - this._element = element + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // MODAL CLASS DEFINITION + // ====================== + + var Modal = function (element, options) { + this.options = options + this.$body = $(document.body) + this.$element = $(element) + this.$dialog = this.$element.find('.modal-dialog') + this.$backdrop = null + this.isShown = null + this.originalBodyPad = null + this.scrollbarWidth = 0 + this.ignoreBackdropClick = false + + if (this.options.remote) { + this.$element + .find('.modal-content') + .load(this.options.remote, $.proxy(function () { + this.$element.trigger('loaded.bs.modal') + }, this)) + } + } - /** @private {Element} */ - this._backdrop = null + Modal.VERSION = '3.3.4' - /** @private {boolean} */ - this._isShown = false - - /** @private {boolean} */ - this._isBodyOverflowing = false - - /** @private {number} */ - this._scrollbarWidth = 0 + Modal.TRANSITION_DURATION = 300 + Modal.BACKDROP_TRANSITION_DURATION = 150 -} + Modal.DEFAULTS = { + backdrop: true, + keyboard: true, + show: true + } + Modal.prototype.toggle = function (_relatedTarget) { + return this.isShown ? this.hide() : this.show(_relatedTarget) + } -/** - * @const - * @type {string} - */ -Modal['VERSION'] = '4.0.0' - - -/** - * @const - * @type {Object} - */ -Modal['Defaults'] = { - 'backdrop' : true, - 'keyboard' : true, - 'show' : true -} - - -/** - * @const - * @type {string} - * @private - */ -Modal._NAME = 'modal' - - -/** - * @const - * @type {string} - * @private - */ -Modal._DATA_KEY = 'bs.modal' - - -/** - * @const - * @type {number} - * @private - */ -Modal._TRANSITION_DURATION = 300 - - -/** - * @const - * @type {number} - * @private - */ -Modal._BACKDROP_TRANSITION_DURATION = 150 - - -/** - * @const - * @type {Function} - * @private - */ -Modal._JQUERY_NO_CONFLICT = $.fn[Modal._NAME] - - -/** - * @const - * @enum {string} - * @private - */ -Modal._Event = { - HIDE : 'hide.bs.modal', - HIDDEN : 'hidden.bs.modal', - SHOW : 'show.bs.modal', - SHOWN : 'shown.bs.modal' -} - - -/** - * @const - * @enum {string} - * @private - */ -Modal._ClassName = { - BACKDROP : 'modal-backdrop', - OPEN : 'modal-open', - FADE : 'fade', - IN : 'in' -} - - -/** - * @const - * @enum {string} - * @private - */ -Modal._Selector = { - DIALOG : '.modal-dialog', - DATA_TOGGLE : '[data-toggle="modal"]', - DATA_DISMISS : '[data-dismiss="modal"]', - SCROLLBAR_MEASURER : 'modal-scrollbar-measure' -} - - - -/** - * Provides the jQuery Interface for the alert component. - * @param {Object|string=} opt_config - * @param {Element=} opt_relatedTarget - * @this {jQuery} - * @return {jQuery} - * @private - */ -Modal._jQueryInterface = function Plugin(opt_config, opt_relatedTarget) { - return this.each(function () { - var data = $(this).data(Modal._DATA_KEY) - var config = $.extend({}, Modal['Defaults'], $(this).data(), typeof opt_config == 'object' && opt_config) - - if (!data) { - data = new Modal(this, config) - $(this).data(Modal._DATA_KEY, data) - } + Modal.prototype.show = function (_relatedTarget) { + var that = this + var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget }) - if (typeof opt_config == 'string') { - data[opt_config](opt_relatedTarget) + this.$element.trigger(e) - } else if (config['show']) { - data['show'](opt_relatedTarget) - } - }) -} + if (this.isShown || e.isDefaultPrevented()) return + this.isShown = true -/** - * @param {Element} relatedTarget - */ -Modal.prototype['toggle'] = function (relatedTarget) { - return this._isShown ? this['hide']() : this['show'](relatedTarget) -} + this.checkScrollbar() + this.setScrollbar() + this.$body.addClass('modal-open') + this.escape() + this.resize() -/** - * @param {Element} relatedTarget - */ -Modal.prototype['show'] = function (relatedTarget) { - var showEvent = $.Event(Modal._Event.SHOW, { relatedTarget: relatedTarget }) + this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this)) - $(this._element).trigger(showEvent) + this.$dialog.on('mousedown.dismiss.bs.modal', function () { + that.$element.one('mouseup.dismiss.bs.modal', function (e) { + if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true + }) + }) - if (this._isShown || showEvent.isDefaultPrevented()) { - return - } + this.backdrop(function () { + var transition = $.support.transition && that.$element.hasClass('fade') - this._isShown = true + if (!that.$element.parent().length) { + that.$element.appendTo(that.$body) // don't move modals dom position + } - this._checkScrollbar() - this._setScrollbar() + that.$element + .show() + .scrollTop(0) - $(document.body).addClass(Modal._ClassName.OPEN) + that.adjustDialog() - this._escape() - this._resize() + if (transition) { + that.$element[0].offsetWidth // force reflow + } - $(this._element).on('click.dismiss.bs.modal', Modal._Selector.DATA_DISMISS, this['hide'].bind(this)) + that.$element.addClass('in') - this._showBackdrop(this._showElement.bind(this, relatedTarget)) -} + that.enforceFocus() + var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget }) -/** - * @param {Event} event - */ -Modal.prototype['hide'] = function (event) { - if (event) { - event.preventDefault() + transition ? + that.$dialog // wait for modal to slide in + .one('bsTransitionEnd', function () { + that.$element.trigger('focus').trigger(e) + }) + .emulateTransitionEnd(Modal.TRANSITION_DURATION) : + that.$element.trigger('focus').trigger(e) + }) } - var hideEvent = $.Event(Modal._Event.HIDE) - - $(this._element).trigger(hideEvent) - - if (!this._isShown || hideEvent.isDefaultPrevented()) { - return - } + Modal.prototype.hide = function (e) { + if (e) e.preventDefault() - this._isShown = false + e = $.Event('hide.bs.modal') - this._escape() - this._resize() + this.$element.trigger(e) - $(document).off('focusin.bs.modal') + if (!this.isShown || e.isDefaultPrevented()) return - $(this._element).removeClass(Modal._ClassName.IN) - this._element.setAttribute('aria-hidden', true) + this.isShown = false - $(this._element).off('click.dismiss.bs.modal') + this.escape() + this.resize() - if (Bootstrap.transition && $(this._element).hasClass(Modal._ClassName.FADE)) { - $(this._element) - .one(Bootstrap.TRANSITION_END, this._hideModal.bind(this)) - .emulateTransitionEnd(Modal._TRANSITION_DURATION) - } else { - this._hideModal() - } -} + $(document).off('focusin.bs.modal') + this.$element + .removeClass('in') + .off('click.dismiss.bs.modal') + .off('mouseup.dismiss.bs.modal') -/** - * @param {Element} relatedTarget - * @private - */ -Modal.prototype._showElement = function (relatedTarget) { - var transition = Bootstrap.transition && $(this._element).hasClass(Modal._ClassName.FADE) + this.$dialog.off('mousedown.dismiss.bs.modal') - if (!this._element.parentNode || this._element.parentNode.nodeType != Node.ELEMENT_NODE) { - document.body.appendChild(this._element) // don't move modals dom position + $.support.transition && this.$element.hasClass('fade') ? + this.$element + .one('bsTransitionEnd', $.proxy(this.hideModal, this)) + .emulateTransitionEnd(Modal.TRANSITION_DURATION) : + this.hideModal() } - this._element.style.display = 'block' - this._element.scrollTop = 0 - - if (this._config['backdrop']) { - this._adjustBackdrop() + Modal.prototype.enforceFocus = function () { + $(document) + .off('focusin.bs.modal') // guard against infinite focus loop + .on('focusin.bs.modal', $.proxy(function (e) { + if (this.$element[0] !== e.target && !this.$element.has(e.target).length) { + this.$element.trigger('focus') + } + }, this)) } - if (transition) { - Bootstrap.reflow(this._element) + Modal.prototype.escape = function () { + if (this.isShown && this.options.keyboard) { + this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) { + e.which == 27 && this.hide() + }, this)) + } else if (!this.isShown) { + this.$element.off('keydown.dismiss.bs.modal') + } } - $(this._element).addClass(Modal._ClassName.IN) - this._element.setAttribute('aria-hidden', false) - - this._enforceFocus() - - var shownEvent = $.Event(Modal._Event.SHOWN, { relatedTarget: relatedTarget }) - - var transitionComplete = function () { - this._element.focus() - $(this._element).trigger(shownEvent) - }.bind(this) - - if (transition) { - var dialog = $(this._element).find(Modal._Selector.DIALOG)[0] - $(dialog) - .one(Bootstrap.TRANSITION_END, transitionComplete) - .emulateTransitionEnd(Modal._TRANSITION_DURATION) - } else { - transitionComplete() + Modal.prototype.resize = function () { + if (this.isShown) { + $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this)) + } else { + $(window).off('resize.bs.modal') + } } -} - - -/** - * @private - */ -Modal.prototype._enforceFocus = function () { - $(document) - .off('focusin.bs.modal') // guard against infinite focus loop - .on('focusin.bs.modal', function (e) { - if (this._element !== e.target && !$(this._element).has(e.target).length) { - this._element.focus() - } - }.bind(this)) -} - - -/** - * @private - */ -Modal.prototype._escape = function () { - if (this._isShown && this._config['keyboard']) { - $(this._element).on('keydown.dismiss.bs.modal', function (event) { - if (event.which === 27) { - this['hide']() - } - }.bind(this)) - - } else if (!this._isShown) { - $(this._element).off('keydown.dismiss.bs.modal') + Modal.prototype.hideModal = function () { + var that = this + this.$element.hide() + this.backdrop(function () { + that.$body.removeClass('modal-open') + that.resetAdjustments() + that.resetScrollbar() + that.$element.trigger('hidden.bs.modal') + }) } -} - -/** - * @private - */ -Modal.prototype._resize = function () { - if (this._isShown) { - $(window).on('resize.bs.modal', this._handleUpdate.bind(this)) - } else { - $(window).off('resize.bs.modal') + Modal.prototype.removeBackdrop = function () { + this.$backdrop && this.$backdrop.remove() + this.$backdrop = null } -} - - -/** - * @private - */ -Modal.prototype._hideModal = function () { - this._element.style.display = 'none' - this._showBackdrop(function () { - $(document.body).removeClass(Modal._ClassName.OPEN) - this._resetAdjustments() - this._resetScrollbar() - $(this._element).trigger(Modal._Event.HIDDEN) - }.bind(this)) -} - - -/** - * @private - */ -Modal.prototype._removeBackdrop = function () { - if (this._backdrop) { - this._backdrop.parentNode.removeChild(this._backdrop) - this._backdrop = null - } -} + Modal.prototype.backdrop = function (callback) { + var that = this + var animate = this.$element.hasClass('fade') ? 'fade' : '' -/** - * @param {Function} callback - * @private - */ -Modal.prototype._showBackdrop = function (callback) { - var animate = $(this._element).hasClass(Modal._ClassName.FADE) ? Modal._ClassName.FADE : '' + if (this.isShown && this.options.backdrop) { + var doAnimate = $.support.transition && animate - if (this._isShown && this._config['backdrop']) { - var doAnimate = Bootstrap.transition && animate + this.$backdrop = $(document.createElement('div')) + .addClass('modal-backdrop ' + animate) + .appendTo(this.$body) - this._backdrop = document.createElement('div') - this._backdrop.className = Modal._ClassName.BACKDROP + this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) { + if (this.ignoreBackdropClick) { + this.ignoreBackdropClick = false + return + } + if (e.target !== e.currentTarget) return + this.options.backdrop == 'static' + ? this.$element[0].focus() + : this.hide() + }, this)) - if (animate) { - $(this._backdrop).addClass(animate) - } + if (doAnimate) this.$backdrop[0].offsetWidth // force reflow - $(this._element).prepend(this._backdrop) + this.$backdrop.addClass('in') - $(this._backdrop).on('click.dismiss.bs.modal', function (event) { - if (event.target !== event.currentTarget) return - this._config['backdrop'] === 'static' - ? this._element.focus() - : this['hide']() - }.bind(this)) + if (!callback) return - if (doAnimate) { - Bootstrap.reflow(this._backdrop) - } + doAnimate ? + this.$backdrop + .one('bsTransitionEnd', callback) + .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : + callback() - $(this._backdrop).addClass(Modal._ClassName.IN) + } else if (!this.isShown && this.$backdrop) { + this.$backdrop.removeClass('in') - if (!callback) { - return - } + var callbackRemove = function () { + that.removeBackdrop() + callback && callback() + } + $.support.transition && this.$element.hasClass('fade') ? + this.$backdrop + .one('bsTransitionEnd', callbackRemove) + .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : + callbackRemove() - if (!doAnimate) { + } else if (callback) { callback() - return } + } - $(this._backdrop) - .one(Bootstrap.TRANSITION_END, callback) - .emulateTransitionEnd(Modal._BACKDROP_TRANSITION_DURATION) + // these following methods are used to handle overflowing modals - } else if (!this._isShown && this._backdrop) { - $(this._backdrop).removeClass(Modal._ClassName.IN) + Modal.prototype.handleUpdate = function () { + this.adjustDialog() + } - var callbackRemove = function () { - this._removeBackdrop() - if (callback) { - callback() - } - }.bind(this) + Modal.prototype.adjustDialog = function () { + var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight - if (Bootstrap.transition && $(this._element).hasClass(Modal._ClassName.FADE)) { - $(this._backdrop) - .one(Bootstrap.TRANSITION_END, callbackRemove) - .emulateTransitionEnd(Modal._BACKDROP_TRANSITION_DURATION) - } else { - callbackRemove() + this.$element.css({ + paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '', + paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : '' + }) + } + + Modal.prototype.resetAdjustments = function () { + this.$element.css({ + paddingLeft: '', + paddingRight: '' + }) + } + + Modal.prototype.checkScrollbar = function () { + var fullWindowWidth = window.innerWidth + if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8 + var documentElementRect = document.documentElement.getBoundingClientRect() + fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left) } + this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth + this.scrollbarWidth = this.measureScrollbar() + } - } else if (callback) { - callback() + Modal.prototype.setScrollbar = function () { + var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10) + this.originalBodyPad = document.body.style.paddingRight || '' + if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth) } -} - - -/** - * ------------------------------------------------------------------------ - * the following methods are used to handle overflowing modals - * todo (fat): these should probably be refactored into a - * ------------------------------------------------------------------------ - */ - - -/** - * @private - */ -Modal.prototype._handleUpdate = function () { - if (this._config['backdrop']) this._adjustBackdrop() - this._adjustDialog() -} - -/** - * @private - */ -Modal.prototype._adjustBackdrop = function () { - this._backdrop.style.height = 0 // todo (fat): no clue why we do this - this._backdrop.style.height = this._element.scrollHeight + 'px' -} - - -/** - * @private - */ -Modal.prototype._adjustDialog = function () { - var isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight - - if (!this._isBodyOverflowing && isModalOverflowing) { - this._element.style.paddingLeft = this._scrollbarWidth + 'px' + + Modal.prototype.resetScrollbar = function () { + this.$body.css('padding-right', this.originalBodyPad) } - if (this._isBodyOverflowing && !isModalOverflowing) { - this._element.style.paddingRight = this._scrollbarWidth + 'px' + Modal.prototype.measureScrollbar = function () { // thx walsh + var scrollDiv = document.createElement('div') + scrollDiv.className = 'modal-scrollbar-measure' + this.$body.append(scrollDiv) + var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth + this.$body[0].removeChild(scrollDiv) + return scrollbarWidth } -} -/** - * @private - */ -Modal.prototype._resetAdjustments = function () { - this._element.style.paddingLeft = '' - this._element.style.paddingRight = '' -} + // MODAL PLUGIN DEFINITION + // ======================= + function Plugin(option, _relatedTarget) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.modal') + var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option) -/** - * @private - */ -Modal.prototype._checkScrollbar = function () { - this._isBodyOverflowing = document.body.scrollHeight > document.documentElement.clientHeight - this._scrollbarWidth = this._getScrollbarWidth() -} + if (!data) $this.data('bs.modal', (data = new Modal(this, options))) + if (typeof option == 'string') data[option](_relatedTarget) + else if (options.show) data.show(_relatedTarget) + }) + } + var old = $.fn.modal -/** - * @private - */ -Modal.prototype._setScrollbar = function () { - var bodyPadding = parseInt(($(document.body).css('padding-right') || 0), 10) + $.fn.modal = Plugin + $.fn.modal.Constructor = Modal - if (this._isBodyOverflowing) { - document.body.style.paddingRight = bodyPadding + this._scrollbarWidth + 'px' - } -} - - -/** - * @private - */ -Modal.prototype._resetScrollbar = function () { - document.body.style.paddingRight = '' -} - - -/** - * @private - */ -Modal.prototype._getScrollbarWidth = function () { // thx walsh - var scrollDiv = document.createElement('div') - scrollDiv.className = Modal._Selector.SCROLLBAR_MEASURER - document.body.appendChild(scrollDiv) - var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth - document.body.removeChild(scrollDiv) - return scrollbarWidth -} - - -/** - * ------------------------------------------------------------------------ - * jQuery Interface + noConflict implementaiton - * ------------------------------------------------------------------------ - */ - -/** - * @const - * @type {Function} - */ -$.fn[Modal._NAME] = Modal._jQueryInterface - - -/** - * @const - * @type {Function} - */ -$.fn[Modal._NAME]['Constructor'] = Modal - - -/** - * @const - * @type {Function} - */ -$.fn[Modal._NAME]['noConflict'] = function () { - $.fn[Modal._NAME] = Modal._JQUERY_NO_CONFLICT - return this -} - - -/** - * ------------------------------------------------------------------------ - * Data Api implementation - * ------------------------------------------------------------------------ - */ - -$(document).on('click.bs.modal.data-api', Modal._Selector.DATA_TOGGLE, function (event) { - var selector = Bootstrap.getSelectorFromElement(this) - - if (selector) { - var target = $(selector)[0] - } - var config = $(target).data(Modal._DATA_KEY) ? 'toggle' : $.extend({}, $(target).data(), $(this).data()) + // MODAL NO CONFLICT + // ================= - if (this.tagName == 'A') { - event.preventDefault() + $.fn.modal.noConflict = function () { + $.fn.modal = old + return this } - var $target = $(target).one(Modal._Event.SHOW, function (showEvent) { - if (showEvent.isDefaultPrevented()) { - return // only register focus restorer if modal will actually get shown - } - $target.one(Modal._Event.HIDDEN, function () { - if ($(this).is(':visible')) { - this.focus() - } - }.bind(this)) - }.bind(this)) + // MODAL DATA-API + // ============== + + $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) { + var $this = $(this) + var href = $this.attr('href') + var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7 + var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data()) + + if ($this.is('a')) e.preventDefault() + + $target.one('show.bs.modal', function (showEvent) { + if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown + $target.one('hidden.bs.modal', function () { + $this.is(':visible') && $this.trigger('focus') + }) + }) + Plugin.call($target, option, this) + }) - Modal._jQueryInterface.call($(target), config, this) -}) +}(jQuery); |
