Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/twbs/bootstrap.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
authorfat <jacobthornton@gmail.com>2015-01-04 00:58:44 +0300
committerfat <jacobthornton@gmail.com>2015-02-11 22:29:43 +0300
commit834220ea20ce5b7cd31edfb624a28b4bf8b29a6a (patch)
tree19bfdadb0c140df437e7b7034e5e1f5ce58343cc /js
parentaed1cd31218113d67d2eca3296edf5d1700b19b8 (diff)
bootstrap onto closure
Diffstat (limited to 'js')
-rw-r--r--js/alert.js280
-rw-r--r--js/button.js275
-rw-r--r--js/carousel.js669
-rw-r--r--js/collapse.js526
-rw-r--r--js/dropdown.js375
-rw-r--r--js/externs/bootstrap.js6
-rw-r--r--js/externs/jQuery.js2159
-rw-r--r--js/hover.js4
-rw-r--r--js/modal.js752
-rw-r--r--js/popover.js320
-rw-r--r--js/scrollspy.js427
-rw-r--r--js/tab.js395
-rw-r--r--js/tests/closure.html83
-rw-r--r--js/tests/index.html4
-rw-r--r--js/tests/unit/button.js55
-rw-r--r--js/tests/unit/carousel.js9
-rw-r--r--js/tests/unit/collapse.js2
-rw-r--r--js/tests/unit/modal.js2
-rw-r--r--js/tests/unit/tooltip.js22
-rw-r--r--js/tests/visual/affix.html299
-rw-r--r--js/tests/visual/alert.html2
-rw-r--r--js/tests/visual/button.html6
-rw-r--r--js/tests/visual/carousel.html2
-rw-r--r--js/tests/visual/collapse.html2
-rw-r--r--js/tests/visual/dropdown.html2
-rw-r--r--js/tests/visual/modal.html2
-rw-r--r--js/tests/visual/popover.html2
-rw-r--r--js/tests/visual/scrollspy.html2
-rw-r--r--js/tests/visual/tab.html2
-rw-r--r--js/tests/visual/tooltip.html2
-rw-r--r--js/tooltip.js1086
-rw-r--r--js/util.js165
32 files changed, 6087 insertions, 1852 deletions
diff --git a/js/alert.js b/js/alert.js
index e8212caf01..38bce06a4d 100644
--- a/js/alert.js
+++ b/js/alert.js
@@ -1,94 +1,260 @@
-/* ========================================================================
- * Bootstrap: alert.js v3.3.2
+/** =======================================================================
+ * Bootstrap: alert.js v4.0.0
* http://getbootstrap.com/javascript/#alerts
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
+ * ========================================================================
+ * @fileoverview - Bootstrap's generic alert component. Add dismiss
+ * functionality to all alert messages with this plugin.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.alert
+ * + $.alert.noConflict
+ * + $.alert.Constructor
+ * + $.alert.Constructor.VERSION
+ * + $.alert.Constructor.prototype.close
+ *
+ * ========================================================================
+ */
-+function ($) {
- 'use strict';
+'use strict';
- // ALERT CLASS DEFINITION
- // ======================
- var dismiss = '[data-dismiss="alert"]'
- var Alert = function (el) {
- $(el).on('click', dismiss, this.close)
+/**
+ * Our Alert class.
+ * @param {Element=} opt_element
+ * @constructor
+ */
+var Alert = function (opt_element) {
+ if (opt_element) {
+ $(opt_element).on('click', Alert._DISMISS_SELECTOR, Alert._handleDismiss(this))
}
+}
- Alert.VERSION = '3.3.2'
- Alert.TRANSITION_DURATION = 150
+/**
+ * @const
+ * @type {string}
+ */
+Alert['VERSION'] = '4.0.0'
- Alert.prototype.close = function (e) {
- var $this = $(this)
- var selector = $this.attr('data-target')
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
- }
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Alert._NAME = 'alert'
- var $parent = $(selector)
- if (e) e.preventDefault()
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Alert._DATA_KEY = 'bs.alert'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Alert._DISMISS_SELECTOR = '[data-dismiss="alert"]'
+
+
+/**
+ * @const
+ * @type {number}
+ * @private
+ */
+Alert._TRANSITION_DURATION = 150
- if (!$parent.length) {
- $parent = $this.closest('.alert')
- }
- $parent.trigger(e = $.Event('close.bs.alert'))
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+Alert._JQUERY_NO_CONFLICT = $.fn[Alert._NAME]
- if (e.isDefaultPrevented()) return
- $parent.removeClass('in')
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Alert._Event = {
+ CLOSE : 'close.bs.alert',
+ CLOSED : 'closed.bs.alert'
+}
- function removeElement() {
- // detach from parent, fire event then clean up data
- $parent.detach().trigger('closed.bs.alert').remove()
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Alert._ClassName = {
+ ALERT : 'alert',
+ FADE : 'fade',
+ IN : 'in'
+}
+
+
+/**
+ * Provides the jQuery Interface for the alert component.
+ * @param {string=} opt_config
+ * @this {jQuery}
+ * @return {jQuery}
+ * @private
+ */
+Alert._jQueryInterface = function (opt_config) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data(Alert._DATA_KEY)
+
+ if (!data) {
+ data = new Alert(this)
+ $this.data(Alert._DATA_KEY, data)
}
- $.support.transition && $parent.hasClass('fade') ?
- $parent
- .one('bsTransitionEnd', removeElement)
- .emulateTransitionEnd(Alert.TRANSITION_DURATION) :
- removeElement()
+ if (opt_config === 'close') {
+ data[opt_config](this)
+ }
+ })
+}
+
+
+/**
+ * Close the alert component
+ * @param {Alert} alertInstance
+ * @return {Function}
+ * @private
+ */
+Alert._handleDismiss = function (alertInstance) {
+ return function (event) {
+ if (event) {
+ event.preventDefault()
+ }
+
+ alertInstance['close'](this)
}
+}
+
+
+/**
+ * Close the alert component
+ * @param {Element} element
+ */
+Alert.prototype['close'] = function (element) {
+ var rootElement = this._getRootElement(element)
+ var customEvent = this._triggerCloseEvent(rootElement)
+
+ if (customEvent.isDefaultPrevented()) return
+ this._removeElement(rootElement)
+}
- // ALERT PLUGIN DEFINITION
- // =======================
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.alert')
+/**
+ * Tries to get the alert's root element
+ * @return {Element}
+ * @private
+ */
+Alert.prototype._getRootElement = function (element) {
+ var parent = false
+ var selector = Bootstrap.getSelectorFromElement(element)
- if (!data) $this.data('bs.alert', (data = new Alert(this)))
- if (typeof option == 'string') data[option].call($this)
- })
+ if (selector) {
+ parent = $(selector)[0]
}
- var old = $.fn.alert
+ if (!parent) {
+ parent = $(element).closest('.' + Alert._ClassName.ALERT)[0]
+ }
+
+ return parent
+}
- $.fn.alert = Plugin
- $.fn.alert.Constructor = Alert
+/**
+ * Trigger close event on element
+ * @return {$.Event}
+ * @private
+ */
+Alert.prototype._triggerCloseEvent = function (element) {
+ var closeEvent = $.Event(Alert._Event.CLOSE)
+ $(element).trigger(closeEvent)
+ return closeEvent
+}
- // ALERT NO CONFLICT
- // =================
- $.fn.alert.noConflict = function () {
- $.fn.alert = old
- return this
+/**
+ * Trigger closed event and remove element from dom
+ * @private
+ */
+Alert.prototype._removeElement = function (element) {
+ $(element).removeClass(Alert._ClassName.IN)
+
+ if (!Bootstrap.transition || !$(element).hasClass(Alert._ClassName.FADE)) {
+ this._destroyElement(element)
+ return
}
+ $(element)
+ .one(Bootstrap.TRANSITION_END, this._destroyElement.bind(this, element))
+ .emulateTransitionEnd(Alert._TRANSITION_DURATION)
+}
+
+
+/**
+ * clean up any lingering jquery data and kill element
+ * @private
+ */
+Alert.prototype._destroyElement = function (element) {
+ $(element)
+ .detach()
+ .trigger(Alert._Event.CLOSED)
+ .remove()
+}
+
+
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Alert._NAME] = Alert._jQueryInterface
+
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Alert._NAME]['Constructor'] = Alert
+
+
+/**
+ * @return {Function}
+ */
+$.fn[Alert._NAME]['noConflict'] = function () {
+ $.fn[Alert._NAME] = Alert._JQUERY_NO_CONFLICT
+ return Alert._jQueryInterface
+}
- // ALERT DATA-API
- // ==============
- $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
+/**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
-}(jQuery);
+$(document).on('click.bs.alert.data-api', Alert._DISMISS_SELECTOR, Alert._handleDismiss(new Alert))
diff --git a/js/button.js b/js/button.js
index 4d56901737..8ee2d6b084 100644
--- a/js/button.js
+++ b/js/button.js
@@ -1,116 +1,207 @@
-/* ========================================================================
- * Bootstrap: button.js v3.3.2
+/** =======================================================================
+ * Bootstrap: button.js v4.0.0
* http://getbootstrap.com/javascript/#buttons
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
-
-+function ($) {
- 'use strict';
-
- // BUTTON PUBLIC CLASS DEFINITION
- // ==============================
-
- var Button = function (element, options) {
- this.$element = $(element)
- this.options = $.extend({}, Button.DEFAULTS, options)
- this.isLoading = false
- }
-
- Button.VERSION = '3.3.2'
-
- Button.DEFAULTS = {
- loadingText: 'loading...'
- }
-
- Button.prototype.setState = function (state) {
- var d = 'disabled'
- var $el = this.$element
- var val = $el.is('input') ? 'val' : 'html'
- var data = $el.data()
-
- state = state + 'Text'
-
- if (data.resetText == null) $el.data('resetText', $el[val]())
-
- // push to event loop to allow forms to submit
- setTimeout($.proxy(function () {
- $el[val](data[state] == null ? this.options[state] : data[state])
+ * ========================================================================
+ * @fileoverview - Bootstrap's generic button component.
+ *
+ * Note (@fat): Deprecated "setState" – imo, better solutions for managing a
+ * buttons state should exist outside this plugin.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.button
+ * + $.button.noConflict
+ * + $.button.Constructor
+ * + $.button.Constructor.VERSION
+ * + $.button.Constructor.prototype.toggle
+ *
+ * ========================================================================
+ */
+
+'use strict';
+
+
+/**
+ * Our Button class.
+ * @param {Element!} element
+ * @constructor
+ */
+var Button = function (element) {
+
+ /** @private {Element} */
+ this._element = element
+
+}
+
+
+/**
+ * @const
+ * @type {string}
+ */
+Button['VERSION'] = '4.0.0'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Button._NAME = 'button'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Button._DATA_KEY = 'bs.button'
+
+
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+Button._JQUERY_NO_CONFLICT = $.fn[Button._NAME]
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Button._ClassName = {
+ ACTIVE : 'active',
+ BUTTON : 'btn',
+ FOCUS : 'focus'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Button._Selector = {
+ DATA_TOGGLE_CARROT : '[data-toggle^="button"]',
+ DATA_TOGGLE : '[data-toggle="buttons"]',
+ INPUT : 'input',
+ ACTIVE : '.active',
+ BUTTON : '.btn'
+}
+
+
+/**
+ * Provides the jQuery Interface for the Button component.
+ * @param {string=} opt_config
+ * @this {jQuery}
+ * @return {jQuery}
+ * @private
+ */
+Button._jQueryInterface = function (opt_config) {
+ return this.each(function () {
+ var data = $(this).data(Button._DATA_KEY)
+
+ if (!data) {
+ data = new Button(this)
+ $(this).data(Button._DATA_KEY, data)
+ }
- if (state == 'loadingText') {
- this.isLoading = true
- $el.addClass(d).attr(d, d)
- } else if (this.isLoading) {
- this.isLoading = false
- $el.removeClass(d).removeAttr(d)
+ if (opt_config === 'toggle') {
+ data[opt_config]()
+ }
+ })
+}
+
+
+/**
+ * Toggle's the button active state
+ */
+Button.prototype['toggle'] = function () {
+ var triggerChangeEvent = true
+ var rootElement = $(this._element).closest(Button._Selector.DATA_TOGGLE)[0]
+
+ if (rootElement) {
+ var input = $(this._element).find(Button._Selector.INPUT)[0]
+ if (input) {
+ if (input.type == 'radio') {
+ if (input.checked && $(this._element).hasClass(Button._ClassName.ACTIVE)) {
+ triggerChangeEvent = false
+ } else {
+ var activeElement = $(rootElement).find(Button._Selector.ACTIVE)[0]
+ if (activeElement) {
+ $(activeElement).removeClass(Button._ClassName.ACTIVE)
+ }
+ }
}
- }, this), 0)
- }
-
- Button.prototype.toggle = function () {
- var changed = true
- var $parent = this.$element.closest('[data-toggle="buttons"]')
- if ($parent.length) {
- var $input = this.$element.find('input')
- if ($input.prop('type') == 'radio') {
- if ($input.prop('checked') && this.$element.hasClass('active')) changed = false
- else $parent.find('.active').removeClass('active')
+ if (triggerChangeEvent) {
+ input.checked = !$(this._element).hasClass(Button._ClassName.ACTIVE)
+ $(this._element).trigger('change')
}
- if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change')
- } else {
- this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
}
+ } else {
+ this._element.setAttribute('aria-pressed', !$(this._element).hasClass(Button._ClassName.ACTIVE))
+ }
- if (changed) this.$element.toggleClass('active')
+ if (triggerChangeEvent) {
+ $(this._element).toggleClass(Button._ClassName.ACTIVE)
}
+}
- // BUTTON PLUGIN DEFINITION
- // ========================
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.button')
- var options = typeof option == 'object' && option
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Button._NAME] = Button._jQueryInterface
- if (!data) $this.data('bs.button', (data = new Button(this, options)))
- if (option == 'toggle') data.toggle()
- else if (option) data.setState(option)
- })
- }
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Button._NAME]['Constructor'] = Button
- var old = $.fn.button
- $.fn.button = Plugin
- $.fn.button.Constructor = Button
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Button._NAME]['noConflict'] = function () {
+ $.fn[Button._NAME] = Button._JQUERY_NO_CONFLICT
+ return this
+}
- // BUTTON NO CONFLICT
- // ==================
+/**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
- $.fn.button.noConflict = function () {
- $.fn.button = old
- return this
- }
+$(document)
+ .on('click.bs.button.data-api', Button._Selector.DATA_TOGGLE_CARROT, function (event) {
+ event.preventDefault()
+ var button = event.target
- // BUTTON DATA-API
- // ===============
-
- $(document)
- .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
- var $btn = $(e.target)
- if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
- Plugin.call($btn, 'toggle')
- e.preventDefault()
- })
- .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
- $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
- })
+ if (!$(button).hasClass(Button._ClassName.BUTTON)) {
+ button = $(button).closest(Button._Selector.BUTTON)
+ }
-}(jQuery);
+ Button._jQueryInterface.call($(button), 'toggle')
+ })
+ .on('focus.bs.button.data-api blur.bs.button.data-api', Button._Selector.DATA_TOGGLE_CARROT, function (event) {
+ var button = $(event.target).closest(Button._Selector.BUTTON)[0]
+ $(button).toggleClass(Button._ClassName.FOCUS, /^focus(in)?$/.test(event.type))
+ })
diff --git a/js/carousel.js b/js/carousel.js
index 8aa04e1170..8f33f48601 100644
--- a/js/carousel.js
+++ b/js/carousel.js
@@ -1,237 +1,576 @@
-/* ========================================================================
- * Bootstrap: carousel.js v3.3.2
+/** =======================================================================
+ * Bootstrap: carousel.js v4.0.0
* http://getbootstrap.com/javascript/#carousel
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @fileoverview - Bootstrap's carousel. A slideshow component for cycling
+ * through elements, like a carousel. Nested carousels are not supported.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.carousel
+ * + $.carousel.noConflict
+ * + $.carousel.Constructor
+ * + $.carousel.Constructor.VERSION
+ * + $.carousel.Constructor.Defaults
+ * + $.carousel.Constructor.Defaults.interval
+ * + $.carousel.Constructor.Defaults.pause
+ * + $.carousel.Constructor.Defaults.wrap
+ * + $.carousel.Constructor.Defaults.keyboard
+ * + $.carousel.Constructor.Defaults.slide
+ * + $.carousel.Constructor.prototype.next
+ * + $.carousel.Constructor.prototype.prev
+ * + $.carousel.Constructor.prototype.pause
+ * + $.carousel.Constructor.prototype.cycle
+ *
+ * ========================================================================
+ */
+
+'use strict';
+
+
+/**
+ * Our carousel class.
+ * @param {Element!} element
+ * @param {Object=} opt_config
+ * @constructor
+ */
+var Carousel = function (element, opt_config) {
+
+ /** @private {Element} */
+ this._element = $(element)[0]
+
+ /** @private {Element} */
+ this._indicatorsElement = $(this._element).find(Carousel._Selector.INDICATORS)[0]
+
+ /** @private {?Object} */
+ this._config = opt_config || null
+ /** @private {boolean} */
+ this._isPaused = false
-+function ($) {
- 'use strict';
+ /** @private {boolean} */
+ this._isSliding = false
+
+ /** @private {?number} */
+ this._interval = null
+
+ /** @private {?Element} */
+ this._activeElement = null
+
+ /** @private {?Array} */
+ this._items = null
+
+ this._addEventListeners()
+
+}
+
+
+/**
+ * @const
+ * @type {string}
+ */
+Carousel['VERSION'] = '4.0.0'
+
+
+/**
+ * @const
+ * @type {Object}
+ */
+Carousel['Defaults'] = {
+ 'interval' : 5000,
+ 'pause' : 'hover',
+ 'wrap' : true,
+ 'keyboard' : true,
+ 'slide' : false
+}
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Carousel._NAME = 'carousel'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Carousel._DATA_KEY = 'bs.carousel'
+
+
+/**
+ * @const
+ * @type {number}
+ * @private
+ */
+Carousel._TRANSITION_DURATION = 600
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Carousel._Direction = {
+ NEXT : 'next',
+ PREVIOUS : 'prev'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Carousel._Event = {
+ SLIDE : 'slide.bs.carousel',
+ SLID : 'slid.bs.carousel'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Carousel._ClassName = {
+ CAROUSEL : 'carousel',
+ ACTIVE : 'active',
+ SLIDE : 'slide',
+ RIGHT : 'right',
+ LEFT : 'left',
+ ITEM : 'carousel-item'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Carousel._Selector = {
+ ACTIVE : '.active',
+ ACTIVE_ITEM : '.active.carousel-item',
+ ITEM : '.carousel-item',
+ NEXT_PREV : '.next, .prev',
+ INDICATORS : '.carousel-indicators'
+}
+
+
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+Carousel._JQUERY_NO_CONFLICT = $.fn[Carousel._NAME]
+
+
+/**
+ * @param {Object=} opt_config
+ * @this {jQuery}
+ * @return {jQuery}
+ * @private
+ */
+Carousel._jQueryInterface = function (opt_config) {
+ return this.each(function () {
+ var data = $(this).data(Carousel._DATA_KEY)
+ var config = $.extend({}, Carousel['Defaults'], $(this).data(), typeof opt_config == 'object' && opt_config)
+ var action = typeof opt_config == 'string' ? opt_config : config.slide
+
+ if (!data) {
+ data = new Carousel(this, config)
+ $(this).data(Carousel._DATA_KEY, data)
+ }
+
+ if (typeof opt_config == 'number') {
+ data.to(opt_config)
- // CAROUSEL CLASS DEFINITION
- // =========================
+ } else if (action) {
+ data[action]()
+
+ } else if (config.interval) {
+ data['pause']()
+ data['cycle']()
+ }
+ })
+}
- var Carousel = function (element, options) {
- this.$element = $(element)
- this.$indicators = this.$element.find('.carousel-indicators')
- this.options = options
- this.paused =
- this.sliding =
- this.interval =
- this.$active =
- this.$items = null
- this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))
+/**
+ * Click handler for data api
+ * @param {Event} event
+ * @this {Element}
+ * @private
+ */
+Carousel._dataApiClickHandler = function (event) {
+ var selector = Bootstrap.getSelectorFromElement(this)
- this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element
- .on('mouseenter.bs.carousel', $.proxy(this.pause, this))
- .on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
+ if (!selector) {
+ return
}
- Carousel.VERSION = '3.3.2'
+ var target = $(selector)[0]
+
+ if (!target || !$(target).hasClass(Carousel._ClassName.CAROUSEL)) {
+ return
+ }
- Carousel.TRANSITION_DURATION = 600
+ var config = $.extend({}, $(target).data(), $(this).data())
- Carousel.DEFAULTS = {
- interval: 5000,
- pause: 'hover',
- wrap: true,
- keyboard: true
+ var slideIndex = this.getAttribute('data-slide-to')
+ if (slideIndex) {
+ config.interval = false
}
- Carousel.prototype.keydown = function (e) {
- if (/input|textarea/i.test(e.target.tagName)) return
- switch (e.which) {
- case 37: this.prev(); break
- case 39: this.next(); break
- default: return
- }
+ Carousel._jQueryInterface.call($(target), config)
- e.preventDefault()
+ if (slideIndex) {
+ $(target).data(Carousel._DATA_KEY).to(slideIndex)
}
- Carousel.prototype.cycle = function (e) {
- e || (this.paused = false)
+ event.preventDefault()
+}
- this.interval && clearInterval(this.interval)
- this.options.interval
- && !this.paused
- && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
+/**
+ * Advance the carousel to the next slide
+ */
+Carousel.prototype['next'] = function () {
+ if (!this._isSliding) {
+ this._slide(Carousel._Direction.NEXT)
+ }
+}
+
- return this
+/**
+ * Return the carousel to the previous slide
+ */
+Carousel.prototype['prev'] = function () {
+ if (!this._isSliding) {
+ this._slide(Carousel._Direction.PREVIOUS)
}
+}
- Carousel.prototype.getItemIndex = function (item) {
- this.$items = item.parent().children('.carousel-item')
- return this.$items.index(item || this.$active)
+
+/**
+ * Pause the carousel cycle
+ * @param {Event=} opt_event
+ */
+Carousel.prototype['pause'] = function (opt_event) {
+ if (!opt_event) {
+ this._isPaused = true
}
- Carousel.prototype.getItemForDirection = function (direction, active) {
- var activeIndex = this.getItemIndex(active)
- var willWrap = (direction == 'prev' && activeIndex === 0)
- || (direction == 'next' && activeIndex == (this.$items.length - 1))
- if (willWrap && !this.options.wrap) return active
- var delta = direction == 'prev' ? -1 : 1
- var itemIndex = (activeIndex + delta) % this.$items.length
- return this.$items.eq(itemIndex)
+ if ($(this._element).find(Carousel._Selector.NEXT_PREV)[0] && Bootstrap.transition) {
+ $(this._element).trigger(Bootstrap.transition.end)
+ this['cycle'](true)
}
- Carousel.prototype.to = function (pos) {
- var that = this
- var activeIndex = this.getItemIndex(this.$active = this.$element.find('.carousel-item.active'))
+ clearInterval(this._interval)
+ this._interval = null
+}
+
- if (pos > (this.$items.length - 1) || pos < 0) return
+/**
+ * Cycle to the next carousel item
+ * @param {Event|boolean=} opt_event
+ */
+Carousel.prototype['cycle'] = function (opt_event) {
+ if (!opt_event) {
+ this._isPaused = false
+ }
- if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid"
- if (activeIndex == pos) return this.pause().cycle()
+ if (this._interval) {
+ clearInterval(this._interval)
+ this._interval = null
+ }
- return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos))
+ if (this._config['interval'] && !this._isPaused) {
+ this._interval = setInterval(this['next'].bind(this), this._config['interval'])
}
+}
- Carousel.prototype.pause = function (e) {
- e || (this.paused = true)
- if (this.$element.find('.next, .prev').length && $.support.transition) {
- this.$element.trigger($.support.transition.end)
- this.cycle(true)
- }
+/**
+ * @return {Object}
+ */
+Carousel.prototype['getConfig'] = function () {
+ return this._config
+}
+
- this.interval = clearInterval(this.interval)
+/**
+ * Move active carousel item to specified index
+ * @param {number} index
+ */
+Carousel.prototype.to = function (index) {
+ this._activeElement = $(this._element).find(Carousel._Selector.ACTIVE_ITEM)[0]
- return this
+ var activeIndex = this._getItemIndex(this._activeElement)
+
+ if (index > (this._items.length - 1) || index < 0) {
+ return
}
- Carousel.prototype.next = function () {
- if (this.sliding) return
- return this.slide('next')
+ if (this._isSliding) {
+ $(this._element).one(Carousel._Event.SLID, function () { this.to(index) }.bind(this))
+ return
}
- Carousel.prototype.prev = function () {
- if (this.sliding) return
- return this.slide('prev')
+ if (activeIndex == index) {
+ this['pause']()
+ this['cycle']()
+ return
}
- Carousel.prototype.slide = function (type, next) {
- var $active = this.$element.find('.carousel-item.active')
- var $next = next || this.getItemForDirection(type, $active)
- var isCycling = this.interval
- var direction = type == 'next' ? 'left' : 'right'
- var that = this
+ var direction = index > activeIndex ?
+ Carousel._Direction.NEXT :
+ Carousel._Direction.PREVIOUS
- if ($next.hasClass('active')) return (this.sliding = false)
+ this._slide(direction, this._items[index])
+}
- var relatedTarget = $next[0]
- var slideEvent = $.Event('slide.bs.carousel', {
- relatedTarget: relatedTarget,
- direction: direction
- })
- this.$element.trigger(slideEvent)
- if (slideEvent.isDefaultPrevented()) return
- this.sliding = true
+/**
+ * Add event listeners to root element
+ * @private
+ */
+Carousel.prototype._addEventListeners = function () {
+ if (this._config['keyboard']) {
+ $(this._element).on('keydown.bs.carousel', this._keydown.bind(this))
+ }
- isCycling && this.pause()
+ if (this._config['pause'] == 'hover' && !('ontouchstart' in document.documentElement)) {
+ $(this._element)
+ .on('mouseenter.bs.carousel', this['pause'].bind(this))
+ .on('mouseleave.bs.carousel', this['cycle'].bind(this))
+ }
+}
- if (this.$indicators.length) {
- this.$indicators.find('.active').removeClass('active')
- var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)])
- $nextIndicator && $nextIndicator.addClass('active')
- }
- var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid"
- if ($.support.transition && this.$element.hasClass('slide')) {
- $next.addClass(type)
- $next[0].offsetWidth // force reflow
- $active.addClass(direction)
- $next.addClass(direction)
- $active
- .one('bsTransitionEnd', function () {
- $next.removeClass([type, direction].join(' ')).addClass('active')
- $active.removeClass(['active', direction].join(' '))
- that.sliding = false
- setTimeout(function () {
- that.$element.trigger(slidEvent)
- }, 0)
- })
- .emulateTransitionEnd(Carousel.TRANSITION_DURATION)
- } else {
- $active.removeClass('active')
- $next.addClass('active')
- this.sliding = false
- this.$element.trigger(slidEvent)
- }
+/**
+ * Keydown handler
+ * @param {Event} event
+ * @private
+ */
+Carousel.prototype._keydown = function (event) {
+ event.preventDefault()
- isCycling && this.cycle()
+ if (/input|textarea/i.test(event.target.tagName)) return
- return this
+ switch (event.which) {
+ case 37: this['prev'](); break
+ case 39: this['next'](); break
+ default: return
+ }
+}
+
+
+/**
+ * Get item index
+ * @param {Element} element
+ * @return {number}
+ * @private
+ */
+Carousel.prototype._getItemIndex = function (element) {
+ this._items = $.makeArray($(element).parent().find(Carousel._Selector.ITEM))
+
+ return this._items.indexOf(element)
+}
+
+
+/**
+ * Get next displayed item based on direction
+ * @param {Carousel._Direction} direction
+ * @param {Element} activeElement
+ * @return {Element}
+ * @private
+ */
+Carousel.prototype._getItemByDirection = function (direction, activeElement) {
+ var activeIndex = this._getItemIndex(activeElement)
+ var isGoingToWrap = (direction === Carousel._Direction.PREVIOUS && activeIndex === 0) ||
+ (direction === Carousel._Direction.NEXT && activeIndex == (this._items.length - 1))
+
+ if (isGoingToWrap && !this._config['wrap']) {
+ return activeElement
}
+ var delta = direction == Carousel._Direction.PREVIOUS ? -1 : 1
+ var itemIndex = (activeIndex + delta) % this._items.length
- // CAROUSEL PLUGIN DEFINITION
- // ==========================
+ return itemIndex === -1 ? this._items[this._items.length - 1] : this._items[itemIndex]
+}
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.carousel')
- var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
- var action = typeof option == 'string' ? option : options.slide
- if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))
- if (typeof option == 'number') data.to(option)
- else if (action) data[action]()
- else if (options.interval) data.pause().cycle()
- })
+/**
+ * Trigger slide event on element
+ * @param {Element} relatedTarget
+ * @param {Carousel._ClassName} directionalClassname
+ * @return {$.Event}
+ * @private
+ */
+Carousel.prototype._triggerSlideEvent = function (relatedTarget, directionalClassname) {
+ var slideEvent = $.Event(Carousel._Event.SLIDE, {
+ relatedTarget: relatedTarget,
+ direction: directionalClassname
+ })
+
+ $(this._element).trigger(slideEvent)
+
+ return slideEvent
+}
+
+
+/**
+ * Set the active indicator if available
+ * @param {Element} element
+ * @private
+ */
+Carousel.prototype._setActiveIndicatorElement = function (element) {
+ if (this._indicatorsElement) {
+ $(this._indicatorsElement)
+ .find(Carousel._Selector.ACTIVE)
+ .removeClass(Carousel._ClassName.ACTIVE)
+
+ var nextIndicator = this._indicatorsElement.children[this._getItemIndex(element)]
+ if (nextIndicator) {
+ $(nextIndicator).addClass(Carousel._ClassName.ACTIVE)
+ }
}
+}
+
+
+/**
+ * Slide the carousel element in a direction
+ * @param {Carousel._Direction} direction
+ * @param {Element=} opt_nextElement
+ */
+Carousel.prototype._slide = function (direction, opt_nextElement) {
+ var activeElement = $(this._element).find(Carousel._Selector.ACTIVE_ITEM)[0]
+ var nextElement = opt_nextElement || activeElement && this._getItemByDirection(direction, activeElement)
+
+ var isCycling = !!this._interval
- var old = $.fn.carousel
+ var directionalClassName = direction == Carousel._Direction.NEXT ?
+ Carousel._ClassName.LEFT :
+ Carousel._ClassName.RIGHT
- $.fn.carousel = Plugin
- $.fn.carousel.Constructor = Carousel
+ if (nextElement && $(nextElement).hasClass(Carousel._ClassName.ACTIVE)) {
+ this._isSliding = false
+ return
+ }
+
+ var slideEvent = this._triggerSlideEvent(nextElement, directionalClassName)
+ if (slideEvent.isDefaultPrevented()) {
+ return
+ }
+ if (!activeElement || !nextElement) {
+ // some weirdness is happening, so we bail (maybe throw exception here alerting user that they're dom is off
+ return
+ }
- // CAROUSEL NO CONFLICT
- // ====================
+ this._isSliding = true
- $.fn.carousel.noConflict = function () {
- $.fn.carousel = old
- return this
+ if (isCycling) {
+ this['pause']()
}
+ this._setActiveIndicatorElement(nextElement)
- // CAROUSEL DATA-API
- // =================
+ var slidEvent = $.Event(Carousel._Event.SLID, { relatedTarget: nextElement, direction: directionalClassName })
- var clickHandler = function (e) {
- var href
- var $this = $(this)
- var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
- if (!$target.hasClass('carousel')) return
- var options = $.extend({}, $target.data(), $this.data())
- var slideIndex = $this.attr('data-slide-to')
- if (slideIndex) options.interval = false
+ if (Bootstrap.transition && $(this._element).hasClass(Carousel._ClassName.SLIDE)) {
+ $(nextElement).addClass(direction)
- Plugin.call($target, options)
+ Bootstrap.reflow(nextElement)
- if (slideIndex) {
- $target.data('bs.carousel').to(slideIndex)
- }
+ $(activeElement).addClass(directionalClassName)
+ $(nextElement).addClass(directionalClassName)
+
+ $(activeElement)
+ .one(Bootstrap.TRANSITION_END, function () {
+ $(nextElement)
+ .removeClass(directionalClassName)
+ .removeClass(direction)
+
+ $(nextElement).addClass(Carousel._ClassName.ACTIVE)
+
+ $(activeElement)
+ .removeClass(Carousel._ClassName.ACTIVE)
+ .removeClass(direction)
+ .removeClass(directionalClassName)
- e.preventDefault()
+ this._isSliding = false
+
+ setTimeout(function () {
+ $(this._element).trigger(slidEvent)
+ }.bind(this), 0)
+ }.bind(this))
+ .emulateTransitionEnd(Carousel._TRANSITION_DURATION)
+
+ } else {
+ $(activeElement).removeClass(Carousel._ClassName.ACTIVE)
+ $(nextElement).addClass(Carousel._ClassName.ACTIVE)
+
+ this._isSliding = false
+ $(this._element).trigger(slidEvent)
+ }
+
+ if (isCycling) {
+ this['cycle']()
}
+}
- $(document)
- .on('click.bs.carousel.data-api', '[data-slide]', clickHandler)
- .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler)
- $(window).on('load', function () {
- $('[data-ride="carousel"]').each(function () {
- var $carousel = $(this)
- Plugin.call($carousel, $carousel.data())
- })
- })
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Carousel._NAME] = Carousel._jQueryInterface
+
-}(jQuery);
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Carousel._NAME]['Constructor'] = Carousel
+
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Carousel._NAME]['noConflict'] = function () {
+ $.fn[Carousel._NAME] = Carousel._JQUERY_NO_CONFLICT
+ return this
+}
+
+
+/**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+$(document)
+ .on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', Carousel._dataApiClickHandler)
+
+$(window).on('load', function () {
+ $('[data-ride="carousel"]').each(function () {
+ var $carousel = $(this)
+ Carousel._jQueryInterface.call($carousel, /** @type {Object} */ ($carousel.data()))
+ })
+})
diff --git a/js/collapse.js b/js/collapse.js
index 2bc30e7ba4..156163e3f9 100644
--- a/js/collapse.js
+++ b/js/collapse.js
@@ -1,211 +1,455 @@
-/* ========================================================================
- * Bootstrap: collapse.js v3.3.2
+/** =======================================================================
+ * Bootstrap: collapse.js v4.0.0
* http://getbootstrap.com/javascript/#collapse
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @fileoverview - Bootstrap's collapse plugin. Flexible support for
+ * collapsible components like accordions and navigation.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.carousel
+ * + $.carousel.noConflict
+ * + $.carousel.Constructor
+ * + $.carousel.Constructor.VERSION
+ * + $.carousel.Constructor.Defaults
+ * + $.carousel.Constructor.Defaults.toggle
+ * + $.carousel.Constructor.Defaults.trigger
+ * + $.carousel.Constructor.Defaults.parent
+ * + $.carousel.Constructor.prototype.toggle
+ * + $.carousel.Constructor.prototype.show
+ * + $.carousel.Constructor.prototype.hide
+ *
+ * ========================================================================
+ */
+'use strict';
-+function ($) {
- 'use strict';
- // COLLAPSE PUBLIC CLASS DEFINITION
- // ================================
+/**
+ * Our collapse class.
+ * @param {Element!} element
+ * @param {Object=} opt_config
+ * @constructor
+ */
+var Collapse = function (element, opt_config) {
- var Collapse = function (element, options) {
- this.$element = $(element)
- this.options = $.extend({}, Collapse.DEFAULTS, options)
- this.$trigger = $(this.options.trigger).filter('[href="#' + element.id + '"], [data-target="#' + element.id + '"]')
- this.transitioning = null
+ /** @private {Element} */
+ this._element = element
- if (this.options.parent) {
- this.$parent = this.getParent()
- } else {
- this.addAriaAndCollapsedClass(this.$element, this.$trigger)
- }
+ /** @private {Object} */
+ this._config = $.extend({}, Collapse['Defaults'], opt_config)
- if (this.options.toggle) this.toggle()
- }
+ /** @private {Element} */
+ this._trigger = typeof this._config['trigger'] == 'string' ?
+ $(this._config['trigger'])[0] : this._config['trigger']
- Collapse.VERSION = '3.3.2'
+ /** @private {boolean} */
+ this._isTransitioning = false
- Collapse.TRANSITION_DURATION = 350
+ /** @private {?Element} */
+ this._parent = this._config['parent'] ? this._getParent() : null
- Collapse.DEFAULTS = {
- toggle: true,
- trigger: '[data-toggle="collapse"]'
+ if (!this._config['parent']) {
+ this._addAriaAndCollapsedClass(this._element, this._trigger)
}
- Collapse.prototype.dimension = function () {
- var hasWidth = this.$element.hasClass('width')
- return hasWidth ? 'width' : 'height'
+ if (this._config['toggle']) {
+ this['toggle']()
}
- Collapse.prototype.show = function () {
- if (this.transitioning || this.$element.hasClass('in')) return
-
- var activesData
- var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing')
+}
+
+
+/**
+ * @const
+ * @type {string}
+ */
+Collapse['VERSION'] = '4.0.0'
+
+
+/**
+ * @const
+ * @type {Object}
+ */
+Collapse['Defaults'] = {
+ 'toggle' : true,
+ 'trigger' : '[data-toggle="collapse"]',
+ 'parent' : null
+}
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Collapse._NAME = 'collapse'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Collapse._DATA_KEY = 'bs.collapse'
+
+
+/**
+ * @const
+ * @type {number}
+ * @private
+ */
+Collapse._TRANSITION_DURATION = 600
+
+
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+Collapse._JQUERY_NO_CONFLICT = $.fn[Collapse._NAME]
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Collapse._Event = {
+ SHOW : 'show.bs.collapse',
+ SHOWN : 'shown.bs.collapse',
+ HIDE : 'hide.bs.collapse',
+ HIDDEN : 'hidden.bs.collapse'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Collapse._ClassName = {
+ IN : 'in',
+ COLLAPSE : 'collapse',
+ COLLAPSING : 'collapsing',
+ COLLAPSED : 'collapsed'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Collapse._Dimension = {
+ WIDTH : 'width',
+ HEIGHT : 'height'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Collapse._Selector = {
+ ACTIVES : '.panel > .in, .panel > .collapsing'
+}
+
+
+/**
+ * Provides the jQuery Interface for the alert component.
+ * @param {Object|string=} opt_config
+ * @this {jQuery}
+ * @return {jQuery}
+ * @private
+ */
+Collapse._jQueryInterface = function (opt_config) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data(Collapse._DATA_KEY)
+ var config = $.extend({}, Collapse['Defaults'], $this.data(), typeof opt_config == 'object' && opt_config)
- if (actives && actives.length) {
- activesData = actives.data('bs.collapse')
- if (activesData && activesData.transitioning) return
+ if (!data && config['toggle'] && opt_config == 'show') {
+ config['toggle'] = false
}
- var startEvent = $.Event('show.bs.collapse')
- this.$element.trigger(startEvent)
- if (startEvent.isDefaultPrevented()) return
+ if (!data) {
+ data = new Collapse(this, config)
+ $this.data(Collapse._DATA_KEY, data)
+ }
- if (actives && actives.length) {
- Plugin.call(actives, 'hide')
- activesData || actives.data('bs.collapse', null)
+ if (typeof opt_config == 'string') {
+ data[opt_config]()
}
+ })
+}
+
+
+/**
+ * Function for getting target element from element
+ * @return {Element}
+ * @private
+ */
+Collapse._getTargetFromElement = function (element) {
+ var selector = Bootstrap.getSelectorFromElement(element)
+
+ return selector ? $(selector)[0] : null
+}
+
- var dimension = this.dimension()
+/**
+ * Toggles the collapse element based on the presence of the 'in' class
+ */
+Collapse.prototype['toggle'] = function () {
+ if ($(this._element).hasClass(Collapse._ClassName.IN)) {
+ this['hide']()
+ } else {
+ this['show']()
+ }
+}
- this.$element
- .removeClass('collapse')
- .addClass('collapsing')[dimension](0)
- .attr('aria-expanded', true)
- this.$trigger
- .removeClass('collapsed')
- .attr('aria-expanded', true)
+/**
+ * Show's the collapsing element
+ */
+Collapse.prototype['show'] = function () {
+ if (this._isTransitioning || $(this._element).hasClass(Collapse._ClassName.IN)) {
+ return
+ }
- this.transitioning = 1
+ var activesData, actives
- var complete = function () {
- this.$element
- .removeClass('collapsing')
- .addClass('collapse in')[dimension]('')
- this.transitioning = 0
- this.$element
- .trigger('shown.bs.collapse')
+ if (this._parent) {
+ actives = $.makeArray($(Collapse._Selector.ACTIVES))
+ if (!actives.length) {
+ actives = null
}
+ }
- if (!$.support.transition) return complete.call(this)
+ if (actives) {
+ activesData = $(actives).data(Collapse._DATA_KEY)
+ if (activesData && activesData._isTransitioning) {
+ return
+ }
+ }
- var scrollSize = $.camelCase(['scroll', dimension].join('-'))
+ var startEvent = $.Event(Collapse._Event.SHOW)
+ $(this._element).trigger(startEvent)
+ if (startEvent.isDefaultPrevented()) {
+ return
+ }
- this.$element
- .one('bsTransitionEnd', $.proxy(complete, this))
- .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
+ if (actives) {
+ Collapse._jQueryInterface.call($(actives), 'hide')
+ if (!activesData) {
+ $(actives).data(Collapse._DATA_KEY, null)
+ }
}
- Collapse.prototype.hide = function () {
- if (this.transitioning || !this.$element.hasClass('in')) return
+ var dimension = this._getDimension()
- var startEvent = $.Event('hide.bs.collapse')
- this.$element.trigger(startEvent)
- if (startEvent.isDefaultPrevented()) return
+ $(this._element)
+ .removeClass(Collapse._ClassName.COLLAPSE)
+ .addClass(Collapse._ClassName.COLLAPSING)
- var dimension = this.dimension()
+ this._element.style[dimension] = 0
+ this._element.setAttribute('aria-expanded', true)
- this.$element[dimension](this.$element[dimension]())[0].offsetHeight
+ if (this._trigger) {
+ $(this._trigger).removeClass(Collapse._ClassName.COLLAPSED)
+ this._trigger.setAttribute('aria-expanded', true)
+ }
- this.$element
- .addClass('collapsing')
- .removeClass('collapse in')
- .attr('aria-expanded', false)
+ this['setTransitioning'](true)
- this.$trigger
- .addClass('collapsed')
- .attr('aria-expanded', false)
+ var complete = function () {
+ $(this._element)
+ .removeClass(Collapse._ClassName.COLLAPSING)
+ .addClass(Collapse._ClassName.COLLAPSE)
+ .addClass(Collapse._ClassName.IN)
- this.transitioning = 1
+ this._element.style[dimension] = ''
- var complete = function () {
- this.transitioning = 0
- this.$element
- .removeClass('collapsing')
- .addClass('collapse')
- .trigger('hidden.bs.collapse')
- }
+ this['setTransitioning'](false)
- if (!$.support.transition) return complete.call(this)
+ $(this._element).trigger(Collapse._Event.SHOWN)
+ }.bind(this)
- this.$element
- [dimension](0)
- .one('bsTransitionEnd', $.proxy(complete, this))
- .emulateTransitionEnd(Collapse.TRANSITION_DURATION)
+ if (!Bootstrap.transition) {
+ complete()
+ return
}
- Collapse.prototype.toggle = function () {
- this[this.$element.hasClass('in') ? 'hide' : 'show']()
+ var scrollSize = 'scroll' + (dimension[0].toUpperCase() + dimension.slice(1))
+
+ $(this._element)
+ .one(Bootstrap.TRANSITION_END, complete)
+ .emulateTransitionEnd(Collapse._TRANSITION_DURATION)
+
+ this._element.style[dimension] = this._element[scrollSize] + 'px'
+}
+
+
+/**
+ * Hides's the collapsing element
+ */
+Collapse.prototype['hide'] = function () {
+ if (this._isTransitioning || !$(this._element).hasClass(Collapse._ClassName.IN)) {
+ return
}
- Collapse.prototype.getParent = function () {
- return $(this.options.parent)
- .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
- .each($.proxy(function (i, element) {
- var $element = $(element)
- this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)
- }, this))
- .end()
+ var startEvent = $.Event(Collapse._Event.HIDE)
+ $(this._element).trigger(startEvent)
+ if (startEvent.isDefaultPrevented()) return
+
+ var dimension = this._getDimension()
+ var offsetDimension = dimension === Collapse._Dimension.WIDTH ?
+ 'offsetWidth' : 'offsetHeight'
+
+ this._element.style[dimension] = this._element[offsetDimension] + 'px'
+
+ Bootstrap.reflow(this._element)
+
+ $(this._element)
+ .addClass(Collapse._ClassName.COLLAPSING)
+ .removeClass(Collapse._ClassName.COLLAPSE)
+ .removeClass(Collapse._ClassName.IN)
+
+ this._element.setAttribute('aria-expanded', false)
+
+ if (this._trigger) {
+ $(this._trigger).addClass(Collapse._ClassName.COLLAPSED)
+ this._trigger.setAttribute('aria-expanded', false)
}
- Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
- var isOpen = $element.hasClass('in')
+ this['setTransitioning'](true)
+
+ var complete = function () {
+ this['setTransitioning'](false)
+ $(this._element)
+ .removeClass(Collapse._ClassName.COLLAPSING)
+ .addClass(Collapse._ClassName.COLLAPSE)
+ .trigger(Collapse._Event.HIDDEN)
+
+ }.bind(this)
- $element.attr('aria-expanded', isOpen)
- $trigger
- .toggleClass('collapsed', !isOpen)
- .attr('aria-expanded', isOpen)
+ this._element.style[dimension] = 0
+
+ if (!Bootstrap.transition) {
+ return complete()
}
- function getTargetFromTrigger($trigger) {
- var href
- var target = $trigger.attr('data-target')
- || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
+ $(this._element)
+ .one(Bootstrap.TRANSITION_END, complete)
+ .emulateTransitionEnd(Collapse._TRANSITION_DURATION)
+}
+
+
- return $(target)
+/**
+ * @param {boolean} isTransitioning
+ */
+Collapse.prototype['setTransitioning'] = function (isTransitioning) {
+ this._isTransitioning = isTransitioning
+}
+
+
+/**
+ * Returns the collapsing dimension
+ * @return {string}
+ * @private
+ */
+Collapse.prototype._getDimension = function () {
+ var hasWidth = $(this._element).hasClass(Collapse._Dimension.WIDTH)
+ return hasWidth ? Collapse._Dimension.WIDTH : Collapse._Dimension.HEIGHT
+}
+
+
+/**
+ * Returns the parent element
+ * @return {Element}
+ * @private
+ */
+Collapse.prototype._getParent = function () {
+ var selector = '[data-toggle="collapse"][data-parent="' + this._config['parent'] + '"]'
+ var parent = $(this._config['parent'])[0]
+ var elements = /** @type {Array.<Element>} */ ($.makeArray($(parent).find(selector)))
+
+ for (var i = 0; i < elements.length; i++) {
+ this._addAriaAndCollapsedClass(Collapse._getTargetFromElement(elements[i]), elements[i])
}
+ return parent
+}
- // COLLAPSE PLUGIN DEFINITION
- // ==========================
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.collapse')
- var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
+/**
+ * Returns the parent element
+ * @param {Element} element
+ * @param {Element} trigger
+ * @private
+ */
+Collapse.prototype._addAriaAndCollapsedClass = function (element, trigger) {
+ if (element) {
+ var isOpen = $(element).hasClass(Collapse._ClassName.IN)
+ element.setAttribute('aria-expanded', isOpen)
- if (!data && options.toggle && option == 'show') options.toggle = false
- if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
- if (typeof option == 'string') data[option]()
- })
+ if (trigger) {
+ trigger.setAttribute('aria-expanded', isOpen)
+ $(trigger).toggleClass(Collapse._ClassName.COLLAPSED, !isOpen)
+ }
}
+}
- var old = $.fn.collapse
- $.fn.collapse = Plugin
- $.fn.collapse.Constructor = Collapse
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
- // COLLAPSE NO CONFLICT
- // ====================
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Collapse._NAME] = Collapse._jQueryInterface
- $.fn.collapse.noConflict = function () {
- $.fn.collapse = old
- return this
- }
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Collapse._NAME]['Constructor'] = Collapse
- // COLLAPSE DATA-API
- // =================
- $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
- var $this = $(this)
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Collapse._NAME]['noConflict'] = function () {
+ $.fn[Collapse._NAME] = Collapse._JQUERY_NO_CONFLICT
+ return this
+}
- if (!$this.attr('data-target')) e.preventDefault()
- var $target = getTargetFromTrigger($this)
- var data = $target.data('bs.collapse')
- var option = data ? 'toggle' : $.extend({}, $this.data(), { trigger: this })
+/**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
- Plugin.call($target, option)
- })
+$(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (event) {
+ event.preventDefault()
+
+ var target = Collapse._getTargetFromElement(this)
+
+ var data = $(target).data(Collapse._DATA_KEY)
+ var config = data ? 'toggle' : $.extend({}, $(this).data(), { trigger: this })
-}(jQuery);
+ Collapse._jQueryInterface.call($(target), config)
+})
diff --git a/js/dropdown.js b/js/dropdown.js
index 200e1c67b4..4599d5ba92 100644
--- a/js/dropdown.js
+++ b/js/dropdown.js
@@ -1,161 +1,322 @@
-/* ========================================================================
- * Bootstrap: dropdown.js v3.3.2
- * http://getbootstrap.com/javascript/#dropdowns
+/** =======================================================================
+ * Bootstrap: dropdown.js v4.0.0
+ * http://getbootstrap.com/javascript/#dropdown
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
+ * ========================================================================
+ * @fileoverview - Add dropdown menus to nearly anything with this simple
+ * plugin, including the navbar, tabs, and pills.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.dropdown
+ * + $.dropdown.noConflict
+ * + $.dropdown.Constructor
+ * + $.dropdown.Constructor.VERSION
+ * + $.dropdown.Constructor.prototype.toggle
+ *
+ * ========================================================================
+ */
+
+'use strict';
+
+
+/**
+ * Our dropdown class.
+ * @param {Element!} element
+ * @constructor
+ */
+var Dropdown = function (element) {
+ $(element).on('click.bs.dropdown', this['toggle'])
+}
+
+
+/**
+ * @const
+ * @type {string}
+ */
+Dropdown['VERSION'] = '4.0.0'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Dropdown._NAME = 'dropdown'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Dropdown._DATA_KEY = 'bs.dropdown'
+
+
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+Dropdown._JQUERY_NO_CONFLICT = $.fn[Dropdown._NAME]
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Dropdown._Event = {
+ HIDE : 'hide.bs.dropdown',
+ HIDDEN : 'hidden.bs.dropdown',
+ SHOW : 'show.bs.dropdown',
+ SHOWN : 'shown.bs.dropdown'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Dropdown._ClassName = {
+ BACKDROP : 'dropdown-backdrop',
+ DISABLED : 'disabled',
+ OPEN : 'open'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Dropdown._Selector = {
+ BACKDROP : '.dropdown-backdrop',
+ DATA_TOGGLE : '[data-toggle="dropdown"]',
+ FORM_CHILD : '.dropdown form',
+ ROLE_MENU : '[role="menu"]',
+ ROLE_LISTBOX : '[role="listbox"]',
+ NAVBAR_NAV : '.navbar-nav',
+ VISIBLE_ITEMS : '[role="menu"] li:not(.divider) a, [role="listbox"] li:not(.divider) a'
+}
+
+
+/**
+ * Provides the jQuery Interface for the alert component.
+ * @param {string=} opt_config
+ * @this {jQuery}
+ * @return {jQuery}
+ * @private
+ */
+Dropdown._jQueryInterface = function (opt_config) {
+ return this.each(function () {
+ var data = $(this).data(Dropdown._DATA_KEY)
+
+ if (!data) {
+ $(this).data(Dropdown._DATA_KEY, (data = new Dropdown(this)))
+ }
-+function ($) {
- 'use strict';
+ if (typeof opt_config === 'string') {
+ data[opt_config].call(this)
+ }
+ })
+}
- // DROPDOWN CLASS DEFINITION
- // =========================
- var backdrop = '.dropdown-backdrop'
- var toggle = '[data-toggle="dropdown"]'
- var Dropdown = function (element) {
- $(element).on('click.bs.dropdown', this.toggle)
+/**
+ * @param {Event=} opt_event
+ * @private
+ */
+Dropdown._clearMenus = function (opt_event) {
+ if (opt_event && opt_event.which == 3) {
+ return
}
- Dropdown.VERSION = '3.3.2'
-
- Dropdown.prototype.toggle = function (e) {
- var $this = $(this)
+ var backdrop = $(Dropdown._Selector.BACKDROP)[0]
+ if (backdrop) {
+ backdrop.parentNode.removeChild(backdrop)
+ }
- if ($this.is('.disabled, :disabled')) return
+ var toggles = /** @type {Array.<Element>} */ ($.makeArray($(Dropdown._Selector.DATA_TOGGLE)))
- var $parent = getParent($this)
- var isActive = $parent.hasClass('open')
+ for (var i = 0; i < toggles.length; i++) {
+ var parent = Dropdown._getParentFromElement(toggles[i])
+ var relatedTarget = { 'relatedTarget': toggles[i] }
- clearMenus()
+ if (!$(parent).hasClass(Dropdown._ClassName.OPEN)) {
+ continue
+ }
- if (!isActive) {
- if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
- // if mobile we use a backdrop because click events don't delegate
- $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
- }
+ var hideEvent = $.Event(Dropdown._Event.HIDE, relatedTarget)
+ $(parent).trigger(hideEvent)
+ if (hideEvent.isDefaultPrevented()) {
+ continue
+ }
- var relatedTarget = { relatedTarget: this }
- $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
+ toggles[i].setAttribute('aria-expanded', 'false')
- if (e.isDefaultPrevented()) return
+ $(parent)
+ .removeClass(Dropdown._ClassName.OPEN)
+ .trigger(Dropdown._Event.HIDDEN, relatedTarget)
+ }
+}
- $this
- .trigger('focus')
- .attr('aria-expanded', 'true')
- $parent
- .toggleClass('open')
- .trigger('shown.bs.dropdown', relatedTarget)
- }
+/**
+ * @param {Element} element
+ * @return {Element}
+ * @private
+ */
+Dropdown._getParentFromElement = function (element) {
+ var selector = Bootstrap.getSelectorFromElement(element)
- return false
+ if (selector) {
+ var parent = $(selector)[0]
}
- Dropdown.prototype.keydown = function (e) {
- if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return
+ return /** @type {Element} */ (parent || element.parentNode)
+}
+
- var $this = $(this)
+/**
+ * @param {Event} event
+ * @this {Element}
+ * @private
+ */
+Dropdown._dataApiKeydownHandler = function (event) {
+ if (!/(38|40|27|32)/.test(event.which) || /input|textarea/i.test(event.target.tagName)) {
+ return
+ }
- e.preventDefault()
- e.stopPropagation()
+ event.preventDefault()
+ event.stopPropagation()
- if ($this.is('.disabled, :disabled')) return
+ if (this.disabled || $(this).hasClass(Dropdown._ClassName.DISABLED)) {
+ return
+ }
- var $parent = getParent($this)
- var isActive = $parent.hasClass('open')
+ var parent = Dropdown._getParentFromElement(this)
+ var isActive = $(parent).hasClass(Dropdown._ClassName.OPEN)
- if ((!isActive && e.which != 27) || (isActive && e.which == 27)) {
- if (e.which == 27) $parent.find(toggle).trigger('focus')
- return $this.trigger('click')
+ if ((!isActive && event.which != 27) || (isActive && event.which == 27)) {
+ if (event.which == 27) {
+ var toggle = $(parent).find(Dropdown._Selector.DATA_TOGGLE)[0]
+ $(toggle).trigger('focus')
}
+ $(this).trigger('click')
+ return
+ }
- var desc = ' li:not(.divider):visible a'
- var $items = $parent.find('[role="menu"]' + desc + ', [role="listbox"]' + desc)
+ var items = $.makeArray($(Dropdown._Selector.VISIBLE_ITEMS))
- if (!$items.length) return
+ items = items.filter(function (item) {
+ return item.offsetWidth || item.offsetHeight
+ })
- var index = $items.index(e.target)
+ if (!items.length) {
+ return
+ }
- if (e.which == 38 && index > 0) index-- // up
- if (e.which == 40 && index < $items.length - 1) index++ // down
- if (!~index) index = 0
+ var index = items.indexOf(event.target)
- $items.eq(index).trigger('focus')
- }
+ if (event.which == 38 && index > 0) index-- // up
+ if (event.which == 40 && index < items.length - 1) index++ // down
+ if (!~index) index = 0
+
+ items[index].focus()
+}
- function clearMenus(e) {
- if (e && e.which === 3) return
- $(backdrop).remove()
- $(toggle).each(function () {
- var $this = $(this)
- var $parent = getParent($this)
- var relatedTarget = { relatedTarget: this }
- if (!$parent.hasClass('open')) return
+/**
+ * Toggles the dropdown
+ * @this {Element}
+ * @return {boolean|undefined}
+ */
+Dropdown.prototype['toggle'] = function () {
+ if (this.disabled || $(this).hasClass(Dropdown._ClassName.DISABLED)) {
+ return
+ }
- $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
+ var parent = Dropdown._getParentFromElement(this)
+ var isActive = $(parent).hasClass(Dropdown._ClassName.OPEN)
- if (e.isDefaultPrevented()) return
+ Dropdown._clearMenus()
- $this.attr('aria-expanded', 'false')
- $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
- })
+ if (isActive) {
+ return false
}
- function getParent($this) {
- var selector = $this.attr('data-target')
+ if ('ontouchstart' in document.documentElement && !$(parent).closest(Dropdown._Selector.NAVBAR_NAV).length) {
+ // if mobile we use a backdrop because click events don't delegate
+ var dropdown = document.createElement('div')
+ dropdown.className = Dropdown._ClassName.BACKDROP
+ this.parentNode.insertBefore(this, dropdown)
+ $(dropdown).on('click', Dropdown._clearMenus)
+ }
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
- }
+ var relatedTarget = { 'relatedTarget': this }
+ var showEvent = $.Event(Dropdown._Event.SHOW, relatedTarget)
- var $parent = selector && $(selector)
+ $(parent).trigger(showEvent)
- return $parent && $parent.length ? $parent : $this.parent()
+ if (showEvent.isDefaultPrevented()) {
+ return
}
+ this.focus()
+ this.setAttribute('aria-expanded', 'true')
- // DROPDOWN PLUGIN DEFINITION
- // ==========================
+ $(parent).toggleClass(Dropdown._ClassName.OPEN)
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.dropdown')
+ $(parent).trigger(Dropdown._Event.SHOWN, relatedTarget)
- if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
- if (typeof option == 'string') data[option].call($this)
- })
- }
+ return false
+}
- var old = $.fn.dropdown
- $.fn.dropdown = Plugin
- $.fn.dropdown.Constructor = Dropdown
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Dropdown._NAME] = Dropdown._jQueryInterface
- // DROPDOWN NO CONFLICT
- // ====================
- $.fn.dropdown.noConflict = function () {
- $.fn.dropdown = old
- return this
- }
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Dropdown._NAME]['Constructor'] = Dropdown
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Dropdown._NAME]['noConflict'] = function () {
+ $.fn[Dropdown._NAME] = Dropdown._JQUERY_NO_CONFLICT
+ return this
+}
- // APPLY TO STANDARD DROPDOWN ELEMENTS
- // ===================================
- $(document)
- .on('click.bs.dropdown.data-api', clearMenus)
- .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
- .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
- .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
- .on('keydown.bs.dropdown.data-api', '[role="menu"]', Dropdown.prototype.keydown)
- .on('keydown.bs.dropdown.data-api', '[role="listbox"]', Dropdown.prototype.keydown)
+/**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
-}(jQuery);
+$(document)
+ .on('click.bs.dropdown.data-api', Dropdown._clearMenus)
+ .on('click.bs.dropdown.data-api', Dropdown._Selector.FORM_CHILD, function (e) { e.stopPropagation() })
+ .on('click.bs.dropdown.data-api', Dropdown._Selector.DATA_TOGGLE, Dropdown.prototype['toggle'])
+ .on('keydown.bs.dropdown.data-api', Dropdown._Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler)
+ .on('keydown.bs.dropdown.data-api', Dropdown._Selector.ROLE_MENU, Dropdown._dataApiKeydownHandler)
+ .on('keydown.bs.dropdown.data-api', Dropdown._Selector.ROLE_LISTBOX, Dropdown._dataApiKeydownHandler)
diff --git a/js/externs/bootstrap.js b/js/externs/bootstrap.js
new file mode 100644
index 0000000000..b4c2e36c28
--- /dev/null
+++ b/js/externs/bootstrap.js
@@ -0,0 +1,6 @@
+jQuery.event.prototype.handleObj = function () {}
+jQuery.event.prototype.handleObj.handler = function () {}
+$.event.special = function () {}
+$.event.trueHover = true
+$.offset = {}
+$.offset.setOffset = function (a,b,c) {}
diff --git a/js/externs/jQuery.js b/js/externs/jQuery.js
new file mode 100644
index 0000000000..f9b80c497d
--- /dev/null
+++ b/js/externs/jQuery.js
@@ -0,0 +1,2159 @@
+/*
+ * Copyright 2011 The Closure Compiler Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Externs for jQuery 1.9.1
+ *
+ * Note that some functions use different return types depending on the number
+ * of parameters passed in. In these cases, you may need to annotate the type
+ * of the result in your code, so the JSCompiler understands which type you're
+ * expecting. For example:
+ * <code>var elt = /** @type {Element} * / (foo.get(0));</code>
+ *
+ * @see http://api.jquery.com/
+ * @externs
+ */
+
+/**
+ * @typedef {(Window|Document|Element|Array.<Element>|string|jQuery|
+ * NodeList)}
+ */
+var jQuerySelector;
+
+/** @typedef {function(...)|Array.<function(...)>} */
+var jQueryCallback;
+
+/** @typedef {
+ {
+ accepts: (Object.<string, string>|undefined),
+ async: (?boolean|undefined),
+ beforeSend: (function(jQuery.jqXHR, (jQueryAjaxSettings|Object.<string, *>))|undefined),
+ cache: (?boolean|undefined),
+ complete: (function(jQuery.jqXHR, string)|undefined),
+ contents: (Object.<string, RegExp>|undefined),
+ contentType: (?string|undefined),
+ context: (Object.<?, ?>|jQueryAjaxSettings|undefined),
+ converters: (Object.<string, Function>|undefined),
+ crossDomain: (?boolean|undefined),
+ data: (Object.<?, ?>|?string|Array.<?>|undefined),
+ dataFilter: (function(string, string):?|undefined),
+ dataType: (?string|undefined),
+ error: (function(jQuery.jqXHR, string, string)|undefined),
+ global: (?boolean|undefined),
+ headers: (Object.<?, ?>|undefined),
+ ifModified: (?boolean|undefined),
+ isLocal: (?boolean|undefined),
+ jsonp: (?string|undefined),
+ jsonpCallback: (?string|function()|undefined),
+ mimeType: (?string|undefined),
+ password: (?string|undefined),
+ processData: (?boolean|undefined),
+ scriptCharset: (?string|undefined),
+ statusCode: (Object.<number, function()>|undefined),
+ success: (function(?, string, jQuery.jqXHR)|undefined),
+ timeout: (?number|undefined),
+ traditional: (?boolean|undefined),
+ type: (?string|undefined),
+ url: (?string|undefined),
+ username: (?string|undefined),
+ xhr: (function():(ActiveXObject|XMLHttpRequest)|undefined),
+ xhrFields: (Object.<?, ?>|undefined)
+ }} */
+var jQueryAjaxSettings;
+
+/**
+ * @constructor
+ * @param {(jQuerySelector|Element|Object|Array.<Element>|jQuery|string|
+ * function())=} arg1
+ * @param {(Element|jQuery|Document|
+ * Object.<string, (string|function(!jQuery.event=))>)=} arg2
+ * @return {!jQuery}
+ */
+function jQuery(arg1, arg2) {}
+
+/**
+ * @constructor
+ * @extends {jQuery}
+ * @param {(jQuerySelector|Element|Object|Array.<Element>|jQuery|string|
+ * function())=} arg1
+ * @param {(Element|jQuery|Document|
+ * Object.<string, (string|function(!jQuery.event=))>)=} arg2
+ * @return {!jQuery}
+ */
+function $(arg1, arg2) {}
+
+/**
+ * @param {(jQuerySelector|Array.<Element>|string|jQuery)} arg1
+ * @param {Element=} context
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.add = function(arg1, context) {};
+
+/**
+ * @param {(jQuerySelector|Array.<Element>|string|jQuery)=} arg1
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.addBack = function(arg1) {};
+
+/**
+ * @param {(string|function(number,String))} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.addClass = function(arg1) {};
+
+/**
+ * @param {(string|Element|jQuery|function(number))} arg1
+ * @param {(string|Element|Array.<Element>|jQuery)=} content
+ * @return {!jQuery}
+ */
+jQuery.prototype.after = function(arg1, content) {};
+
+/**
+ * @param {(string|jQueryAjaxSettings|Object.<string,*>)} arg1
+ * @param {(jQueryAjaxSettings|Object.<string, *>)=} settings
+ * @return {jQuery.jqXHR}
+ */
+jQuery.ajax = function(arg1, settings) {};
+
+/**
+ * @param {(string|jQueryAjaxSettings|Object.<string, *>)} arg1
+ * @param {(jQueryAjaxSettings|Object.<string, *>)=} settings
+ * @return {jQuery.jqXHR}
+ */
+$.ajax = function(arg1, settings) {};
+
+/**
+ * @param {function(!jQuery.event,XMLHttpRequest,(jQueryAjaxSettings|Object.<string, *>))} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ajaxComplete = function(handler) {};
+
+/**
+ * @param {function(!jQuery.event,jQuery.jqXHR,(jQueryAjaxSettings|Object.<string, *>),*)} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ajaxError = function(handler) {};
+
+/**
+ * @param {(string|function((jQueryAjaxSettings|Object.<string, *>),(jQueryAjaxSettings|Object.<string, *>),jQuery.jqXHR))} dataTypes
+ * @param {function((jQueryAjaxSettings|Object.<string, *>),(jQueryAjaxSettings|Object.<string, *>),jQuery.jqXHR)=} handler
+ */
+jQuery.ajaxPrefilter = function(dataTypes, handler) {};
+
+/**
+ * @param {(string|function((jQueryAjaxSettings|Object.<string, *>),(jQueryAjaxSettings|Object.<string, *>),jQuery.jqXHR))} dataTypes
+ * @param {function((jQueryAjaxSettings|Object.<string, *>),(jQueryAjaxSettings|Object.<string, *>),jQuery.jqXHR)=} handler
+ */
+$.ajaxPrefilter = function(dataTypes, handler) {};
+
+/**
+ * @param {function(!jQuery.event,jQuery.jqXHR,(jQueryAjaxSettings|Object.<string, *>))} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ajaxSend = function(handler) {};
+
+/** @const {jQueryAjaxSettings|Object.<string, *>} */
+jQuery.ajaxSettings;
+
+/** @const {jQueryAjaxSettings|Object.<string, *>} */
+$.ajaxSettings = {};
+
+/** @type {Object.<string, boolean>} */
+jQuery.ajaxSettings.flatOptions = {};
+
+/** @type {Object.<string, boolean>} */
+$.ajaxSettings.flatOptions = {};
+
+/** @type {boolean} */
+jQuery.ajaxSettings.processData;
+
+/** @type {boolean} */
+$.ajaxSettings.processData;
+
+/** @type {Object.<string, string>} */
+jQuery.ajaxSettings.responseFields = {};
+
+/** @type {Object.<string, string>} */
+$.ajaxSettings.responseFields = {};
+
+/** @param {jQueryAjaxSettings|Object.<string, *>} options */
+jQuery.ajaxSetup = function(options) {};
+
+/** @param {jQueryAjaxSettings|Object.<string, *>} options */
+$.ajaxSetup = function(options) {};
+
+/**
+ * @param {function()} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ajaxStart = function(handler) {};
+
+/**
+ * @param {function()} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ajaxStop = function(handler) {};
+
+/**
+ * @param {function(!jQuery.event,XMLHttpRequest,(jQueryAjaxSettings|Object.<string, *>), ?)} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ajaxSuccess = function(handler) {};
+
+/**
+ * @deprecated Please use .addBack(selector) instead.
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.andSelf = function() {};
+
+/**
+ * @param {Object.<string,*>} properties
+ * @param {(string|number|function()|Object.<string,*>)=} arg2
+ * @param {(string|function())=} easing
+ * @param {function()=} complete
+ * @return {!jQuery}
+ */
+jQuery.prototype.animate = function(properties, arg2, easing, complete) {};
+
+/**
+ * @param {(string|Element|Array.<Element>|jQuery|function(number,string))} arg1
+ * @param {...(string|Element|Array.<Element>|jQuery)} content
+ * @return {!jQuery}
+ */
+jQuery.prototype.append = function(arg1, content) {};
+
+/**
+ * @param {(jQuerySelector|Element|jQuery)} target
+ * @return {!jQuery}
+ */
+jQuery.prototype.appendTo = function(target) {};
+
+/**
+ * @param {(string|Object.<string,*>)} arg1
+ * @param {(string|number|boolean|function(number,string))=} arg2
+ * @return {(string|!jQuery)}
+ */
+jQuery.prototype.attr = function(arg1, arg2) {};
+
+/**
+ * @param {(string|Element|jQuery|function())} arg1
+ * @param {(string|Element|Array.<Element>|jQuery)=} content
+ * @return {!jQuery}
+ */
+jQuery.prototype.before = function(arg1, content) {};
+
+/**
+ * @param {(string|Object.<string, function(!jQuery.event=)>)} arg1
+ * @param {(Object.<string, *>|function(!jQuery.event=)|boolean)=} eventData
+ * @param {(function(!jQuery.event=)|boolean)=} arg3
+ * @return {!jQuery}
+ */
+jQuery.prototype.bind = function(arg1, eventData, arg3) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.blur = function(arg1, handler) {};
+
+/**
+ * @constructor
+ * @private
+ */
+jQuery.callbacks = function () {};
+
+/**
+ * @param {string=} flags
+ * @return {jQuery.callbacks}
+ */
+jQuery.Callbacks = function (flags) {};
+
+/** @param {function()} callbacks */
+jQuery.callbacks.prototype.add = function(callbacks) {};
+
+/** @return {undefined} */
+jQuery.callbacks.prototype.disable = function() {};
+
+/** @return {undefined} */
+jQuery.callbacks.prototype.empty = function() {};
+
+/** @param {...*} var_args */
+jQuery.callbacks.prototype.fire = function(var_args) {};
+
+/** @return {boolean} */
+jQuery.callbacks.prototype.fired = function() {};
+
+/** @param {...*} var_args */
+jQuery.callbacks.prototype.fireWith = function(var_args) {};
+
+/**
+ * @param {function()} callback
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.callbacks.prototype.has = function(callback) {};
+
+/** @return {undefined} */
+jQuery.callbacks.prototype.lock = function() {};
+
+/** @return {boolean} */
+jQuery.callbacks.prototype.locked = function() {};
+
+/** @param {function()} callbacks */
+jQuery.callbacks.prototype.remove = function(callbacks) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.change = function(arg1, handler) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.children = function(selector) {};
+
+/**
+ * @param {string=} queueName
+ * @return {!jQuery}
+ */
+jQuery.prototype.clearQueue = function(queueName) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.click = function(arg1, handler) {};
+
+/**
+ * @param {boolean=} withDataAndEvents
+ * @param {boolean=} deepWithDataAndEvents
+ * @return {!jQuery}
+ * @suppress {checkTypes} see https://code.google.com/p/closure-compiler/issues/detail?id=583
+ */
+jQuery.prototype.clone = function(withDataAndEvents, deepWithDataAndEvents) {};
+
+/**
+ * @param {(jQuerySelector|jQuery|Element|string)} arg1
+ * @param {Element=} context
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.closest = function(arg1, context) {};
+
+/**
+ * @param {Element} container
+ * @param {Element} contained
+ * @return {boolean}
+ */
+jQuery.contains = function(container, contained) {};
+
+/**
+ * @param {Element} container
+ * @param {Element} contained
+ * @return {boolean}
+ */
+$.contains = function(container, contained) {};
+
+/**
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.contents = function() {};
+
+/** @type {Element|Document} */
+jQuery.prototype.context;
+
+/**
+ * @param {(string|Object.<string,*>)} arg1
+ * @param {(string|number|function(number,*))=} arg2
+ * @return {(string|!jQuery)}
+ */
+jQuery.prototype.css = function(arg1, arg2) {};
+
+/** @type {Object.<string, *>} */
+jQuery.cssHooks;
+
+/** @type {Object.<string, *>} */
+$.cssHooks;
+
+/**
+ * @param {Element} elem
+ * @param {string=} key
+ * @param {*=} value
+ * @return {*}
+ */
+jQuery.data = function(elem, key, value) {};
+
+/**
+ * @param {(string|Object.<string, *>)=} arg1
+ * @param {*=} value
+ * @return {*}
+ */
+jQuery.prototype.data = function(arg1, value) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} key
+ * @param {*=} value
+ * @return {*}
+ */
+$.data = function(elem, key, value) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.dblclick = function(arg1, handler) {};
+
+/**
+ * @constructor
+ * @implements {jQuery.Promise}
+ * @param {function()=} opt_fn
+ * @see http://api.jquery.com/category/deferred-object/
+ */
+jQuery.deferred = function(opt_fn) {};
+
+/**
+ * @constructor
+ * @extends {jQuery.deferred}
+ * @param {function()=} opt_fn
+ * @return {jQuery.Deferred}
+ */
+jQuery.Deferred = function(opt_fn) {};
+
+/**
+ * @constructor
+ * @extends {jQuery.deferred}
+ * @param {function()=} opt_fn
+ * @see http://api.jquery.com/category/deferred-object/
+ */
+$.deferred = function(opt_fn) {};
+
+/**
+ * @constructor
+ * @extends {jQuery.deferred}
+ * @param {function()=} opt_fn
+ * @return {jQuery.deferred}
+ */
+$.Deferred = function(opt_fn) {};
+
+/**
+ * @override
+ * @param {jQueryCallback} alwaysCallbacks
+ * @param {jQueryCallback=} alwaysCallbacks2
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.always
+ = function(alwaysCallbacks, alwaysCallbacks2) {};
+
+/**
+ * @override
+ * @param {jQueryCallback} doneCallbacks
+ * @param {jQueryCallback=} doneCallbacks2
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.done = function(doneCallbacks, doneCallbacks2) {};
+
+/**
+ * @override
+ * @param {jQueryCallback} failCallbacks
+ * @param {jQueryCallback=} failCallbacks2
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.fail = function(failCallbacks, failCallbacks2) {};
+
+/**
+ * @param {...*} var_args
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.notify = function(var_args) {};
+
+/**
+ * @param {Object} context
+ * @param {...*} var_args
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.notifyWith = function(context, var_args) {};
+
+/**
+ * @deprecated Please use deferred.then() instead.
+ * @override
+ * @param {function()=} doneFilter
+ * @param {function()=} failFilter
+ * @param {function()=} progressFilter
+ * @return {jQuery.Promise}
+ */
+jQuery.deferred.prototype.pipe =
+ function(doneFilter, failFilter, progressFilter) {};
+
+/**
+ * @param {jQueryCallback} progressCallbacks
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.progress = function(progressCallbacks) {};
+
+/**
+ * @param {Object=} target
+ * @return {jQuery.Promise}
+ */
+jQuery.deferred.prototype.promise = function(target) {};
+
+/**
+ * @param {...*} var_args
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.reject = function(var_args) {};
+
+/**
+ * @param {Object} context
+ * @param {Array.<*>=} args
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.rejectWith = function(context, args) {};
+
+/**
+ * @param {...*} var_args
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.resolve = function(var_args) {};
+
+/**
+ * @param {Object} context
+ * @param {Array.<*>=} args
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.resolveWith = function(context, args) {};
+
+/** @return {string} */
+jQuery.deferred.prototype.state = function() {};
+
+/**
+ * @override
+ * @param {jQueryCallback} doneCallbacks
+ * @param {jQueryCallback=} failCallbacks
+ * @param {jQueryCallback=} progressCallbacks
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.then
+ = function(doneCallbacks, failCallbacks, progressCallbacks) {};
+
+/**
+ * @param {number} duration
+ * @param {string=} queueName
+ * @return {!jQuery}
+ */
+jQuery.prototype.delay = function(duration, queueName) {};
+
+/**
+ * @param {string} selector
+ * @param {(string|Object.<string,*>)} arg2
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg3
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.delegate = function(selector, arg2, arg3, handler) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} queueName
+ */
+jQuery.dequeue = function(elem, queueName) {};
+
+/**
+ * @param {string=} queueName
+ * @return {!jQuery}
+ */
+jQuery.prototype.dequeue = function(queueName) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} queueName
+ */
+$.dequeue = function(elem, queueName) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ */
+jQuery.prototype.detach = function(selector) {};
+
+/**
+ * @param {Object} collection
+ * @param {function((number|string),?)} callback
+ * @return {Object}
+ */
+jQuery.each = function(collection, callback) {};
+
+/**
+ * @param {function(number,Element)} fnc
+ * @return {!jQuery}
+ */
+jQuery.prototype.each = function(fnc) {};
+
+/**
+ * @param {Object} collection
+ * @param {function((number|string),?)} callback
+ * @return {Object}
+ */
+$.each = function(collection, callback) {};
+
+/** @return {!jQuery} */
+jQuery.prototype.empty = function() {};
+
+/**
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.end = function() {};
+
+/**
+ * @param {number} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.eq = function(arg1) {};
+
+/** @param {string} message */
+jQuery.error = function(message) {};
+
+/**
+ * @deprecated Please use .on( "error", handler ) instead.
+ * @param {(function(!jQuery.event=)|Object.<string, *>)} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.error = function(arg1, handler) {};
+
+/** @param {string} message */
+$.error = function(message) {};
+
+/**
+ * @constructor
+ * @param {string} eventType
+ */
+jQuery.event = function(eventType) {};
+
+/**
+ * @constructor
+ * @extends {jQuery.event}
+ * @param {string} eventType
+ * @param {Object=} properties
+ * @return {jQuery.Event}
+ */
+jQuery.Event = function(eventType, properties) {};
+
+/**
+ * @constructor
+ * @extends {jQuery.event}
+ * @param {string} eventType
+ */
+$.event = function(eventType) {};
+
+/**
+ * @constructor
+ * @extends {jQuery.event}
+ * @param {string} eventType
+ * @param {Object=} properties
+ * @return {$.Event}
+ */
+$.Event = function(eventType, properties) {};
+
+/** @type {Element} */
+jQuery.event.prototype.currentTarget;
+
+/** @type {Object.<string, *>} */
+jQuery.event.prototype.data;
+
+/** @type {Element} */
+jQuery.event.prototype.delegateTarget;
+
+/**
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.event.prototype.isDefaultPrevented = function() {};
+
+/**
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.event.prototype.isImmediatePropagationStopped = function() {};
+
+/**
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.event.prototype.isPropagationStopped = function() {};
+
+/** @type {string} */
+jQuery.event.prototype.namespace;
+
+/** @type {Event} */
+jQuery.event.prototype.originalEvent;
+
+/** @type {number} */
+jQuery.event.prototype.pageX;
+
+/** @type {number} */
+jQuery.event.prototype.pageY;
+
+/** @return {undefined} */
+jQuery.event.prototype.preventDefault = function() {};
+
+/** @type {Object.<string, *>} */
+jQuery.event.prototype.props;
+
+/** @type {Element} */
+jQuery.event.prototype.relatedTarget;
+
+/** @type {*} */
+jQuery.event.prototype.result;
+
+/** @return {undefined} */
+jQuery.event.prototype.stopImmediatePropagation = function() {};
+
+/** @return {undefined} */
+jQuery.event.prototype.stopPropagation = function() {};
+
+/** @type {Element} */
+jQuery.event.prototype.target;
+
+/** @type {number} */
+jQuery.event.prototype.timeStamp;
+
+/** @type {string} */
+jQuery.event.prototype.type;
+
+/** @type {number} */
+jQuery.event.prototype.which;
+
+/**
+ * @param {(Object|boolean)} arg1
+ * @param {...*} var_args
+ * @return {Object}
+ */
+jQuery.extend = function(arg1, var_args) {};
+
+/**
+ * @param {(Object|boolean)} arg1
+ * @param {...*} var_args
+ * @return {Object}
+ */
+jQuery.prototype.extend = function(arg1, var_args) {};
+
+/**
+ * @param {(Object|boolean)} arg1
+ * @param {...*} var_args
+ * @return {Object}
+ */
+$.extend = function(arg1, var_args) {};
+
+/**
+ * @param {(string|number|function())=} duration
+ * @param {(function()|string)=} arg2
+ * @param {function()=} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.fadeIn = function(duration, arg2, callback) {};
+
+/**
+ * @param {(string|number|function())=} duration
+ * @param {(function()|string)=} arg2
+ * @param {function()=} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.fadeOut = function(duration, arg2, callback) {};
+
+/**
+ * @param {(string|number)} duration
+ * @param {number} opacity
+ * @param {(function()|string)=} arg3
+ * @param {function()=} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.fadeTo = function(duration, opacity, arg3, callback) {};
+
+/**
+ * @param {(string|number|function())=} duration
+ * @param {(string|function())=} easing
+ * @param {function()=} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.fadeToggle = function(duration, easing, callback) {};
+
+/**
+ * @param {(jQuerySelector|function(number)|Element|jQuery)} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.filter = function(arg1) {};
+
+/**
+ * @param {(jQuerySelector|jQuery|Element)} arg1
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.find = function(arg1) {};
+
+/** @return {!jQuery} */
+jQuery.prototype.first = function() {};
+
+/** @see http://docs.jquery.com/Plugins/Authoring */
+jQuery.fn = jQuery.prototype;
+
+/** @see http://docs.jquery.com/Plugins/Authoring */
+$.fn = $.prototype;
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.focus = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.focusin = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.focusout = function(arg1, handler) {};
+
+/** @const */
+jQuery.fx = {};
+
+/** @const */
+$.fx = {};
+
+/** @type {number} */
+jQuery.fx.interval;
+
+/** @type {number} */
+$.fx.interval;
+
+/** @type {boolean} */
+jQuery.fx.off;
+
+/** @type {boolean} */
+$.fx.off;
+
+/**
+ * @param {string} url
+ * @param {(Object.<string,*>|string|
+ * function(string,string,jQuery.jqXHR))=} data
+ * @param {(function(string,string,jQuery.jqXHR)|string)=} success
+ * @param {string=} dataType
+ * @return {jQuery.jqXHR}
+ */
+jQuery.get = function(url, data, success, dataType) {};
+
+/**
+ * @param {number=} index
+ * @return {(Element|Array.<Element>)}
+ * @nosideeffects
+ */
+jQuery.prototype.get = function(index) {};
+
+/**
+ * @param {string} url
+ * @param {(Object.<string,*>|string|
+ * function(string,string,jQuery.jqXHR))=} data
+ * @param {(function(string,string,jQuery.jqXHR)|string)=} success
+ * @param {string=} dataType
+ * @return {jQuery.jqXHR}
+ */
+$.get = function(url, data, success, dataType) {};
+
+/**
+ * @param {string} url
+ * @param {(Object.<string,*>|
+ * function(Object.<string,*>,string,jQuery.jqXHR))=} data
+ * @param {function(Object.<string,*>,string,jQuery.jqXHR)=} success
+ * @return {jQuery.jqXHR}
+ * @see http://api.jquery.com/jquery.getjson/#jQuery-getJSON-url-data-success
+ */
+jQuery.getJSON = function(url, data, success) {};
+
+/**
+ * @param {string} url
+ * @param {(Object.<string,*>|
+ * function(Object.<string,*>,string,jQuery.jqXHR))=} data
+ * @param {function(Object.<string,*>,string,jQuery.jqXHR)=} success
+ * @return {jQuery.jqXHR}
+ * @see http://api.jquery.com/jquery.getjson/#jQuery-getJSON-url-data-success
+ */
+$.getJSON = function(url, data, success) {};
+
+/**
+ * @param {string} url
+ * @param {function(Node,string,jQuery.jqXHR)=} success
+ * @return {jQuery.jqXHR}
+ */
+jQuery.getScript = function(url, success) {};
+
+/**
+ * @param {string} url
+ * @param {function(Node,string,jQuery.jqXHR)=} success
+ * @return {jQuery.jqXHR}
+ */
+$.getScript = function(url, success) {};
+
+/** @param {string} code */
+jQuery.globalEval = function(code) {};
+
+/** @param {string} code */
+$.globalEval = function(code) {};
+
+/**
+ * @param {Array.<*>} arr
+ * @param {function(*,number)} fnc
+ * @param {boolean=} invert
+ * @return {Array.<*>}
+ */
+jQuery.grep = function(arr, fnc, invert) {};
+
+/**
+ * @param {Array.<*>} arr
+ * @param {function(*,number)} fnc
+ * @param {boolean=} invert
+ * @return {Array.<*>}
+ */
+$.grep = function(arr, fnc, invert) {};
+
+/**
+ * @param {(string|Element)} arg1
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.has = function(arg1) {};
+
+/**
+ * @param {string} className
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.prototype.hasClass = function(className) {};
+
+/**
+ * @param {Element} elem
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.hasData = function(elem) {};
+
+/**
+ * @param {Element} elem
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.hasData = function(elem) {};
+
+/**
+ * @param {(string|number|function(number,number))=} arg1
+ * @return {(number|!jQuery)}
+ */
+jQuery.prototype.height = function(arg1) {};
+
+/**
+ * @param {(string|number|function())=} duration
+ * @param {(function()|string)=} arg2
+ * @param {function()=} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.hide = function(duration, arg2, callback) {};
+
+/** @param {boolean} hold */
+jQuery.holdReady = function(hold) {};
+
+/** @param {boolean} hold */
+$.holdReady = function(hold) {};
+
+/**
+ * @param {function(!jQuery.event=)} arg1
+ * @param {function(!jQuery.event=)=} handlerOut
+ * @return {!jQuery}
+ */
+jQuery.prototype.hover = function(arg1, handlerOut) {};
+
+/**
+ * @param {(string|function(number,string))=} arg1
+ * @return {(string|!jQuery)}
+ */
+jQuery.prototype.html = function(arg1) {};
+
+/**
+ * @param {*} value
+ * @param {Array.<*>} arr
+ * @param {number=} fromIndex
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.inArray = function(value, arr, fromIndex) {};
+
+/**
+ * @param {*} value
+ * @param {Array.<*>} arr
+ * @param {number=} fromIndex
+ * @return {number}
+ * @nosideeffects
+ */
+$.inArray = function(value, arr, fromIndex) {};
+
+/**
+ * @param {(jQuerySelector|Element|jQuery)=} arg1
+ * @return {number}
+ */
+jQuery.prototype.index = function(arg1) {};
+
+/**
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.prototype.innerHeight = function() {};
+
+/**
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.prototype.innerWidth = function() {};
+
+/**
+ * @param {(jQuerySelector|Element|jQuery)} target
+ * @return {!jQuery}
+ */
+jQuery.prototype.insertAfter = function(target) {};
+
+/**
+ * @param {(jQuerySelector|Element|jQuery)} target
+ * @return {!jQuery}
+ */
+jQuery.prototype.insertBefore = function(target) {};
+
+/**
+ * @param {(jQuerySelector|function(number)|jQuery|Element)} arg1
+ * @return {boolean}
+ */
+jQuery.prototype.is = function(arg1) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isArray = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isArray = function(obj) {};
+
+/**
+ * @param {Object} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isEmptyObject = function(obj) {};
+
+/**
+ * @param {Object} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isEmptyObject = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isFunction = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isFunction = function(obj) {};
+
+/**
+ * @param {*} value
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isNumeric = function(value) {};
+
+/**
+ * @param {*} value
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isNumeric = function(value) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isPlainObject = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isPlainObject = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isWindow = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isWindow = function(obj) {};
+
+/**
+ * @param {Element} node
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isXMLDoc = function(node) {};
+
+/**
+ * @param {Element} node
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isXMLDoc = function(node) {};
+
+/** @type {string} */
+jQuery.prototype.jquery;
+
+/**
+ * @constructor
+ * @extends {XMLHttpRequest}
+ * @implements {jQuery.Promise}
+ * @private
+ * @see http://api.jquery.com/jQuery.ajax/#jqXHR
+ */
+jQuery.jqXHR = function () {};
+
+/**
+ * @override
+ * @param {jQueryCallback} alwaysCallbacks
+ * @param {jQueryCallback=} alwaysCallbacks2
+ * @return {jQuery.jqXHR}
+ */
+jQuery.jqXHR.prototype.always =
+ function(alwaysCallbacks, alwaysCallbacks2) {};
+
+/**
+ * @deprecated
+ * @param {function()} callback
+ * @return {jQuery.jqXHR}
+*/
+jQuery.jqXHR.prototype.complete = function (callback) {};
+
+/**
+ * @override
+ * @param {jQueryCallback} doneCallbacks
+ * @return {jQuery.jqXHR}
+ */
+jQuery.jqXHR.prototype.done = function(doneCallbacks) {};
+
+/**
+ * @deprecated
+ * @param {function()} callback
+ * @return {jQuery.jqXHR}
+*/
+jQuery.jqXHR.prototype.error = function (callback) {};
+
+/**
+ * @override
+ * @param {jQueryCallback} failCallbacks
+ * @return {jQuery.jqXHR}
+ */
+jQuery.jqXHR.prototype.fail = function(failCallbacks) {};
+
+/**
+ * @deprecated
+ * @override
+ */
+jQuery.jqXHR.prototype.onreadystatechange = function (callback) {};
+
+/**
+ * @override
+ * @param {function()=} doneFilter
+ * @param {function()=} failFilter
+ * @param {function()=} progressFilter
+ * @return {jQuery.jqXHR}
+ */
+jQuery.jqXHR.prototype.pipe =
+ function(doneFilter, failFilter, progressFilter) {};
+
+/**
+ * @deprecated
+ * @param {function()} callback
+ * @return {jQuery.jqXHR}
+*/
+jQuery.jqXHR.prototype.success = function (callback) {};
+
+/**
+ * @override
+ * @param {jQueryCallback} doneCallbacks
+ * @param {jQueryCallback=} failCallbacks
+ * @param {jQueryCallback=} progressCallbacks
+ * @return {jQuery.jqXHR}
+ */
+jQuery.jqXHR.prototype.then =
+ function(doneCallbacks, failCallbacks, progressCallbacks) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.keydown = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.keypress = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.keyup = function(arg1, handler) {};
+
+/** @return {!jQuery} */
+jQuery.prototype.last = function() {};
+
+/** @type {number} */
+jQuery.prototype.length;
+
+/**
+ * @deprecated Please avoid the document loading Event invocation of
+ * .load() and use .on( "load", handler ) instead. (The AJAX
+ * module invocation signature is OK.)
+ * @param {(function(!jQuery.event=)|Object.<string, *>|string)} arg1
+ * @param {(function(!jQuery.event=)|Object.<string,*>|string)=} arg2
+ * @param {function(string,string,XMLHttpRequest)=} complete
+ * @return {!jQuery}
+ */
+jQuery.prototype.load = function(arg1, arg2, complete) {};
+
+/**
+ * @param {*} obj
+ * @return {Array.<*>}
+ */
+jQuery.makeArray = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {Array.<*>}
+ */
+$.makeArray = function(obj) {};
+
+/**
+ * @param {(Array.<*>|Object.<string, *>)} arg1
+ * @param {(function(*,number)|function(*,(string|number)))} callback
+ * @return {Array.<*>}
+ */
+jQuery.map = function(arg1, callback) {};
+
+/**
+ * @param {function(number,Element)} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.map = function(callback) {};
+
+/**
+ * @param {(Array.<*>|Object.<string, *>)} arg1
+ * @param {(function(*,number)|function(*,(string|number)))} callback
+ * @return {Array.<*>}
+ */
+$.map = function(arg1, callback) {};
+
+/**
+ * @param {Array.<*>} first
+ * @param {Array.<*>} second
+ * @return {Array.<*>}
+ */
+jQuery.merge = function(first, second) {};
+
+/**
+ * @param {Array.<*>} first
+ * @param {Array.<*>} second
+ * @return {Array.<*>}
+ */
+$.merge = function(first, second) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mousedown = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mouseenter = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mouseleave = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mousemove = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mouseout = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mouseover = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mouseup = function(arg1, handler) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.next = function(selector) {};
+
+/**
+ * @param {string=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.nextAll = function(selector) {};
+
+/**
+ * @param {(jQuerySelector|Element)=} arg1
+ * @param {jQuerySelector=} filter
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.nextUntil = function(arg1, filter) {};
+
+/**
+ * @param {boolean=} removeAll
+ * @return {Object}
+ */
+jQuery.noConflict = function(removeAll) {};
+
+/**
+ * @param {boolean=} removeAll
+ * @return {Object}
+ */
+$.noConflict = function(removeAll) {};
+
+/**
+ * @return {function()}
+ * @nosideeffects
+ */
+jQuery.noop = function() {};
+
+/**
+ * @return {function()}
+ * @nosideeffects
+ */
+$.noop = function() {};
+
+/**
+ * @param {(jQuerySelector|Array.<Element>|function(number)|jQuery)} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.not = function(arg1) {};
+
+/**
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.now = function() {};
+
+/**
+ * @return {number}
+ * @nosideeffects
+ */
+$.now = function() {};
+
+/**
+ * @param {(string|Object.<string,*>)=} arg1
+ * @param {(string|function(!jQuery.event=))=} selector
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.off = function(arg1, selector, handler) {};
+
+/**
+ * @param {({left:number,top:number}|
+ * function(number,{top:number,left:number}))=} arg1
+ * @return {({left:number,top:number}|!jQuery)}
+ */
+jQuery.prototype.offset = function(arg1) {};
+
+/**
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.offsetParent = function() {};
+
+/**
+ * @param {(string|Object.<string,*>)} arg1
+ * @param {*=} selector
+ * @param {*=} data
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.on = function(arg1, selector, data, handler) {};
+
+/**
+ * @param {(string|Object.<string,*>)} arg1
+ * @param {*=} arg2
+ * @param {*=} arg3
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.one = function(arg1, arg2, arg3, handler) {};
+
+/**
+ * @param {boolean=} includeMargin
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.prototype.outerHeight = function(includeMargin) {};
+
+/**
+ * @param {boolean=} includeMargin
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.prototype.outerWidth = function(includeMargin) {};
+
+/**
+ * @param {(Object.<string, *>|Array.<Object.<string, *>>)} obj
+ * @param {boolean=} traditional
+ * @return {string}
+ */
+jQuery.param = function(obj, traditional) {};
+
+/**
+ * @param {(Object.<string, *>|Array.<Object.<string, *>>)} obj
+ * @param {boolean=} traditional
+ * @return {string}
+ */
+$.param = function(obj, traditional) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.parent = function(selector) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.parents = function(selector) {};
+
+/**
+ * @param {(jQuerySelector|Element)=} arg1
+ * @param {jQuerySelector=} filter
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.parentsUntil = function(arg1, filter) {};
+
+/**
+ * @param {string} data
+ * @param {(Element|boolean)=} context
+ * @param {boolean=} keepScripts
+ * @return {Array.<Element>}
+ */
+jQuery.parseHTML = function(data, context, keepScripts) {};
+
+/**
+ * @param {string} data
+ * @param {(Element|boolean)=} context
+ * @param {boolean=} keepScripts
+ * @return {Array.<Element>}
+ */
+$.parseHTML = function(data, context, keepScripts) {};
+
+/**
+ * @param {string} json
+ * @return {string|number|Object.<string, *>|Array.<?>|boolean}
+ */
+jQuery.parseJSON = function(json) {};
+
+/**
+ * @param {string} json
+ * @return {Object.<string, *>}
+ */
+$.parseJSON = function(json) {};
+
+/**
+ * @param {string} data
+ * @return {Document}
+ */
+jQuery.parseXML = function(data) {};
+
+/**
+ * @param {string} data
+ * @return {Document}
+ */
+$.parseXML = function(data) {};
+
+/**
+ * @return {{left:number,top:number}}
+ * @nosideeffects
+ */
+jQuery.prototype.position = function() {};
+
+/**
+ * @param {string} url
+ * @param {(Object.<string,*>|string|
+ * function(string,string,jQuery.jqXHR))=} data
+ * @param {(function(string,string,jQuery.jqXHR)|string)=} success
+ * @param {string=} dataType
+ * @return {jQuery.jqXHR}
+ */
+jQuery.post = function(url, data, success, dataType) {};
+
+/**
+ * @param {string} url
+ * @param {(Object.<string,*>|string|
+ * function(string,string,jQuery.jqXHR))=} data
+ * @param {(function(string,string,jQuery.jqXHR)|string)=} success
+ * @param {string=} dataType
+ * @return {jQuery.jqXHR}
+ */
+$.post = function(url, data, success, dataType) {};
+
+/**
+ * @param {(string|Element|jQuery|function(number,string))} arg1
+ * @param {(string|Element|jQuery)=} content
+ * @return {!jQuery}
+ */
+jQuery.prototype.prepend = function(arg1, content) {};
+
+/**
+ * @param {(jQuerySelector|Element|jQuery)} target
+ * @return {!jQuery}
+ */
+jQuery.prototype.prependTo = function(target) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.prev = function(selector) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.prevAll = function(selector) {};
+
+/**
+ * @param {(jQuerySelector|Element)=} arg1
+ * @param {jQuerySelector=} filter
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.prevUntil = function(arg1, filter) {};
+
+/**
+ * @param {(string|Object)=} type
+ * @param {Object=} target
+ * @return {jQuery.Promise}
+ */
+jQuery.prototype.promise = function(type, target) {};
+
+/**
+ * @interface
+ * @private
+ * @see http://api.jquery.com/Types/#Promise
+ */
+jQuery.Promise = function () {};
+
+/**
+ * @param {jQueryCallback} alwaysCallbacks
+ * @param {jQueryCallback=} alwaysCallbacks2
+ * @return {jQuery.Promise}
+ */
+jQuery.Promise.prototype.always =
+ function(alwaysCallbacks, alwaysCallbacks2) {};
+
+/**
+ * @param {jQueryCallback} doneCallbacks
+ * @return {jQuery.Promise}
+ */
+jQuery.Promise.prototype.done = function(doneCallbacks) {};
+
+/**
+ * @param {jQueryCallback} failCallbacks
+ * @return {jQuery.Promise}
+ */
+jQuery.Promise.prototype.fail = function(failCallbacks) {};
+
+/**
+ * @param {function()=} doneFilter
+ * @param {function()=} failFilter
+ * @param {function()=} progressFilter
+ * @return {jQuery.Promise}
+ */
+jQuery.Promise.prototype.pipe =
+ function(doneFilter, failFilter, progressFilter) {};
+
+/**
+ * @param {jQueryCallback} doneCallbacks
+ * @param {jQueryCallback=} failCallbacks
+ * @param {jQueryCallback=} progressCallbacks
+ * @return {jQuery.Promise}
+ */
+jQuery.Promise.prototype.then =
+ function(doneCallbacks, failCallbacks, progressCallbacks) {};
+
+/**
+ * @param {(string|Object.<string,*>)} arg1
+ * @param {(string|number|boolean|function(number,String))=} arg2
+ * @return {(string|boolean|!jQuery)}
+ */
+jQuery.prototype.prop = function(arg1, arg2) {};
+
+/**
+ * @param {...*} var_args
+ * @return {function()}
+ */
+jQuery.proxy = function(var_args) {};
+
+/**
+ * @param {...*} var_args
+ * @return {function()}
+ */
+$.proxy = function(var_args) {};
+
+/**
+ * @param {Array.<Element>} elements
+ * @param {string=} name
+ * @param {Array.<*>=} args
+ * @return {!jQuery}
+ */
+jQuery.prototype.pushStack = function(elements, name, args) {};
+
+/**
+ * @param {(string|Array.<function()>|function(function()))=} queueName
+ * @param {(Array.<function()>|function(function()))=} arg2
+ * @return {(Array.<Element>|!jQuery)}
+ */
+jQuery.prototype.queue = function(queueName, arg2) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} queueName
+ * @param {(Array.<function()>|function())=} arg3
+ * @return {(Array.<Element>|!jQuery)}
+ */
+jQuery.queue = function(elem, queueName, arg3) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} queueName
+ * @param {(Array.<function()>|function())=} arg3
+ * @return {(Array.<Element>|!jQuery)}
+ */
+$.queue = function(elem, queueName, arg3) {};
+
+/**
+ * @param {function()} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ready = function(handler) {};
+
+/**
+ * @param {string=} selector
+ * @return {!jQuery}
+ */
+jQuery.prototype.remove = function(selector) {};
+
+/**
+ * @param {string} attributeName
+ * @return {!jQuery}
+ */
+jQuery.prototype.removeAttr = function(attributeName) {};
+
+/**
+ * @param {(string|function(number,string))=} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.removeClass = function(arg1) {};
+
+/**
+ * @param {(string|Array.<string>)=} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.removeData = function(arg1) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} name
+ * @return {!jQuery}
+ */
+jQuery.removeData = function(elem, name) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} name
+ * @return {!jQuery}
+ */
+$.removeData = function(elem, name) {};
+
+/**
+ * @param {string} propertyName
+ * @return {!jQuery}
+ */
+jQuery.prototype.removeProp = function(propertyName) {};
+
+/**
+ * @param {jQuerySelector} target
+ * @return {!jQuery}
+ */
+jQuery.prototype.replaceAll = function(target) {};
+
+/**
+ * @param {(string|Element|jQuery|function())} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.replaceWith = function(arg1) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.resize = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.scroll = function(arg1, handler) {};
+
+/**
+ * @param {number=} value
+ * @return {(number|!jQuery)}
+ */
+jQuery.prototype.scrollLeft = function(value) {};
+
+/**
+ * @param {number=} value
+ * @return {(number|!jQuery)}
+ */
+jQuery.prototype.scrollTop = function(value) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.select = function(arg1, handler) {};
+
+/**
+ * @return {string}
+ * @nosideeffects
+ */
+jQuery.prototype.serialize = function() {};
+
+/**
+ * @return {Array.<Object.<string, *>>}
+ * @nosideeffects
+ */
+jQuery.prototype.serializeArray = function() {};
+
+/**
+ * @param {(string|number|function())=} duration
+ * @param {(function()|string)=} arg2
+ * @param {function()=} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.show = function(duration, arg2, callback) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.siblings = function(selector) {};
+
+/**
+ * @deprecated Please use the .length property instead.
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.prototype.size = function() {};
+
+/**
+ * @param {number} start
+ * @param {number=} end
+ * @return {!jQuery}
+ */
+jQuery.prototype.slice = function(start, end) {};
+
+/**
+ * @param {(Object.<string,*>|string|number)=} optionsOrDuration
+ * @param {(function()|string)=} completeOrEasing
+ * @param {function()=} complete
+ * @return {!jQuery}
+ */
+jQuery.prototype.slideDown =
+ function(optionsOrDuration, completeOrEasing, complete) {};
+
+/**
+ * @param {(Object.<string,*>|string|number)=} optionsOrDuration
+ * @param {(function()|string)=} completeOrEasing
+ * @param {function()=} complete
+ * @return {!jQuery}
+ */
+jQuery.prototype.slideToggle =
+ function(optionsOrDuration, completeOrEasing, complete) {};
+
+/**
+ * @param {(Object.<string,*>|string|number)=} optionsOrDuration
+ * @param {(function()|string)=} completeOrEasing
+ * @param {function()=} complete
+ * @return {!jQuery}
+ */
+jQuery.prototype.slideUp =
+ function(optionsOrDuration, completeOrEasing, complete) {};
+
+/**
+ * @param {(boolean|string)=} arg1
+ * @param {boolean=} arg2
+ * @param {boolean=} jumpToEnd
+ * @return {!jQuery}
+ */
+jQuery.prototype.stop = function(arg1, arg2, jumpToEnd) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.submit = function(arg1, handler) {};
+
+/** @type {Object.<string, *>}
+ * @deprecated Please try to use feature detection instead.
+ */
+jQuery.support;
+
+/** @type {Object.<string, *>}
+ * @deprecated Please try to use feature detection instead.
+ */
+$.support;
+
+/**
+ * @deprecated Please try to use feature detection instead.
+ * @type {boolean}
+ */
+jQuery.support.boxModel;
+
+/**
+ * @deprecated Please try to use feature detection instead.
+ * @type {boolean}
+ */
+$.support.boxModel;
+
+/** @type {boolean} */
+jQuery.support.changeBubbles;
+
+/** @type {boolean} */
+$.support.changeBubbles;
+
+/** @type {boolean} */
+jQuery.support.cors;
+
+/** @type {boolean} */
+$.support.cors;
+
+/** @type {boolean} */
+jQuery.support.cssFloat;
+
+/** @type {boolean} */
+$.support.cssFloat;
+
+/** @type {boolean} */
+jQuery.support.hrefNormalized;
+
+/** @type {boolean} */
+$.support.hrefNormalized;
+
+/** @type {boolean} */
+jQuery.support.htmlSerialize;
+
+/** @type {boolean} */
+$.support.htmlSerialize;
+
+/** @type {boolean} */
+jQuery.support.leadingWhitespace;
+
+/** @type {boolean} */
+$.support.leadingWhitespace;
+
+/** @type {boolean} */
+jQuery.support.noCloneEvent;
+
+/** @type {boolean} */
+$.support.noCloneEvent;
+
+/** @type {boolean} */
+jQuery.support.opacity;
+
+/** @type {boolean} */
+$.support.opacity;
+
+/** @type {boolean} */
+jQuery.support.style;
+
+/** @type {boolean} */
+$.support.style;
+
+/** @type {boolean} */
+jQuery.support.submitBubbles;
+
+/** @type {boolean} */
+$.support.submitBubbles;
+
+/** @type {boolean} */
+jQuery.support.tbody;
+
+/** @type {boolean} */
+$.support.tbody;
+
+/**
+ * @param {(string|function(number,string))=} arg1
+ * @return {(string|!jQuery)}
+ */
+jQuery.prototype.text = function(arg1) {};
+
+/**
+ * @return {Array.<Element>}
+ * @nosideeffects
+ */
+jQuery.prototype.toArray = function() {};
+
+/**
+ * Refers to the method from the Effects category. There used to be a toggle
+ * method on the Events category which was removed starting version 1.9.
+ * @param {(number|string|Object.<string,*>|boolean)=} arg1
+ * @param {(function()|string)=} arg2
+ * @param {function()=} arg3
+ * @return {!jQuery}
+ */
+jQuery.prototype.toggle = function(arg1, arg2, arg3) {};
+
+/**
+ * @param {(string|boolean|function(number,string,boolean))=} arg1
+ * @param {boolean=} flag
+ * @return {!jQuery}
+ */
+jQuery.prototype.toggleClass = function(arg1, flag) {};
+
+/**
+ * @param {(string|jQuery.event)} arg1
+ * @param {...*} var_args
+ * @return {!jQuery}
+ */
+jQuery.prototype.trigger = function(arg1, var_args) {};
+
+/**
+ * @param {string|jQuery.event} eventType
+ * @param {Array.<*>=} extraParameters
+ * @return {*}
+ */
+jQuery.prototype.triggerHandler = function(eventType, extraParameters) {};
+
+/**
+ * @param {string} str
+ * @return {string}
+ * @nosideeffects
+ */
+jQuery.trim = function(str) {};
+
+/**
+ * @param {string} str
+ * @return {string}
+ * @nosideeffects
+ */
+$.trim = function(str) {};
+
+/**
+ * @param {*} obj
+ * @return {string}
+ * @nosideeffects
+ */
+jQuery.type = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {string}
+ * @nosideeffects
+ */
+$.type = function(obj) {};
+
+/**
+ * @param {(string|function(!jQuery.event=)|jQuery.event)=} arg1
+ * @param {(function(!jQuery.event=)|boolean)=} arg2
+ * @return {!jQuery}
+ */
+jQuery.prototype.unbind = function(arg1, arg2) {};
+
+/**
+ * @param {string=} arg1
+ * @param {(string|Object.<string,*>)=} arg2
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.undelegate = function(arg1, arg2, handler) {};
+
+/**
+ * @param {Array.<Element>} arr
+ * @return {Array.<Element>}
+ */
+jQuery.unique = function(arr) {};
+
+/**
+ * @param {Array.<Element>} arr
+ * @return {Array.<Element>}
+ */
+$.unique = function(arr) {};
+
+/**
+ * @deprecated Please use .on( "unload", handler ) instead.
+ * @param {(function(!jQuery.event=)|Object.<string, *>)} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.unload = function(arg1, handler) {};
+
+/** @return {!jQuery} */
+jQuery.prototype.unwrap = function() {};
+
+/**
+ * @param {(string|Array.<string>|function(number,*))=} arg1
+ * @return {(string|number|Array.<string>|!jQuery)}
+ */
+jQuery.prototype.val = function(arg1) {};
+
+/**
+ * Note: The official documentation (https://api.jquery.com/jQuery.when/) says
+ * jQuery.when accepts deferreds, but it actually accepts any type, e.g.:
+ *
+ * jQuery.when(jQuery.ready, jQuery.ajax(''), jQuery('#my-element'), 1)
+ *
+ * If an argument is not an "observable" (a promise-like object) it is wrapped
+ * into a promise.
+ * @param {*} deferred
+ * @param {...*} deferreds
+ * @return {jQuery.Promise}
+ */
+jQuery.when = function(deferred, deferreds) {};
+
+/**
+ * Note: See jQuery.when().
+ * @param {*} deferred
+ * @param {...*} deferreds
+ * @return {jQuery.Promise}
+ */
+$.when = function(deferred, deferreds) {};
+
+/**
+ * @param {(string|number|function(number,number))=} arg1
+ * @return {(number|!jQuery)}
+ */
+jQuery.prototype.width = function(arg1) {};
+
+/**
+ * @param {(string|jQuerySelector|Element|jQuery|function(number))} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.wrap = function(arg1) {};
+
+/**
+ * @param {(string|jQuerySelector|Element|jQuery)} wrappingElement
+ * @return {!jQuery}
+ */
+jQuery.prototype.wrapAll = function(wrappingElement) {};
+
+/**
+ * @param {(string|jQuerySelector|Element|jQuery|function(number))} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.wrapInner = function(arg1) {};
diff --git a/js/hover.js b/js/hover.js
deleted file mode 100644
index 50ba777681..0000000000
--- a/js/hover.js
+++ /dev/null
@@ -1,4 +0,0 @@
-$(document).on('mq4hsChange', function (e) {
- 'use strict';
- $(document.documentElement).toggleClass('bs-true-hover', e.trueHover);
-});
diff --git a/js/modal.js b/js/modal.js
index e7704b8fc4..fb4cd03296 100644
--- a/js/modal.js
+++ b/js/modal.js
@@ -1,324 +1,598 @@
-/* ========================================================================
- * Bootstrap: modal.js v3.3.2
- * http://getbootstrap.com/javascript/#modals
+/** =======================================================================
+ * Bootstrap: modal.js v4.0.0
+ * http://getbootstrap.com/javascript/#modal
* ========================================================================
* 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';
-+function ($) {
- 'use strict';
- // MODAL CLASS DEFINITION
- // ======================
+/**
+ * Our modal class.
+ * @param {Element} element
+ * @param {Object} config
+ * @constructor
+ */
+var Modal = function (element, config) {
- var Modal = function (element, options) {
- this.options = options
- this.$body = $(document.body)
- this.$element = $(element)
- this.$backdrop =
- this.isShown = null
- this.scrollbarWidth = 0
+ /** @private {Object} */
+ this._config = config
+
+ /** @private {Element} */
+ this._element = element
- 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.2'
+ /** @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.backdrop(function () {
- var transition = $.support.transition && that.$element.hasClass('fade')
+ if (this._isShown || showEvent.isDefaultPrevented()) {
+ return
+ }
- if (!that.$element.parent().length) {
- that.$element.appendTo(that.$body) // don't move modals dom position
- }
+ this._isShown = true
- that.$element
- .show()
- .scrollTop(0)
+ this._checkScrollbar()
+ this._setScrollbar()
- if (that.options.backdrop) that.adjustBackdrop()
- that.adjustDialog()
+ $(document.body).addClass(Modal._ClassName.OPEN)
- if (transition) {
- that.$element[0].offsetWidth // force reflow
- }
+ this._escape()
+ this._resize()
- that.$element
- .addClass('in')
- .attr('aria-hidden', false)
+ $(this._element).on('click.dismiss.bs.modal', Modal._Selector.DATA_DISMISS, this['hide'].bind(this))
- that.enforceFocus()
+ this._showBackdrop(this._showElement.bind(this, relatedTarget))
+}
- var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
- transition ?
- that.$element.find('.modal-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)
- })
+/**
+ * @param {Event} event
+ */
+Modal.prototype['hide'] = function (event) {
+ if (event) {
+ event.preventDefault()
}
- Modal.prototype.hide = function (e) {
- if (e) e.preventDefault()
+ var hideEvent = $.Event(Modal._Event.HIDE)
- e = $.Event('hide.bs.modal')
+ $(this._element).trigger(hideEvent)
- this.$element.trigger(e)
+ if (!this._isShown || hideEvent.isDefaultPrevented()) {
+ return
+ }
- if (!this.isShown || e.isDefaultPrevented()) return
+ this._isShown = false
- this.isShown = false
+ this._escape()
+ this._resize()
- this.escape()
- this.resize()
+ $(document).off('focusin.bs.modal')
- $(document).off('focusin.bs.modal')
+ $(this._element).removeClass(Modal._ClassName.IN)
+ this._element.setAttribute('aria-hidden', true)
- this.$element
- .removeClass('in')
- .attr('aria-hidden', true)
- .off('click.dismiss.bs.modal')
+ $(this._element).off('click.dismiss.bs.modal')
- $.support.transition && this.$element.hasClass('fade') ?
- this.$element
- .one('bsTransitionEnd', $.proxy(this.hideModal, this))
- .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
- this.hideModal()
+ 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()
}
+}
- 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))
- }
- 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')
- }
- }
+/**
+ * @param {Element} relatedTarget
+ * @private
+ */
+Modal.prototype._showElement = function (relatedTarget) {
+ var transition = Bootstrap.transition && $(this._element).hasClass(Modal._ClassName.FADE)
- Modal.prototype.resize = function () {
- if (this.isShown) {
- $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
- } else {
- $(window).off('resize.bs.modal')
- }
+ if (!this._element.parentNode || this._element.parentNode.nodeType != Node.ELEMENT_NODE) {
+ document.body.appendChild(this._element) // don't move modals dom position
}
- 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')
- })
- }
+ this._element.style.display = 'block'
+ this._element.scrollTop = 0
- Modal.prototype.removeBackdrop = function () {
- this.$backdrop && this.$backdrop.remove()
- this.$backdrop = null
+ if (this._config['backdrop']) {
+ this._adjustBackdrop()
}
- Modal.prototype.backdrop = function (callback) {
- var that = this
- var animate = this.$element.hasClass('fade') ? 'fade' : ''
+ if (transition) {
+ Bootstrap.reflow(this._element)
+ }
- if (this.isShown && this.options.backdrop) {
- var doAnimate = $.support.transition && animate
+ $(this._element).addClass(Modal._ClassName.IN)
+ this._element.setAttribute('aria-hidden', false)
- this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
- .prependTo(this.$element)
- .on('click.dismiss.bs.modal', $.proxy(function (e) {
- if (e.target !== e.currentTarget) return
- this.options.backdrop == 'static'
- ? this.$element[0].focus.call(this.$element[0])
- : this.hide.call(this)
- }, this))
+ this._enforceFocus()
- if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
+ var shownEvent = $.Event(Modal._Event.SHOWN, { relatedTarget: relatedTarget })
- this.$backdrop.addClass('in')
+ var transitionComplete = function () {
+ this._element.focus()
+ $(this._element).trigger(shownEvent)
+ }.bind(this)
- if (!callback) return
+ if (transition) {
+ var dialog = $(this._element).find(Modal._Selector.DIALOG)[0]
+ $(dialog)
+ .one(Bootstrap.TRANSITION_END, transitionComplete)
+ .emulateTransitionEnd(Modal._TRANSITION_DURATION)
+ } else {
+ transitionComplete()
+ }
+}
- doAnimate ?
- this.$backdrop
- .one('bsTransitionEnd', callback)
- .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
- callback()
- } else if (!this.isShown && this.$backdrop) {
- this.$backdrop.removeClass('in')
- var callbackRemove = function () {
- that.removeBackdrop()
- callback && callback()
+/**
+ * @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()
}
- $.support.transition && this.$element.hasClass('fade') ?
- this.$backdrop
- .one('bsTransitionEnd', callbackRemove)
- .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
- callbackRemove()
+ }.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 (callback) {
- callback()
- }
+ } else if (!this._isShown) {
+ $(this._element).off('keydown.dismiss.bs.modal')
}
+}
- // these following methods are used to handle overflowing modals
- Modal.prototype.handleUpdate = function () {
- if (this.options.backdrop) this.adjustBackdrop()
- this.adjustDialog()
+/**
+ * @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.adjustBackdrop = function () {
- this.$backdrop
- .css('height', 0)
- .css('height', this.$element[0].scrollHeight)
+}
+
+
+/**
+ * @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.adjustDialog = function () {
- var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
- this.$element.css({
- paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
- paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
- })
- }
+/**
+ * @param {Function} callback
+ * @private
+ */
+Modal.prototype._showBackdrop = function (callback) {
+ var animate = $(this._element).hasClass(Modal._ClassName.FADE) ? Modal._ClassName.FADE : ''
- Modal.prototype.resetAdjustments = function () {
- this.$element.css({
- paddingLeft: '',
- paddingRight: ''
- })
- }
+ if (this._isShown && this._config['backdrop']) {
+ var doAnimate = Bootstrap.transition && animate
- Modal.prototype.checkScrollbar = function () {
- this.bodyIsOverflowing = document.body.scrollHeight > document.documentElement.clientHeight
- this.scrollbarWidth = this.measureScrollbar()
- }
+ this._backdrop = document.createElement('div')
+ this._backdrop.className = Modal._ClassName.BACKDROP
- Modal.prototype.setScrollbar = function () {
- var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
- if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
- }
+ if (animate) {
+ $(this._backdrop).addClass(animate)
+ }
- Modal.prototype.resetScrollbar = function () {
- this.$body.css('padding-right', '')
- }
+ $(this._element).prepend(this._backdrop)
- 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
- }
+ $(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 (doAnimate) {
+ Bootstrap.reflow(this._backdrop)
+ }
- // MODAL PLUGIN DEFINITION
- // =======================
+ $(this._backdrop).addClass(Modal._ClassName.IN)
- 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)
+ if (!callback) {
+ return
+ }
- 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)
- })
- }
+ if (!doAnimate) {
+ callback()
+ return
+ }
- var old = $.fn.modal
+ $(this._backdrop)
+ .one(Bootstrap.TRANSITION_END, callback)
+ .emulateTransitionEnd(Modal._BACKDROP_TRANSITION_DURATION)
- $.fn.modal = Plugin
- $.fn.modal.Constructor = Modal
+ } else if (!this._isShown && this._backdrop) {
+ $(this._backdrop).removeClass(Modal._ClassName.IN)
+ var callbackRemove = function () {
+ this._removeBackdrop()
+ if (callback) {
+ callback()
+ }
+ }.bind(this)
+
+ if (Bootstrap.transition && $(this._element).hasClass(Modal._ClassName.FADE)) {
+ $(this._backdrop)
+ .one(Bootstrap.TRANSITION_END, callbackRemove)
+ .emulateTransitionEnd(Modal._BACKDROP_TRANSITION_DURATION)
+ } else {
+ callbackRemove()
+ }
- // MODAL NO CONFLICT
- // =================
+ } else if (callback) {
+ callback()
+ }
+}
+
+
+/**
+ * ------------------------------------------------------------------------
+ * 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'
+ }
- $.fn.modal.noConflict = function () {
- $.fn.modal = old
- return this
+ if (this._isBodyOverflowing && !isModalOverflowing) {
+ this._element.style.paddingRight = this._scrollbarWidth + 'px'
}
+}
- // MODAL DATA-API
- // ==============
+/**
+ * @private
+ */
+Modal.prototype._resetAdjustments = function () {
+ this._element.style.paddingLeft = ''
+ this._element.style.paddingRight = ''
+}
- $(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()
+/**
+ * @private
+ */
+Modal.prototype._checkScrollbar = function () {
+ this._isBodyOverflowing = document.body.scrollHeight > document.documentElement.clientHeight
+ this._scrollbarWidth = this._getScrollbarWidth()
+}
- $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)
- })
-}(jQuery);
+/**
+ * @private
+ */
+Modal.prototype._setScrollbar = function () {
+ var bodyPadding = parseInt(($(document.body).css('padding-right') || 0), 10)
+
+ 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())
+
+ if (this.tagName == 'A') {
+ event.preventDefault()
+ }
+
+ 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._jQueryInterface.call($(target), config, this)
+})
diff --git a/js/popover.js b/js/popover.js
index 02476ebac9..5663355516 100644
--- a/js/popover.js
+++ b/js/popover.js
@@ -1,113 +1,283 @@
-/* ========================================================================
- * Bootstrap: popover.js v3.3.2
+/** =======================================================================
+ * Bootstrap: popover.js v4.0.0
* http://getbootstrap.com/javascript/#popovers
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @fileoverview - Bootstrap's popover plugin - extends tooltip.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.popover
+ * + $.popover.noConflict
+ * + $.popover.Constructor
+ * + $.popover.Constructor.VERSION
+ * + $.popover.Constructor.Defaults
+ * + $.popover.Constructor.Defaults.container
+ * + $.popover.Constructor.Defaults.animation
+ * + $.popover.Constructor.Defaults.placement
+ * + $.popover.Constructor.Defaults.selector
+ * + $.popover.Constructor.Defaults.template
+ * + $.popover.Constructor.Defaults.trigger
+ * + $.popover.Constructor.Defaults.title
+ * + $.popover.Constructor.Defaults.content
+ * + $.popover.Constructor.Defaults.delay
+ * + $.popover.Constructor.Defaults.html
+ * + $.popover.Constructor.Defaults.viewport
+ * + $.popover.Constructor.Defaults.viewport.selector
+ * + $.popover.Constructor.Defaults.viewport.padding
+ * + $.popover.Constructor.prototype.enable
+ * + $.popover.Constructor.prototype.disable
+ * + $.popover.Constructor.prototype.destroy
+ * + $.popover.Constructor.prototype.toggleEnabled
+ * + $.popover.Constructor.prototype.toggle
+ * + $.popover.Constructor.prototype.show
+ * + $.popover.Constructor.prototype.hide
+ *
+ * ========================================================================
+ */
-+function ($) {
- 'use strict';
+'use strict';
- // POPOVER PUBLIC CLASS DEFINITION
- // ===============================
- var Popover = function (element, options) {
- this.init('popover', element, options)
- }
+if (!Tooltip) throw new Error('Popover requires tooltip.js')
+
+
+/**
+ * Our tooltip class.
+ * @param {Element!} element
+ * @param {Object=} opt_config
+ * @constructor
+ * @extends {Tooltip}
+ */
+var Popover = function (element, opt_config) {
+ Tooltip.apply(this, arguments)
+}
+Bootstrap.inherits(Popover, Tooltip)
+
+
+/**
+ * @const
+ * @type {string}
+ */
+Popover['VERSION'] = '4.0.0'
+
+
+/**
+ * @const
+ * @type {Object}
+ */
+Popover['Defaults'] = $.extend({}, $.fn['tooltip']['Constructor']['Defaults'], {
+ 'placement': 'right',
+ 'trigger': 'click',
+ 'content': '',
+ 'template': '<div class="popover" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
+})
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Popover._NAME = 'popover'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Popover._DATA_KEY = 'bs.popover'
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Popover._Event = {
+ HIDE : 'hide.bs.popover',
+ HIDDEN : 'hidden.bs.popover',
+ SHOW : 'show.bs.popover',
+ SHOWN : 'shown.bs.popover'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Popover._ClassName = {
+ FADE : 'fade',
+ IN : 'in'
+}
- if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
- Popover.VERSION = '3.3.2'
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Popover._Selector = {
+ TITLE : '.popover-title',
+ CONTENT : '.popover-content',
+ ARROW : '.popover-arrow'
+}
- Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
- placement: 'right',
- trigger: 'click',
- content: '',
- template: '<div class="popover" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
+
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+Popover._JQUERY_NO_CONFLICT = $.fn[Popover._NAME]
+
+
+/**
+ * @param {Object|string=} opt_config
+ * @this {jQuery}
+ * @return {jQuery}
+ * @private
+ */
+Popover._jQueryInterface = function (opt_config) {
+ return this.each(function () {
+ var data = $(this).data(Popover._DATA_KEY)
+ var config = typeof opt_config === 'object' ? opt_config : null
+
+ if (!data && opt_config === 'destroy') {
+ return
+ }
+
+ if (!data) {
+ data = new Popover(this, config)
+ $(this).data(Popover._DATA_KEY, data)
+ }
+
+ if (typeof opt_config === 'string') {
+ data[opt_config]()
+ }
})
+}
- // NOTE: POPOVER EXTENDS tooltip.js
- // ================================
+/**
+ * @return {string}
+ * @protected
+ */
+Popover.prototype.getName = function () {
+ return Popover._NAME
+}
- Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
- Popover.prototype.constructor = Popover
+/**
+ * @override
+ */
+Popover.prototype.getDataKey = function () {
+ return Popover._DATA_KEY
+}
- Popover.prototype.getDefaults = function () {
- return Popover.DEFAULTS
- }
- Popover.prototype.setContent = function () {
- var $tip = this.tip()
- var title = this.getTitle()
- var content = this.getContent()
+/**
+ * @override
+ */
+Popover.prototype.getEventObject = function () {
+ return Popover._Event
+}
- $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
- $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events
- this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
- ](content)
- $tip.removeClass('fade popover-top popover-bottom popover-left popover-right in')
+/**
+ * @override
+ */
+Popover.prototype.getArrowElement = function () {
+ return (this.arrow = this.arrow || $(this.getTipElement()).find(Popover._Selector.ARROW)[0])
+}
- // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
- // this manually by checking the contents.
- if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
- }
- Popover.prototype.hasContent = function () {
- return this.getTitle() || this.getContent()
+/**
+ * @override
+ */
+Popover.prototype.setContent = function () {
+ var tip = this.getTipElement()
+ var title = this.getTitle()
+ var content = this._getContent()
+ var titleElement = $(tip).find(Popover._Selector.TITLE)[0]
+
+ if (titleElement) {
+ titleElement[this.config['html'] ? 'innerHTML' : 'innerText'] = title
}
- Popover.prototype.getContent = function () {
- var $e = this.$element
- var o = this.options
+ // we use append for html objects to maintain js events
+ $(tip).find(Popover._Selector.CONTENT).children().detach().end()[
+ this.config['html'] ? (typeof content == 'string' ? 'html' : 'append') : 'text'
+ ](content)
- return $e.attr('data-content')
- || (typeof o.content == 'function' ?
- o.content.call($e[0]) :
- o.content)
- }
+ $(tip)
+ .removeClass(Popover._ClassName.FADE)
+ .removeClass(Popover._ClassName.IN)
- Popover.prototype.arrow = function () {
- return (this.$arrow = this.$arrow || this.tip().find('.popover-arrow'))
+ for (var direction in Tooltip.Direction) {
+ $(tip).removeClass(Popover._NAME + '-' + Tooltip.Direction[direction])
}
+}
- Popover.prototype.tip = function () {
- if (!this.$tip) this.$tip = $(this.options.template)
- return this.$tip
- }
+/**
+ * @override
+ */
+Popover.prototype.isWithContent = function () {
+ return this.getTitle() || this._getContent()
+}
- // POPOVER PLUGIN DEFINITION
- // =========================
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.popover')
- var options = typeof option == 'object' && option
+/**
+ * @override
+ */
+Popover.prototype.getTipElement = function () {
+ return (this.tip = this.tip || $(this.config['template'])[0])
+}
- if (!data && option == 'destroy') return
- if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
- var old = $.fn.popover
+/**
+ * @private
+ */
+Popover.prototype._getContent = function () {
+ return this.element.getAttribute('data-content')
+ || (typeof this.config['content'] == 'function' ?
+ this.config['content'].call(this.element) :
+ this.config['content'])
+}
- $.fn.popover = Plugin
- $.fn.popover.Constructor = Popover
- // POPOVER NO CONFLICT
- // ===================
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Popover._NAME] = Popover._jQueryInterface
+
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Popover._NAME]['Constructor'] = Popover
- $.fn.popover.noConflict = function () {
- $.fn.popover = old
- return this
- }
-}(jQuery);
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Popover._NAME]['noConflict'] = function () {
+ $.fn[Popover._NAME] = Popover._JQUERY_NO_CONFLICT
+ return this
+}
diff --git a/js/scrollspy.js b/js/scrollspy.js
index 0987177fd4..150241b540 100644
--- a/js/scrollspy.js
+++ b/js/scrollspy.js
@@ -1,175 +1,346 @@
-/* ========================================================================
- * Bootstrap: scrollspy.js v3.3.2
+/** =======================================================================
+ * Bootstrap: scrollspy.js v4.0.0
* http://getbootstrap.com/javascript/#scrollspy
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @fileoverview - Bootstrap's scrollspy plugin.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.scrollspy
+ * + $.scrollspy.noConflict
+ * + $.scrollspy.Constructor
+ * + $.scrollspy.Constructor.VERSION
+ * + $.scrollspy.Constructor.Defaults
+ * + $.scrollspy.Constructor.Defaults.offset
+ * + $.scrollspy.Constructor.prototype.refresh
+ *
+ * ========================================================================
+ */
+'use strict';
-+function ($) {
- 'use strict';
- // SCROLLSPY CLASS DEFINITION
- // ==========================
+/**
+ * Our scrollspy class.
+ * @param {Element!} element
+ * @param {Object=} opt_config
+ * @constructor
+ */
+function ScrollSpy(element, opt_config) {
- function ScrollSpy(element, options) {
- var process = $.proxy(this.process, this)
+ /** @private {Element|Window} */
+ this._scrollElement = element.tagName == 'BODY' ? window : element
- this.$body = $('body')
- this.$scrollElement = $(element).is('body') ? $(window) : $(element)
- this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
- this.selector = (this.options.target || '') + ' .nav li > a'
- this.offsets = []
- this.targets = []
- this.activeTarget = null
- this.scrollHeight = 0
+ /** @private {Object} */
+ this._config = $.extend({}, ScrollSpy['Defaults'], opt_config)
- this.$scrollElement.on('scroll.bs.scrollspy', process)
- this.refresh()
- this.process()
- }
+ /** @private {string} */
+ this._selector = (this._config.target || '') + ' .nav li > a'
- ScrollSpy.VERSION = '3.3.2'
+ /** @private {Array} */
+ this._offsets = []
- ScrollSpy.DEFAULTS = {
- offset: 10
- }
+ /** @private {Array} */
+ this._targets = []
- ScrollSpy.prototype.getScrollHeight = function () {
- return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
- }
+ /** @private {Element} */
+ this._activeTarget = null
- ScrollSpy.prototype.refresh = function () {
- var offsetMethod = 'offset'
- var offsetBase = 0
+ /** @private {number} */
+ this._scrollHeight = 0
- if (!$.isWindow(this.$scrollElement[0])) {
- offsetMethod = 'position'
- offsetBase = this.$scrollElement.scrollTop()
- }
+ $(this._scrollElement).on('scroll.bs.scrollspy', this._process.bind(this))
- this.offsets = []
- this.targets = []
- this.scrollHeight = this.getScrollHeight()
-
- var self = this
-
- this.$body
- .find(this.selector)
- .map(function () {
- var $el = $(this)
- var href = $el.data('target') || $el.attr('href')
- var $href = /^#./.test(href) && $(href)
-
- return ($href
- && $href.length
- && $href.is(':visible')
- && [[$href[offsetMethod]().top + offsetBase, href]]) || null
- })
- .sort(function (a, b) { return a[0] - b[0] })
- .each(function () {
- self.offsets.push(this[0])
- self.targets.push(this[1])
- })
- }
+ this['refresh']()
- ScrollSpy.prototype.process = function () {
- var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
- var scrollHeight = this.getScrollHeight()
- var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height()
- var offsets = this.offsets
- var targets = this.targets
- var activeTarget = this.activeTarget
- var i
-
- if (this.scrollHeight != scrollHeight) {
- this.refresh()
- }
+ this._process()
+}
- if (scrollTop >= maxScroll) {
- return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
- }
- if (activeTarget && scrollTop < offsets[0]) {
- this.activeTarget = null
- return this.clear()
+/**
+ * @const
+ * @type {string}
+ */
+ScrollSpy['VERSION'] = '4.0.0'
+
+
+/**
+ * @const
+ * @type {Object}
+ */
+ScrollSpy['Defaults'] = {
+ 'offset': 10
+}
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+ScrollSpy._NAME = 'scrollspy'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+ScrollSpy._DATA_KEY = 'bs.scrollspy'
+
+
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+ScrollSpy._JQUERY_NO_CONFLICT = $.fn[ScrollSpy._NAME]
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+ScrollSpy._Event = {
+ ACTIVATE: 'activate.bs.scrollspy'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+ScrollSpy._ClassName = {
+ DROPDOWN_MENU : 'dropdown-menu',
+ ACTIVE : 'active'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+ScrollSpy._Selector = {
+ DATA_SPY : '[data-spy="scroll"]',
+ ACTIVE : '.active',
+ LI_DROPDOWN : 'li.dropdown',
+ LI : 'li'
+}
+
+
+/**
+ * @param {Object=} opt_config
+ * @this {jQuery}
+ * @return {jQuery}
+ * @private
+ */
+ScrollSpy._jQueryInterface = function (opt_config) {
+ return this.each(function () {
+ var data = $(this).data(ScrollSpy._DATA_KEY)
+ var config = typeof opt_config === 'object' && opt_config || null
+
+ if (!data) {
+ data = new ScrollSpy(this, config)
+ $(this).data(ScrollSpy._DATA_KEY, data)
}
- for (i = offsets.length; i--;) {
- activeTarget != targets[i]
- && scrollTop >= offsets[i]
- && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
- && this.activate(targets[i])
+ if (typeof opt_config === 'string') {
+ data[opt_config]()
}
+ })
+}
+
+
+/**
+ * Refresh the scrollspy target cache
+ */
+ScrollSpy.prototype['refresh'] = function () {
+ var offsetMethod = 'offset'
+ var offsetBase = 0
+
+ if (this._scrollElement !== this._scrollElement.window) {
+ offsetMethod = 'position'
+ offsetBase = this._getScrollTop()
}
- ScrollSpy.prototype.activate = function (target) {
- this.activeTarget = target
+ this._offsets = []
+ this._targets = []
- this.clear()
+ this._scrollHeight = this._getScrollHeight()
- var selector = this.selector +
- '[data-target="' + target + '"],' +
- this.selector + '[href="' + target + '"]'
+ var targets = /** @type {Array.<Element>} */ ($.makeArray($(this._selector)))
- var active = $(selector)
- .parents('li')
- .addClass('active')
+ targets
+ .map(function (element, index) {
+ var target
+ var targetSelector = Bootstrap.getSelectorFromElement(element)
- if (active.parent('.dropdown-menu').length) {
- active = active
- .closest('li.dropdown')
- .addClass('active')
+ if (targetSelector) {
+ target = $(targetSelector)[0]
+ }
+
+ if (target && (target.offsetWidth || target.offsetHeight)) {
+ // todo (fat): remove sketch reliance on jQuery position/offset
+ return [$(target)[offsetMethod]().top + offsetBase, targetSelector]
+ }
+ })
+ .filter(function (item) { return item })
+ .sort(function (a, b) { return a[0] - b[0] })
+ .forEach(function (item, index) {
+ this._offsets.push(item[0])
+ this._targets.push(item[1])
+ }.bind(this))
+}
+
+
+/**
+ * @private
+ */
+ScrollSpy.prototype._getScrollTop = function () {
+ return this._scrollElement === window ?
+ this._scrollElement.scrollY : this._scrollElement.scrollTop
+}
+
+
+/**
+ * @private
+ */
+ScrollSpy.prototype._getScrollHeight = function () {
+ return this._scrollElement.scrollHeight
+ || Math.max(document.body.scrollHeight, document.documentElement.scrollHeight)
+}
+
+
+/**
+ * @private
+ */
+ScrollSpy.prototype._process = function () {
+ var scrollTop = this._getScrollTop() + this._config.offset
+ var scrollHeight = this._getScrollHeight()
+ var maxScroll = this._config.offset + scrollHeight - this._scrollElement.offsetHeight
+
+ if (this._scrollHeight != scrollHeight) {
+ this['refresh']()
+ }
+
+ if (scrollTop >= maxScroll) {
+ var target = this._targets[this._targets.length - 1]
+
+ if (this._activeTarget != target) {
+ this._activate(target)
}
+ }
- active.trigger('activate.bs.scrollspy')
+ if (this._activeTarget && scrollTop < this._offsets[0]) {
+ this._activeTarget = null
+ this._clear()
+ return
}
- ScrollSpy.prototype.clear = function () {
- $(this.selector)
- .parentsUntil(this.options.target, '.active')
- .removeClass('active')
+ for (var i = this._offsets.length; i--;) {
+ var isActiveTarget = this._activeTarget != this._targets[i]
+ && scrollTop >= this._offsets[i]
+ && (!this._offsets[i + 1] || scrollTop < this._offsets[i + 1])
+
+ if (isActiveTarget) {
+ this._activate(this._targets[i])
+ }
}
+}
- // SCROLLSPY PLUGIN DEFINITION
- // ===========================
+/**
+ * @param {Element} target
+ * @private
+ */
+ScrollSpy.prototype._activate = function (target) {
+ this._activeTarget = target
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.scrollspy')
- var options = typeof option == 'object' && option
+ this._clear()
- if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
+ var selector = this._selector
+ + '[data-target="' + target + '"],'
+ + this._selector + '[href="' + target + '"]'
+
+ // todo (fat): this seems horribly wrong… getting all raw li elements up the tree ,_,
+ var parentListItems = $(selector).parents(ScrollSpy._Selector.LI)
+
+ for (var i = parentListItems.length; i--;) {
+ $(parentListItems[i]).addClass(ScrollSpy._ClassName.ACTIVE)
+
+ var itemParent = parentListItems[i].parentNode
- var old = $.fn.scrollspy
+ if (itemParent && $(itemParent).hasClass(ScrollSpy._ClassName.DROPDOWN_MENU)) {
+ var closestDropdown = $(itemParent).closest(ScrollSpy._Selector.LI_DROPDOWN)[0]
+ $(closestDropdown).addClass(ScrollSpy._ClassName.ACTIVE)
+ }
+ }
- $.fn.scrollspy = Plugin
- $.fn.scrollspy.Constructor = ScrollSpy
+ $(this._scrollElement).trigger(ScrollSpy._Event.ACTIVATE, {
+ relatedTarget: target
+ })
+}
- // SCROLLSPY NO CONFLICT
- // =====================
+/**
+ * @private
+ */
+ScrollSpy.prototype._clear = function () {
+ var activeParents = $(this._selector).parentsUntil(this._config.target, ScrollSpy._Selector.ACTIVE)
- $.fn.scrollspy.noConflict = function () {
- $.fn.scrollspy = old
- return this
+ for (var i = activeParents.length; i--;) {
+ $(activeParents[i]).removeClass(ScrollSpy._ClassName.ACTIVE)
}
+}
- // SCROLLSPY DATA-API
- // ==================
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
- $(window).on('load.bs.scrollspy.data-api', function () {
- $('[data-spy="scroll"]').each(function () {
- var $spy = $(this)
- Plugin.call($spy, $spy.data())
- })
- })
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[ScrollSpy._NAME] = ScrollSpy._jQueryInterface
+
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[ScrollSpy._NAME]['Constructor'] = ScrollSpy
-}(jQuery);
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[ScrollSpy._NAME]['noConflict'] = function () {
+ $.fn[ScrollSpy._NAME] = ScrollSpy._JQUERY_NO_CONFLICT
+ return this
+}
+
+
+/**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+$(window).on('load.bs.scrollspy.data-api', function () {
+ var scrollSpys = /** @type {Array.<Element>} */ ($.makeArray($(ScrollSpy._Selector.DATA_SPY)))
+
+ for (var i = scrollSpys.length; i--;) {
+ var $spy = $(scrollSpys[i])
+ ScrollSpy._jQueryInterface.call($spy, /** @type {Object|null} */ ($spy.data()))
+ }
+})
diff --git a/js/tab.js b/js/tab.js
index ddb7fc9c4f..07897294a3 100644
--- a/js/tab.js
+++ b/js/tab.js
@@ -1,153 +1,324 @@
-/* ========================================================================
- * Bootstrap: tab.js v3.3.2
+/** =======================================================================
+ * Bootstrap: tab.js v4.0.0
* http://getbootstrap.com/javascript/#tabs
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @fileoverview - Bootstrap's tab plugin. Tab O_O
+ *
+ * Public Methods & Properties:
+ *
+ * + $.tab
+ * + $.tab.noConflict
+ * + $.tab.Constructor
+ * + $.tab.Constructor.VERSION
+ * + $.tab.Constructor.prototype.show
+ *
+ * ========================================================================
+ */
+
+
+'use strict';
+
+/**
+ * Our Tab class.
+ * @param {Element!} element
+ * @constructor
+ */
+var Tab = function (element) {
+
+ /** @type {Element} */
+ this._element = element
+
+}
+
+
+/**
+ * @const
+ * @type {string}
+ */
+Tab['VERSION'] = '4.0.0'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Tab._NAME = 'tab'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Tab._DATA_KEY = 'bs.tab'
+
+
+/**
+ * @const
+ * @type {number}
+ * @private
+ */
+Tab._TRANSITION_DURATION = 150
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Tab._Event = {
+ HIDE : 'hide.bs.tab',
+ HIDDEN : 'hidden.bs.tab',
+ SHOW : 'show.bs.tab',
+ SHOWN : 'shown.bs.tab'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Tab._ClassName = {
+ DROPDOWN_MENU : 'dropdown-menu',
+ ACTIVE : 'active',
+ FADE : 'fade',
+ IN : 'in'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Tab._Selector = {
+ A : 'a',
+ LI : 'li',
+ LI_DROPDOWN : 'li.dropdown',
+ UL : 'ul:not(.dropdown-menu)',
+ FADE_CHILD : ':scope > .fade',
+ ACTIVE : '.active',
+ ACTIVE_CHILD : ':scope > .active',
+ DATA_TOGGLE : '[data-toggle="tab"], [data-toggle="pill"]',
+ DROPDOWN_ACTIVE_CHILD : ':scope > .dropdown-menu > .active'
+}
+
+
+/**
+ * @param {Object|string=} opt_config
+ * @this {jQuery}
+ * @return {jQuery}
+ * @private
+ */
+Tab._jQueryInterface = function (opt_config) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data(Tab._DATA_KEY)
+
+ if (!data) {
+ data = data = new Tab(this)
+ $this.data(Tab._DATA_KEY, data)
+ }
+ if (typeof opt_config === 'string') {
+ data[opt_config]()
+ }
+ })
+}
+
+
+/**
+ * Show the tab
+ */
+Tab.prototype['show'] = function () {
+ if ( this._element.parentNode
+ && this._element.parentNode.nodeType == Node.ELEMENT_NODE
+ && $(this._element).parent().hasClass(Tab._ClassName.ACTIVE)) {
+ return
+ }
-+function ($) {
- 'use strict';
+ var ulElement = $(this._element).closest(Tab._Selector.UL)[0]
+ var selector = Bootstrap.getSelectorFromElement(this._element)
- // TAB CLASS DEFINITION
- // ====================
+ if (ulElement) {
+ var previous = /** @type {Array.<Element>} */ ($.makeArray($(ulElement).find(Tab._Selector.ACTIVE)))
+ previous = previous[previous.length - 1]
- var Tab = function (element) {
- this.element = $(element)
+ if (previous) {
+ previous = $(previous).find('a')[0]
+ }
}
- Tab.VERSION = '3.3.2'
+ var hideEvent = $.Event(Tab._Event.HIDE, {
+ relatedTarget: this._element
+ })
+
+ var showEvent = $.Event(Tab._Event.SHOW, {
+ relatedTarget: previous
+ })
- Tab.TRANSITION_DURATION = 150
+ if (previous) {
+ $(previous).trigger(hideEvent)
+ }
- Tab.prototype.show = function () {
- var $this = this.element
- var $ul = $this.closest('ul:not(.dropdown-menu)')
- var selector = $this.data('target')
+ $(this._element).trigger(showEvent)
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
- }
+ if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
- if ($this.parent('li').hasClass('active')) return
+ if (selector) {
+ var target = $(selector)[0]
+ }
- var $previous = $ul.find('.active:last a')
- var hideEvent = $.Event('hide.bs.tab', {
- relatedTarget: $this[0]
- })
- var showEvent = $.Event('show.bs.tab', {
- relatedTarget: $previous[0]
+ this._activate($(this._element).closest(Tab._Selector.LI)[0], ulElement)
+
+ var complete = function () {
+ var hiddenEvent = $.Event(Tab._Event.HIDDEN, {
+ relatedTarget: this._element
})
- $previous.trigger(hideEvent)
- $this.trigger(showEvent)
+ var shownEvent = $.Event(Tab._Event.SHOWN, {
+ relatedTarget: previous
+ })
- if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
+ $(previous).trigger(hiddenEvent)
+ $(this._element).trigger(shownEvent)
+ }.bind(this)
- var $target = $(selector)
+ if (target) {
+ this._activate(target, /** @type {Element} */ (target.parentNode), complete)
+ } else {
+ complete()
+ }
+}
+
+
+/**
+ * @param {Element} element
+ * @param {Element} container
+ * @param {Function=} opt_callback
+ * @private
+ */
+Tab.prototype._activate = function (element, container, opt_callback) {
+ var active = $(container).find(Tab._Selector.ACTIVE_CHILD)[0]
+ var isTransitioning = opt_callback
+ && Bootstrap.transition
+ && ((active && $(active).hasClass(Tab._ClassName.FADE))
+ || !!$(container).find(Tab._Selector.FADE_CHILD)[0])
+
+ var complete = this._transitionComplete.bind(this, element, active, isTransitioning, opt_callback)
+
+ if (active && isTransitioning) {
+ $(active)
+ .one(Bootstrap.TRANSITION_END, complete)
+ .emulateTransitionEnd(Tab._TRANSITION_DURATION)
+
+ } else {
+ complete()
+ }
- this.activate($this.closest('li'), $ul)
- this.activate($target, $target.parent(), function () {
- $previous.trigger({
- type: 'hidden.bs.tab',
- relatedTarget: $this[0]
- })
- $this.trigger({
- type: 'shown.bs.tab',
- relatedTarget: $previous[0]
- })
- })
+ if (active) {
+ $(active).removeClass(Tab._ClassName.IN)
}
+}
+
+
+/**
+ * @param {Element} element
+ * @param {Element} active
+ * @param {boolean} isTransitioning
+ * @param {Function=} opt_callback
+ * @private
+ */
+Tab.prototype._transitionComplete = function (element, active, isTransitioning, opt_callback) {
+ if (active) {
+ $(active).removeClass(Tab._ClassName.ACTIVE)
+
+ var dropdownChild = $(active).find(Tab._Selector.DROPDOWN_ACTIVE_CHILD)[0]
+ if (dropdownChild) {
+ $(dropdownChild).removeClass(Tab._ClassName.ACTIVE)
+ }
- Tab.prototype.activate = function (element, container, callback) {
- var $active = container.find('> .active')
- var transition = callback
- && $.support.transition
- && (($active.length && $active.hasClass('fade')) || !!container.find('> .fade').length)
-
- function next() {
- $active
- .removeClass('active')
- .find('> .dropdown-menu > .active')
- .removeClass('active')
- .end()
- .find('[data-toggle="tab"]')
- .attr('aria-expanded', false)
-
- element
- .addClass('active')
- .find('[data-toggle="tab"]')
- .attr('aria-expanded', true)
-
- if (transition) {
- element[0].offsetWidth // reflow for transition
- element.addClass('in')
- } else {
- element.removeClass('fade')
- }
-
- if (element.parent('.dropdown-menu')) {
- element
- .closest('li.dropdown')
- .addClass('active')
- .end()
- .find('[data-toggle="tab"]')
- .attr('aria-expanded', true)
- }
-
- callback && callback()
+ var activeToggle = $(active).find(Tab._Selector.DATA_TOGGLE)[0]
+ if (activeToggle) {
+ activeToggle.setAttribute('aria-expanded', false)
}
+ }
- $active.length && transition ?
- $active
- .one('bsTransitionEnd', next)
- .emulateTransitionEnd(Tab.TRANSITION_DURATION) :
- next()
+ $(element).addClass(Tab._ClassName.ACTIVE)
- $active.removeClass('in')
+ var elementToggle = $(element).find(Tab._Selector.DATA_TOGGLE)[0]
+ if (elementToggle) {
+ elementToggle.setAttribute('aria-expanded', true)
}
+ if (isTransitioning) {
+ Bootstrap.reflow(element)
+ $(element).addClass(Tab._ClassName.IN)
+ } else {
+ $(element).removeClass(Tab._ClassName.FADE)
+ }
- // TAB PLUGIN DEFINITION
- // =====================
+ if (element.parentNode && $(element.parentNode).hasClass(Tab._ClassName.DROPDOWN_MENU)) {
+ var dropdownElement = $(element).closest(Tab._Selector.LI_DROPDOWN)[0]
+ if (dropdownElement) {
+ $(dropdownElement).addClass(Tab._ClassName.ACTIVE)
+ }
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.tab')
+ elementToggle = $(element).find(Tab._Selector.DATA_TOGGLE)[0]
+ if (elementToggle) {
+ elementToggle.setAttribute('aria-expanded', true)
+ }
+ }
- if (!data) $this.data('bs.tab', (data = new Tab(this)))
- if (typeof option == 'string') data[option]()
- })
+ if (opt_callback) {
+ opt_callback()
}
+}
- var old = $.fn.tab
- $.fn.tab = Plugin
- $.fn.tab.Constructor = Tab
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Tab._NAME] = Tab._jQueryInterface
- // TAB NO CONFLICT
- // ===============
- $.fn.tab.noConflict = function () {
- $.fn.tab = old
- return this
- }
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Tab._NAME]['Constructor'] = Tab
- // TAB DATA-API
- // ============
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Tab._NAME]['noConflict'] = function () {
+ $.fn[Tab._NAME] = Tab._JQUERY_NO_CONFLICT
+ return this
+}
- var clickHandler = function (e) {
- e.preventDefault()
- Plugin.call($(this), 'show')
- }
- $(document)
- .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
- .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
-}(jQuery);
+// TAB DATA-API
+// ============
+
+var clickHandler = function (e) {
+ e.preventDefault()
+ Tab._jQueryInterface.call($(this), 'show')
+}
+
+$(document)
+ .on('click.bs.tab.data-api', Tab._Selector.DATA_TOGGLE, clickHandler)
diff --git a/js/tests/closure.html b/js/tests/closure.html
new file mode 100644
index 0000000000..82c65f62b8
--- /dev/null
+++ b/js/tests/closure.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Bootstrap Plugin Test Suite</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+
+ <!-- jQuery -->
+ <script src="vendor/jquery.min.js"></script>
+
+ <!-- QUnit -->
+ <link rel="stylesheet" href="vendor/qunit.css" media="screen">
+ <script src="vendor/qunit.js"></script>
+ <style>
+ #qunit-tests > li.pass {
+ display: none;/* Make it easier to see failing tests in Sauce screencasts */
+ }
+
+ #qunit-fixture {
+ top: 0;
+ left: 0;
+ }
+ </style>
+ <script>
+ // See https://github.com/axemclion/grunt-saucelabs#test-result-details-with-qunit
+ var log = []
+ QUnit.done(function (testResults) {
+ var tests = []
+ for (var i = 0, len = log.length; i < len; i++) {
+ var details = log[i]
+ tests.push({
+ name: details.name,
+ result: details.result,
+ expected: details.expected,
+ actual: details.actual,
+ source: details.source
+ })
+ }
+ testResults.tests = tests
+
+ window.global_test_results = testResults
+ })
+
+ QUnit.testStart(function (testDetails) {
+ $(window).scrollTop(0)
+ QUnit.log = function (details) {
+ if (!details.result) {
+ details.name = testDetails.name
+ log.push(details)
+ }
+ }
+ })
+
+ // Cleanup
+ QUnit.testDone(function () {
+ $('#qunit-fixture').empty()
+ $('#modal-test, .modal-backdrop').remove()
+ })
+ </script>
+
+ <!-- Plugin sources -->
+ <script src="../../dist/js/bootstrap.min.js"></script>
+
+ <!-- Unit tests -->
+ <script src="unit/alert.js"></script>
+ <script src="unit/button.js"></script>
+ <script src="unit/carousel.js"></script>
+ <script src="unit/collapse.js"></script>
+ <script src="unit/dropdown.js"></script>
+ <script src="unit/modal.js"></script>
+ <script src="unit/scrollspy.js"></script>
+ <script src="unit/tab.js"></script>
+ <script src="unit/tooltip.js"></script>
+ <script src="unit/popover.js"></script>
+
+ </head>
+ <body>
+ <div id="qunit-container">
+ <div id="qunit"></div>
+ <div id="qunit-fixture"></div>
+ </div>
+ </body>
+</html>
diff --git a/js/tests/index.html b/js/tests/index.html
index b32f0adc22..a534da526d 100644
--- a/js/tests/index.html
+++ b/js/tests/index.html
@@ -59,7 +59,7 @@
</script>
<!-- Plugin sources -->
- <script>$.support.transition = false</script>
+ <script src="../../js/util.js"></script>
<script src="../../js/alert.js"></script>
<script src="../../js/button.js"></script>
<script src="../../js/carousel.js"></script>
@@ -70,7 +70,6 @@
<script src="../../js/tab.js"></script>
<script src="../../js/tooltip.js"></script>
<script src="../../js/popover.js"></script>
- <script src="../../js/affix.js"></script>
<!-- Unit tests -->
<script src="unit/alert.js"></script>
@@ -83,7 +82,6 @@
<script src="unit/tab.js"></script>
<script src="unit/tooltip.js"></script>
<script src="unit/popover.js"></script>
- <script src="unit/affix.js"></script>
</head>
<body>
diff --git a/js/tests/unit/button.js b/js/tests/unit/button.js
index cb51d40b95..08b071d655 100644
--- a/js/tests/unit/button.js
+++ b/js/tests/unit/button.js
@@ -29,61 +29,6 @@ $(function () {
strictEqual($button[0], $el[0], 'collection contains element')
})
- test('should return set state to loading', function (assert) {
- var $btn = $('<button class="btn" data-loading-text="fat">mdo</button>')
- equal($btn.html(), 'mdo', 'btn text equals mdo')
- $btn.bootstrapButton('loading')
- var done = assert.async()
- setTimeout(function () {
- equal($btn.html(), 'fat', 'btn text equals fat')
- ok($btn[0].hasAttribute('disabled'), 'btn is disabled')
- ok($btn.hasClass('disabled'), 'btn has disabled class')
- done()
- }, 0)
- })
-
- test('should return reset state', function (assert) {
- var $btn = $('<button class="btn" data-loading-text="fat">mdo</button>')
- equal($btn.html(), 'mdo', 'btn text equals mdo')
- $btn.bootstrapButton('loading')
- var doneOne = assert.async()
- setTimeout(function () {
- equal($btn.html(), 'fat', 'btn text equals fat')
- ok($btn[0].hasAttribute('disabled'), 'btn is disabled')
- ok($btn.hasClass('disabled'), 'btn has disabled class')
- doneOne()
- var doneTwo = assert.async()
- $btn.bootstrapButton('reset')
- setTimeout(function () {
- equal($btn.html(), 'mdo', 'btn text equals mdo')
- ok(!$btn[0].hasAttribute('disabled'), 'btn is not disabled')
- ok(!$btn.hasClass('disabled'), 'btn does not have disabled class')
- doneTwo()
- }, 0)
- }, 0)
- })
-
- test('should work with an empty string as reset state', function (assert) {
- var $btn = $('<button class="btn" data-loading-text="fat"/>')
- equal($btn.html(), '', 'btn text equals ""')
- $btn.bootstrapButton('loading')
- var doneOne = assert.async()
- setTimeout(function () {
- equal($btn.html(), 'fat', 'btn text equals fat')
- ok($btn[0].hasAttribute('disabled'), 'btn is disabled')
- ok($btn.hasClass('disabled'), 'btn has disabled class')
- doneOne()
- var doneTwo = assert.async()
- $btn.bootstrapButton('reset')
- setTimeout(function () {
- equal($btn.html(), '', 'btn text equals ""')
- ok(!$btn[0].hasAttribute('disabled'), 'btn is not disabled')
- ok(!$btn.hasClass('disabled'), 'btn does not have disabled class')
- doneTwo()
- }, 0)
- }, 0)
- })
-
test('should toggle active', function () {
var $btn = $('<button class="btn" data-toggle="button">mdo</button>')
ok(!$btn.hasClass('active'), 'btn does not have active class')
diff --git a/js/tests/unit/carousel.js b/js/tests/unit/carousel.js
index 6da932d2c2..2ad2c67347 100644
--- a/js/tests/unit/carousel.js
+++ b/js/tests/unit/carousel.js
@@ -322,30 +322,31 @@ $(function () {
+ '<a class="left carousel-control" href="#myCarousel" data-slide="prev">&lsaquo;</a>'
+ '<a class="right carousel-control" href="#myCarousel" data-slide="next">&rsaquo;</a>'
+ '</div>'
+
var $carousel = $(templateHTML)
$carousel.attr('data-interval', 1814)
$carousel.appendTo('body')
$('[data-slide]').first().click()
- equal($carousel.data('bs.carousel').options.interval, 1814)
+ equal($carousel.data('bs.carousel').getConfig().interval, 1814)
$carousel.remove()
$carousel.appendTo('body').attr('data-modal', 'foobar')
$('[data-slide]').first().click()
- equal($carousel.data('bs.carousel').options.interval, 1814, 'even if there is an data-modal attribute set')
+ equal($carousel.data('bs.carousel').getConfig().interval, 1814, 'even if there is an data-modal attribute set')
$carousel.remove()
$carousel.appendTo('body')
$('[data-slide]').first().click()
$carousel.attr('data-interval', 1860)
$('[data-slide]').first().click()
- equal($carousel.data('bs.carousel').options.interval, 1814, 'attributes should be read only on initialization')
+ equal($carousel.data('bs.carousel').getConfig().interval, 1814, 'attributes should be read only on initialization')
$carousel.remove()
$carousel.attr('data-interval', false)
$carousel.appendTo('body')
$carousel.bootstrapCarousel(1)
- strictEqual($carousel.data('bs.carousel').options.interval, false, 'data attribute has higher priority than default options')
+ strictEqual($carousel.data('bs.carousel').getConfig().interval, false, 'data attribute has higher priority than default options')
$carousel.remove()
})
diff --git a/js/tests/unit/collapse.js b/js/tests/unit/collapse.js
index ad59d87dbc..ac6e657b38 100644
--- a/js/tests/unit/collapse.js
+++ b/js/tests/unit/collapse.js
@@ -288,7 +288,7 @@ $(function () {
$body2
.toggleClass('in collapsing')
- .data('bs.collapse').transitioning = 1
+ .data('bs.collapse').setTransitioning(true)
$target1.click()
diff --git a/js/tests/unit/modal.js b/js/tests/unit/modal.js
index c1af15a2f9..7018b865f3 100644
--- a/js/tests/unit/modal.js
+++ b/js/tests/unit/modal.js
@@ -30,7 +30,7 @@ $(function () {
})
test('should expose defaults var for settings', function () {
- ok($.fn.bootstrapModal.Constructor.DEFAULTS, 'default object exposed')
+ ok($.fn.bootstrapModal.Constructor.Defaults, 'default object exposed')
})
test('should insert into dom when show method is called', function (assert) {
diff --git a/js/tests/unit/tooltip.js b/js/tests/unit/tooltip.js
index 20efff8cc0..2b63a3b48c 100644
--- a/js/tests/unit/tooltip.js
+++ b/js/tests/unit/tooltip.js
@@ -30,7 +30,7 @@ $(function () {
})
test('should expose default settings', function () {
- ok($.fn.bootstrapTooltip.Constructor.DEFAULTS, 'defaults is defined')
+ ok($.fn.bootstrapTooltip.Constructor.Defaults, 'defaults is defined')
})
test('should empty title attribute', function () {
@@ -879,17 +879,17 @@ $(function () {
.bootstrapTooltip({ delay: { show: 0, hide: 150 }})
setTimeout(function () {
- ok($tooltip.data('bs.tooltip').$tip.is('.fade.in'), '1ms: tooltip faded in')
+ ok($('.tooltip').is('.fade.in'), '1ms: tooltip faded in')
$tooltip.trigger('mouseout')
setTimeout(function () {
- ok($tooltip.data('bs.tooltip').$tip.is('.fade.in'), '100ms: tooltip still faded in')
+ ok($('.tooltip').is('.fade.in'), '100ms: tooltip still faded in')
}, 100)
setTimeout(function () {
- ok(!$tooltip.data('bs.tooltip').$tip.is('.in'), '200ms: tooltip removed')
- done()
+ ok(!$('.tooltip').is('.in'), '200ms: tooltip removed')
+ start()
}, 200)
}, 0)
@@ -978,7 +978,7 @@ $(function () {
test('should not reload the tooltip on subsequent mouseenter events', function () {
var titleHtml = function () {
- var uid = $.fn.bootstrapTooltip.Constructor.prototype.getUID('tooltip')
+ var uid = 'fatTooltip'
return '<p id="tt-content">' + uid + '</p><p>' + uid + '</p><p>' + uid + '</p>'
}
@@ -1004,7 +1004,7 @@ $(function () {
test('should not reload the tooltip if the mouse leaves and re-enters before hiding', function () {
var titleHtml = function () {
- var uid = $.fn.bootstrapTooltip.Constructor.prototype.getUID('tooltip')
+ var uid = 'fatTooltip'
return '<p id="tt-content">' + uid + '</p><p>' + uid + '</p><p>' + uid + '</p>'
}
@@ -1029,10 +1029,12 @@ $(function () {
$('#tt-outer').trigger('mouseleave')
equal(currentUid, $('#tt-content').text())
- ok(obj.hoverState == 'out', 'the tooltip hoverState should be set to "out"')
+ debugger
+
+ ok(obj.getHoverState() == 'out', 'the tooltip hoverState should be set to "out"')
$('#tt-content').trigger('mouseenter')
- ok(obj.hoverState == 'in', 'the tooltip hoverState should be set to "in"')
+ ok(obj.getHoverState() == 'in', 'the tooltip hoverState should be set to "in"')
equal(currentUid, $('#tt-content').text())
})
@@ -1051,7 +1053,7 @@ $(function () {
$('<a href="#" title="tooltip title" style="position: absolute; bottom: 0; right: 0;">Foobar</a>')
.appendTo('body')
.on('shown.bs.tooltip', function () {
- var arrowStyles = $(this).data('bs.tooltip').$tip.find('.tooltip-arrow').attr('style')
+ var arrowStyles = $('.tooltip').find('.tooltip-arrow').attr('style')
ok(/left/i.test(arrowStyles) && !/top/i.test(arrowStyles), 'arrow positioned correctly')
$(this).bootstrapTooltip('hide')
})
diff --git a/js/tests/visual/affix.html b/js/tests/visual/affix.html
deleted file mode 100644
index 782fcb32c7..0000000000
--- a/js/tests/visual/affix.html
+++ /dev/null
@@ -1,299 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
- <meta charset="utf-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <title>Affix</title>
- <link rel="stylesheet" href="../../../dist/css/bootstrap.min.css">
-
- <style>
- /* Test Styles */
- .affixed-element-top.affix {
- top: 10px;
- }
- .affixed-element-top.affix-bottom {
- position: absolute;
- }
- .affixed-element-bottom {
- margin-bottom: 0;
- }
- .affixed-element-bottom.affix {
- bottom: 10px;
- }
- .affixed-element-bottom.affix-bottom {
- position: relative;
- }
- .grow-btn, .shrink-btn {
- color: #FFF;
- }
- .grow-btn {
- background-color: #2ECC40;
- }
- .grow-btn:hover {
- background-color: #3D9970;
- }
- .shrink-btn {
- background-color: #FF4136;
- }
- .shrink-btn:hover {
- background-color: #85144B;
- }
- </style>
-</head>
-<body>
-
-<div class="container">
-
- <div class="page-header js-page-header">
- <h1>Affix <small>Bootstrap Visual Test</small></h1>
- </div>
-
- <div class="col-md-3">
- <ul class="list-group affixed-element-top js-affixed-element-top">
- <li class="list-group-item">Cras justo odio</li>
- <li class="list-group-item">Dapibus ac facilisis in</li>
- <li class="list-group-item">Morbi leo risus</li>
- <li class="list-group-item">Porta ac consectetur ac</li>
- <li class="list-group-item">Vestibulum at eros</li>
- <li class="list-group-item">Cras justo odio</li>
- <li class="list-group-item">Dapibus ac facilisis in</li>
- <li class="list-group-item">Morbi leo risus</li>
- <li class="list-group-item">Porta ac consectetur ac</li>
- <li class="list-group-item">Vestibulum at eros</li>
- <li class="list-group-item">Cras justo odio</li>
- <li class="list-group-item">Dapibus ac facilisis in</li>
- <li class="list-group-item">Morbi leo risus</li>
- <li class="list-group-item">Porta ac consectetur ac</li>
- <li class="list-group-item">Vestibulum at eros</li>
- <li class="list-group-item">Porta ac consectetur ac</li>
- </ul>
- </div>
-
- <div class="col-md-6 js-content">
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- </div>
-
- <div class="col-md-3">
- <ul class="list-group affixed-element-bottom js-affixed-element-bottom">
- <li class="list-group-item">Sit necessitatibus aspernatur.</li>
- <li class="list-group-item">Adipisicing alias dolor!</li>
- <li class="list-group-item">Ipsum molestiae impedit.</li>
- <li class="list-group-item">Amet quis iste?</li>
- <li class="list-group-item">Ipsum quaerat porro.</li>
- <li class="list-group-item">Elit lorem libero.</li>
- <li class="list-group-item">Ipsum dolore facilis.</li>
- <li class="list-group-item">Elit ad atque.</li>
- <li class="list-group-item">Dolor amet sequi!</li>
- <li class="list-group-item">Consectetur voluptatum facilis!</li>
- <li class="list-group-item">Sit neque eligendi?</li>
- <li class="list-group-item">Amet fuga consectetur!</li>
- <li class="list-group-item">Amet molestias repellat!</li>
- <li class="list-group-item">Consectetur minima repellendus.</li>
- <li class="list-group-item grow-btn js-grow-btn">Grow content</li>
- <li class="list-group-item shrink-btn js-shrink-btn">Shrink content</li>
- </ul>
- </div>
-
- <div class="col-md-12 js-footer">
- <hr>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
- </div>
-
-</div>
-
-<!-- JavaScript Includes -->
-<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
-<script src="../../affix.js"></script>
-
-
-<!-- JavaScript Test -->
-<script>
-$(function () {
- $('.js-affixed-element-top').affix({
- offset: {
- top: $('.js-page-header').outerHeight(true) - 10
- , bottom: $('.js-footer').outerHeight(true) + 10
- }
- })
- // todo(fat): sux you have to do this.
- .on('affix.bs.affix', function (e) {
- $(e.target).width(e.target.offsetWidth)
- })
-
- $('.js-affixed-element-bottom').affix({
- offset: {
- bottom: $('.js-footer').outerHeight(true) + 10
- }
- })
-
- $('.js-grow-btn').on('click', function() {
- $('.js-content').append('<p>Ipsum corrupti ipsam est temporibus.</p>')
- })
- $('.js-shrink-btn').on('click', function() {
- $('.js-content p').last().remove()
- })
-})
-</script>
-</body>
-</html>
diff --git a/js/tests/visual/alert.html b/js/tests/visual/alert.html
index 2f824ace16..978f9ac209 100644
--- a/js/tests/visual/alert.html
+++ b/js/tests/visual/alert.html
@@ -34,7 +34,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
+<script src="../../util.js"></script>
<script src="../../alert.js"></script>
</body>
diff --git a/js/tests/visual/button.html b/js/tests/visual/button.html
index e9b06dfa26..c3be921415 100644
--- a/js/tests/visual/button.html
+++ b/js/tests/visual/button.html
@@ -15,10 +15,6 @@
<h1>Button <small>Bootstrap Visual Test</small></h1>
</div>
- <button type="button" data-loading-text="Loading for 3 seconds..." class="btn btn-primary js-loading-button">
- Loading state
- </button>
-
<button type="button" class="btn btn-primary" data-toggle="button">Single toggle</button>
<div class="btn-group" data-toggle="buttons">
@@ -49,7 +45,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
+<script src="../../util.js"></script>
<script src="../../button.js"></script>
<!-- JavaScript Test -->
diff --git a/js/tests/visual/carousel.html b/js/tests/visual/carousel.html
index 5b071679fc..45424a5d15 100644
--- a/js/tests/visual/carousel.html
+++ b/js/tests/visual/carousel.html
@@ -44,7 +44,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
+<script src="../../util.js"></script>
<script src="../../carousel.js"></script>
</body>
diff --git a/js/tests/visual/collapse.html b/js/tests/visual/collapse.html
index 89b36d7a62..1b81cf5ca8 100644
--- a/js/tests/visual/collapse.html
+++ b/js/tests/visual/collapse.html
@@ -64,7 +64,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
+<script src="../../util.js"></script>
<script src="../../collapse.js"></script>
</body>
diff --git a/js/tests/visual/dropdown.html b/js/tests/visual/dropdown.html
index 38f28bca14..6cf18df8ee 100644
--- a/js/tests/visual/dropdown.html
+++ b/js/tests/visual/dropdown.html
@@ -93,7 +93,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
+<script src="../../util.js"></script>
<script src="../../dropdown.js"></script>
<script src="../../collapse.js"></script>
diff --git a/js/tests/visual/modal.html b/js/tests/visual/modal.html
index 86d1427f99..0d6e50ea2c 100644
--- a/js/tests/visual/modal.html
+++ b/js/tests/visual/modal.html
@@ -63,7 +63,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
+<script src="../../util.js"></script>
<script src="../../modal.js"></script>
<script src="../../tooltip.js"></script>
<script src="../../popover.js"></script>
diff --git a/js/tests/visual/popover.html b/js/tests/visual/popover.html
index e71ebca8c6..7446a67412 100644
--- a/js/tests/visual/popover.html
+++ b/js/tests/visual/popover.html
@@ -32,7 +32,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
+<script src="../../util.js"></script>
<script src="../../tooltip.js"></script>
<script src="../../popover.js"></script>
diff --git a/js/tests/visual/scrollspy.html b/js/tests/visual/scrollspy.html
index 1edfbc383d..13bc4383cc 100644
--- a/js/tests/visual/scrollspy.html
+++ b/js/tests/visual/scrollspy.html
@@ -93,7 +93,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
+<script src="../../util.js"></script>
<script src="../../scrollspy.js"></script>
<script src="../../dropdown.js"></script>
<script src="../../collapse.js"></script>
diff --git a/js/tests/visual/tab.html b/js/tests/visual/tab.html
index a1dd8214c0..2480ce08ca 100644
--- a/js/tests/visual/tab.html
+++ b/js/tests/visual/tab.html
@@ -157,7 +157,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
+<script src="../../util.js"></script>
<script src="../../tab.js"></script>
<script src="../../dropdown.js"></script>
diff --git a/js/tests/visual/tooltip.html b/js/tests/visual/tooltip.html
index 7c8361e4dc..c61b088d0e 100644
--- a/js/tests/visual/tooltip.html
+++ b/js/tests/visual/tooltip.html
@@ -29,7 +29,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
+<script src="../../util.js"></script>
<script src="../../tooltip.js"></script>
<!-- JavaScript Test -->
diff --git a/js/tooltip.js b/js/tooltip.js
index 8663bb7675..ced0e9282f 100644
--- a/js/tooltip.js
+++ b/js/tooltip.js
@@ -1,472 +1,868 @@
-/* ========================================================================
- * Bootstrap: tooltip.js v3.3.2
+/** =======================================================================
+ * Bootstrap: tooltip.js v4.0.0
* http://getbootstrap.com/javascript/#tooltip
- * Inspired by the original jQuery.tipsy by Jason Frame
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @fileoverview - Bootstrap's tooltip plugin.
+ * (Inspired by jQuery.tipsy by Jason Frame)
+ *
+ * Public Methods & Properties:
+ *
+ * + $.tooltip
+ * + $.tooltip.noConflict
+ * + $.tooltip.Constructor
+ * + $.tooltip.Constructor.VERSION
+ * + $.tooltip.Constructor.Defaults
+ * + $.tooltip.Constructor.Defaults.container
+ * + $.tooltip.Constructor.Defaults.animation
+ * + $.tooltip.Constructor.Defaults.placement
+ * + $.tooltip.Constructor.Defaults.selector
+ * + $.tooltip.Constructor.Defaults.template
+ * + $.tooltip.Constructor.Defaults.trigger
+ * + $.tooltip.Constructor.Defaults.title
+ * + $.tooltip.Constructor.Defaults.delay
+ * + $.tooltip.Constructor.Defaults.html
+ * + $.tooltip.Constructor.Defaults.viewport
+ * + $.tooltip.Constructor.Defaults.viewport.selector
+ * + $.tooltip.Constructor.Defaults.viewport.padding
+ * + $.tooltip.Constructor.prototype.enable
+ * + $.tooltip.Constructor.prototype.disable
+ * + $.tooltip.Constructor.prototype.destroy
+ * + $.tooltip.Constructor.prototype.toggleEnabled
+ * + $.tooltip.Constructor.prototype.toggle
+ * + $.tooltip.Constructor.prototype.show
+ * + $.tooltip.Constructor.prototype.hide
+ *
+ * ========================================================================
+ */
+'use strict';
-+function ($) {
- 'use strict';
- // TOOLTIP PUBLIC CLASS DEFINITION
- // ===============================
+/**
+ * Our tooltip class.
+ * @param {Element!} element
+ * @param {Object=} opt_config
+ * @constructor
+ */
+var Tooltip = function (element, opt_config) {
- var Tooltip = function (element, options) {
- this.type =
- this.options =
- this.enabled =
- this.timeout =
- this.hoverState =
- this.$element = null
+ /** @private {boolean} */
+ this._isEnabled = true
- this.init('tooltip', element, options)
- }
+ /** @private {number} */
+ this._timeout = 0
- Tooltip.VERSION = '3.3.2'
-
- Tooltip.TRANSITION_DURATION = 150
-
- Tooltip.DEFAULTS = {
- animation: true,
- placement: 'top',
- selector: false,
- template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
- trigger: 'hover focus',
- title: '',
- delay: 0,
- html: false,
- container: false,
- viewport: {
- selector: 'body',
- padding: 0
- }
- }
+ /** @private {string} */
+ this._hoverState = ''
- Tooltip.prototype.init = function (type, element, options) {
- this.enabled = true
- this.type = type
- this.$element = $(element)
- this.options = this.getOptions(options)
- this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)
+ /** @protected {Element} */
+ this.element = element
- var triggers = this.options.trigger.split(' ')
+ /** @protected {Object} */
+ this.config = this._getConfig(opt_config)
- for (var i = triggers.length; i--;) {
- var trigger = triggers[i]
+ /** @protected {Element} */
+ this.tip = null
- if (trigger == 'click') {
- this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
- } else if (trigger != 'manual') {
- var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
- var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
+ /** @protected {Element} */
+ this.arrow = null
- this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
- this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
- }
- }
+ if (this.config['viewport']) {
- this.options.selector ?
- (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
- this.fixTitle()
- }
+ /** @private {Element} */
+ this._viewport = $(this.config['viewport']['selector'] || this.config['viewport'])[0]
- Tooltip.prototype.getDefaults = function () {
- return Tooltip.DEFAULTS
}
- Tooltip.prototype.getOptions = function (options) {
- options = $.extend({}, this.getDefaults(), this.$element.data(), options)
+ this._setListeners()
+}
+
+
+/**
+ * @const
+ * @type {string}
+ */
+Tooltip['VERSION'] = '4.0.0'
+
+
+/**
+ * @const
+ * @type {Object}
+ */
+Tooltip['Defaults'] = {
+ 'container' : false,
+ 'animation' : true,
+ 'placement' : 'top',
+ 'selector' : false,
+ 'template' : '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
+ 'trigger' : 'hover focus',
+ 'title' : '',
+ 'delay' : 0,
+ 'html' : false,
+ 'viewport': {
+ 'selector': 'body',
+ 'padding' : 0
+ }
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @protected
+ */
+Tooltip.Direction = {
+ TOP: 'top',
+ LEFT: 'left',
+ RIGHT: 'right',
+ BOTTOM: 'bottom'
+}
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Tooltip._NAME = 'tooltip'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Tooltip._DATA_KEY = 'bs.tooltip'
+
+
+/**
+ * @const
+ * @type {number}
+ * @private
+ */
+Tooltip._TRANSITION_DURATION = 150
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Tooltip._HoverState = {
+ IN: 'in',
+ OUT: 'out'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Tooltip._Event = {
+ HIDE : 'hide.bs.tooltip',
+ HIDDEN : 'hidden.bs.tooltip',
+ SHOW : 'show.bs.tooltip',
+ SHOWN : 'shown.bs.tooltip'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Tooltip._ClassName = {
+ FADE : 'fade',
+ IN : 'in'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Tooltip._Selector = {
+ TOOLTIP : '.tooltip',
+ TOOLTIP_INNER : '.tooltip-inner',
+ TOOLTIP_ARROW : '.tooltip-arrow'
+}
+
+
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+Tooltip._JQUERY_NO_CONFLICT = $.fn[Tooltip._NAME]
+
+
+/**
+ * @param {Object=} opt_config
+ * @this {jQuery}
+ * @return {jQuery}
+ * @private
+ */
+Tooltip._jQueryInterface = function (opt_config) {
+ return this.each(function () {
+ var data = $(this).data(Tooltip._DATA_KEY)
+ var config = typeof opt_config == 'object' ? opt_config : null
+
+ if (!data && opt_config == 'destroy') {
+ return
+ }
- if (options.delay && typeof options.delay == 'number') {
- options.delay = {
- show: options.delay,
- hide: options.delay
- }
+ if (!data) {
+ data = new Tooltip(this, config)
+ $(this).data(Tooltip._DATA_KEY, data)
}
- return options
+ if (typeof opt_config === 'string') {
+ data[opt_config]()
+ }
+ })
+}
+
+
+/**
+ * Enable tooltip
+ */
+Tooltip.prototype['enable'] = function () {
+ this._isEnabled = true
+}
+
+
+/**
+ * Disable tooltip
+ */
+Tooltip.prototype['disable'] = function () {
+ this._isEnabled = false
+}
+
+
+/**
+ * Toggle the tooltip enable state
+ */
+Tooltip.prototype['toggleEnabled'] = function () {
+ this._isEnabled = !this._isEnabled
+}
+
+/**
+ * Toggle the tooltips display
+ * @param {Event} opt_event
+ */
+Tooltip.prototype['toggle'] = function (opt_event) {
+ var context = this
+ var dataKey = this.getDataKey()
+
+ if (opt_event) {
+ context = $(opt_event.currentTarget).data(dataKey)
+
+ if (!context) {
+ context = new this.constructor(opt_event.currentTarget, this._getDelegateConfig())
+ $(opt_event.currentTarget).data(dataKey, context)
+ }
}
- Tooltip.prototype.getDelegateOptions = function () {
- var options = {}
- var defaults = this.getDefaults()
+ $(context.getTipElement()).hasClass(Tooltip._ClassName.IN) ?
+ context._leave(null, context) :
+ context._enter(null, context)
+}
- this._options && $.each(this._options, function (key, value) {
- if (defaults[key] != value) options[key] = value
- })
- return options
- }
+/**
+ * Remove tooltip functionality
+ */
+Tooltip.prototype['destroy'] = function () {
+ clearTimeout(this._timeout)
+ this['hide'](function () {
+ $(this.element)
+ .off(Tooltip._Selector.TOOLTIP)
+ .removeData(this.getDataKey())
+ }.bind(this))
+}
+
+
+/**
+ * Show the tooltip
+ * todo (fat): ~fuck~ this is a big function - refactor out all of positioning logic
+ * and replace with external lib
+ */
+Tooltip.prototype['show'] = function () {
+ var showEvent = $.Event(this.getEventObject().SHOW)
+
+ if (this.isWithContent() && this._isEnabled) {
+ $(this.element).trigger(showEvent)
- Tooltip.prototype.enter = function (obj) {
- var self = obj instanceof this.constructor ?
- obj : $(obj.currentTarget).data('bs.' + this.type)
+ var isInTheDom = $.contains(this.element.ownerDocument.documentElement, this.element)
- if (self && self.$tip && self.$tip.is(':visible')) {
- self.hoverState = 'in'
+ if (showEvent.isDefaultPrevented() || !isInTheDom) {
return
}
- if (!self) {
- self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
- $(obj.currentTarget).data('bs.' + this.type, self)
+ var tip = this.getTipElement()
+ var tipId = Bootstrap.getUID(this.getName())
+
+ tip.setAttribute('id', tipId)
+ this.element.setAttribute('aria-describedby', tipId)
+
+ this.setContent()
+
+ if (this.config['animation']) {
+ $(tip).addClass(Tooltip._ClassName.FADE)
}
- clearTimeout(self.timeout)
+ var placement = typeof this.config['placement'] == 'function' ?
+ this.config['placement'].call(this, tip, this.element) :
+ this.config['placement']
- self.hoverState = 'in'
+ var autoToken = /\s?auto?\s?/i
+ var isWithAutoPlacement = autoToken.test(placement)
- if (!self.options.delay || !self.options.delay.show) return self.show()
+ if (isWithAutoPlacement) {
+ placement = placement.replace(autoToken, '') || Tooltip.Direction.TOP
+ }
- self.timeout = setTimeout(function () {
- if (self.hoverState == 'in') self.show()
- }, self.options.delay.show)
- }
+ if (tip.parentNode && tip.parentNode.nodeType == Node.ELEMENT_NODE) {
+ tip.parentNode.removeChild(tip)
+ }
+
+ tip.style.top = 0
+ tip.style.left = 0
+ tip.style.display = 'block'
+
+ $(tip).addClass(Tooltip._NAME + '-' + placement)
- Tooltip.prototype.leave = function (obj) {
- var self = obj instanceof this.constructor ?
- obj : $(obj.currentTarget).data('bs.' + this.type)
+ $(tip).data(this.getDataKey(), this)
- if (!self) {
- self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
- $(obj.currentTarget).data('bs.' + this.type, self)
+ if (this.config['container']) {
+ $(this.config['container'])[0].appendChild(tip)
+ } else {
+ this.element.parentNode.insertBefore(tip, this.element.nextSibling)
}
- clearTimeout(self.timeout)
+ var position = this._getPosition()
+ var actualWidth = tip.offsetWidth
+ var actualHeight = tip.offsetHeight
+
+ var calculatedPlacement = this._getCalculatedAutoPlacement(isWithAutoPlacement, placement, position, actualWidth, actualHeight)
+ var calculatedOffset = this._getCalculatedOffset(calculatedPlacement, position, actualWidth, actualHeight)
- self.hoverState = 'out'
+ this._applyCalculatedPlacement(calculatedOffset, calculatedPlacement)
- if (!self.options.delay || !self.options.delay.hide) return self.hide()
+ var complete = function () {
+ var prevHoverState = this.hoverState
+ $(this.element).trigger(this.getEventObject().SHOWN)
+ this.hoverState = null
- self.timeout = setTimeout(function () {
- if (self.hoverState == 'out') self.hide()
- }, self.options.delay.hide)
+ if (prevHoverState == 'out') this._leave(null, this)
+ }.bind(this)
+
+ Bootstrap.transition && $(this._tip).hasClass(Tooltip._ClassName.FADE) ?
+ $(this._tip)
+ .one(Bootstrap.TRANSITION_END, complete)
+ .emulateTransitionEnd(Tooltip._TRANSITION_DURATION) :
+ complete()
}
+}
- Tooltip.prototype.show = function () {
- var e = $.Event('show.bs.' + this.type)
- if (this.hasContent() && this.enabled) {
- this.$element.trigger(e)
+/**
+ * Hide the tooltip breh
+ */
+Tooltip.prototype['hide'] = function (callback) {
+ var tip = this.getTipElement()
+ var hideEvent = $.Event(this.getEventObject().HIDE)
- var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
- if (e.isDefaultPrevented() || !inDom) return
- var that = this
+ var complete = function () {
+ if (this._hoverState != Tooltip._HoverState.IN) {
+ tip.parentNode.removeChild(tip)
+ }
- var $tip = this.tip()
+ this.element.removeAttribute('aria-describedby')
+ $(this.element).trigger(this.getEventObject().HIDDEN)
+
+ if (callback) {
+ callback()
+ }
+ }.bind(this)
- var tipId = this.getUID(this.type)
+ $(this.element).trigger(hideEvent)
- this.setContent()
- $tip.attr('id', tipId)
- this.$element.attr('aria-describedby', tipId)
+ if (hideEvent.isDefaultPrevented()) return
- if (this.options.animation) $tip.addClass('fade')
+ $(tip).removeClass(Tooltip._ClassName.IN)
- var placement = typeof this.options.placement == 'function' ?
- this.options.placement.call(this, $tip[0], this.$element[0]) :
- this.options.placement
+ if (Bootstrap.transition && $(this._tip).hasClass(Tooltip._ClassName.FADE)) {
+ $(tip)
+ .one(Bootstrap.TRANSITION_END, complete)
+ .emulateTransitionEnd(Tooltip._TRANSITION_DURATION)
+ } else {
+ complete()
+ }
- var autoToken = /\s?auto?\s?/i
- var autoPlace = autoToken.test(placement)
- if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
+ this._hoverState = ''
+}
- $tip
- .detach()
- .css({ top: 0, left: 0, display: 'block' })
- .addClass(this.type + '-' + placement)
- .data('bs.' + this.type, this)
- this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
+/**
+ * @return {string}
+ */
+Tooltip.prototype['getHoverState'] = function (callback) {
+ return this._hoverState
+}
- var pos = this.getPosition()
- var actualWidth = $tip[0].offsetWidth
- var actualHeight = $tip[0].offsetHeight
- if (autoPlace) {
- var origPlacement = placement
- var $container = this.options.container ? $(this.options.container) : this.$element.parent()
- var containerDim = this.getPosition($container)
+/**
+ * @return {string}
+ * @protected
+ */
+Tooltip.prototype.getName = function () {
+ return Tooltip._NAME
+}
- placement = placement == 'bottom' && pos.bottom + actualHeight > containerDim.bottom ? 'top' :
- placement == 'top' && pos.top - actualHeight < containerDim.top ? 'bottom' :
- placement == 'right' && pos.right + actualWidth > containerDim.width ? 'left' :
- placement == 'left' && pos.left - actualWidth < containerDim.left ? 'right' :
- placement
- $tip
- .removeClass(this.type + '-' + origPlacement)
- .addClass(this.type + '-' + placement)
- }
+/**
+ * @return {string}
+ * @protected
+ */
+Tooltip.prototype.getDataKey = function () {
+ return Tooltip._DATA_KEY
+}
- var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
- this.applyPlacement(calculatedOffset, placement)
+/**
+ * @return {Object}
+ * @protected
+ */
+Tooltip.prototype.getEventObject = function () {
+ return Tooltip._Event
+}
- var complete = function () {
- var prevHoverState = that.hoverState
- that.$element.trigger('shown.bs.' + that.type)
- that.hoverState = null
- if (prevHoverState == 'out') that.leave(that)
- }
+/**
+ * @return {string}
+ * @protected
+ */
+Tooltip.prototype.getTitle = function () {
+ var title = this.element.getAttribute('data-original-title')
- $.support.transition && this.$tip.hasClass('fade') ?
- $tip
- .one('bsTransitionEnd', complete)
- .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
- complete()
- }
+ if (!title) {
+ title = typeof this.config['title'] === 'function' ?
+ this.config['title'].call(this.element) :
+ this.config['title']
}
- Tooltip.prototype.applyPlacement = function (offset, placement) {
- var $tip = this.tip()
- var width = $tip[0].offsetWidth
- var height = $tip[0].offsetHeight
-
- // manually read margins because getBoundingClientRect includes difference
- var marginTop = parseInt($tip.css('margin-top'), 10)
- var marginLeft = parseInt($tip.css('margin-left'), 10)
-
- // we must check for NaN for IE9
- if (isNaN(marginTop)) marginTop = 0
- if (isNaN(marginLeft)) marginLeft = 0
-
- offset.top = offset.top + marginTop
- offset.left = offset.left + marginLeft
-
- // $.fn.offset doesn't round pixel values
- // so we use setOffset directly with our own function B-0
- $.offset.setOffset($tip[0], $.extend({
- using: function (props) {
- $tip.css({
- top: Math.round(props.top),
- left: Math.round(props.left)
- })
- }
- }, offset), 0)
-
- $tip.addClass('in')
-
- // check to see if placing tip in new offset caused the tip to resize itself
- var actualWidth = $tip[0].offsetWidth
- var actualHeight = $tip[0].offsetHeight
-
- if (placement == 'top' && actualHeight != height) {
- offset.top = offset.top + height - actualHeight
- }
+ return /** @type {string} */ (title)
+}
- var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
- if (delta.left) offset.left += delta.left
- else offset.top += delta.top
+/**
+ * @return {Element}
+ * @protected
+ */
+Tooltip.prototype.getTipElement = function () {
+ return (this._tip = this._tip || $(this.config['template'])[0])
+}
- var isVertical = /top|bottom/.test(placement)
- var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
- var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
- $tip.offset(offset)
- this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
- }
+/**
+ * @return {Element}
+ * @protected
+ */
+Tooltip.prototype.getArrowElement = function () {
+ return (this.arrow = this.arrow || $(this.getTipElement()).find(Tooltip._Selector.TOOLTIP_ARROW)[0])
+}
- Tooltip.prototype.replaceArrow = function (delta, dimension, isHorizontal) {
- this.arrow()
- .css(isHorizontal ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
- .css(isHorizontal ? 'top' : 'left', '')
- }
- Tooltip.prototype.setContent = function () {
- var $tip = this.tip()
- var title = this.getTitle()
+/**
+ * @return {boolean}
+ * @protected
+ */
+Tooltip.prototype.isWithContent = function () {
+ return !!this.getTitle()
+}
- $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
- $tip.removeClass('fade in tooltip-top tooltip-bottom tooltip-left tooltip-right')
- }
- Tooltip.prototype.hide = function (callback) {
- var that = this
- var $tip = this.tip()
- var e = $.Event('hide.bs.' + this.type)
-
- function complete() {
- if (that.hoverState != 'in') $tip.detach()
- that.$element
- .removeAttr('aria-describedby')
- .trigger('hidden.bs.' + that.type)
- callback && callback()
- }
+/**
+ * @protected
+ */
+Tooltip.prototype.setContent = function () {
+ var tip = this.getTipElement()
+ var title = this.getTitle()
- this.$element.trigger(e)
+ $(tip).find(Tooltip._Selector.TOOLTIP_INNER)[0][this.config['html'] ? 'innerHTML' : 'innerText'] = title
- if (e.isDefaultPrevented()) return
+ $(tip)
+ .removeClass(Tooltip._ClassName.FADE)
+ .removeClass(Tooltip._ClassName.IN)
- $tip.removeClass('in')
+ for (var direction in Tooltip.Direction) {
+ $(tip).removeClass(Tooltip._NAME + '-' + direction)
+ }
+}
- $.support.transition && this.$tip.hasClass('fade') ?
- $tip
- .one('bsTransitionEnd', complete)
- .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
- complete()
- this.hoverState = null
+/**
+ * @private
+ */
+Tooltip.prototype._setListeners = function () {
+ var triggers = this.config['trigger'].split(' ')
- return this
- }
+ triggers.forEach(function (trigger) {
+ if (trigger == 'click') {
+ $(this.element).on('click.bs.tooltip', this.config['selector'], this['toggle'].bind(this))
- Tooltip.prototype.fixTitle = function () {
- var $e = this.$element
- if ($e.attr('title') || typeof ($e.attr('data-original-title')) != 'string') {
- $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
+ } else if (trigger != 'manual') {
+ var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
+ var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
+
+ $(this.element)
+ .on(eventIn + '.bs.tooltip', this.config['selector'], this._enter.bind(this))
+ .on(eventOut + '.bs.tooltip', this.config['selector'], this._leave.bind(this))
}
- }
+ }.bind(this))
- Tooltip.prototype.hasContent = function () {
- return this.getTitle()
+ if (this.config['selector']) {
+ this.config = $.extend({}, this.config, { 'trigger': 'manual', 'selector': '' })
+ } else {
+ this._fixTitle()
}
+}
- Tooltip.prototype.getPosition = function ($element) {
- $element = $element || this.$element
- var el = $element[0]
- var isBody = el.tagName == 'BODY'
+/**
+ * @param {Object=} opt_config
+ * @return {Object}
+ * @private
+ */
+Tooltip.prototype._getConfig = function (opt_config) {
+ var config = $.extend({}, this.constructor['Defaults'], $(this.element).data(), opt_config)
- var elRect = el.getBoundingClientRect()
- if (elRect.width == null) {
- // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
- elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
+ if (config['delay'] && typeof config['delay'] == 'number') {
+ config['delay'] = {
+ 'show': config['delay'],
+ 'hide': config['delay']
}
- var elOffset = isBody ? { top: 0, left: 0 } : $element.offset()
- var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
- var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
-
- return $.extend({}, elRect, scroll, outerDims, elOffset)
}
- Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
- return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
- placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
- placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
- /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
+ return config
+}
- }
- Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
- var delta = { top: 0, left: 0 }
- if (!this.$viewport) return delta
-
- var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
- var viewportDimensions = this.getPosition(this.$viewport)
-
- if (/right|left/.test(placement)) {
- var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll
- var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
- if (topEdgeOffset < viewportDimensions.top) { // top overflow
- delta.top = viewportDimensions.top - topEdgeOffset
- } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
- delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
- }
- } else {
- var leftEdgeOffset = pos.left - viewportPadding
- var rightEdgeOffset = pos.left + viewportPadding + actualWidth
- if (leftEdgeOffset < viewportDimensions.left) { // left overflow
- delta.left = viewportDimensions.left - leftEdgeOffset
- } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
- delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
- }
+/**
+ * @return {Object}
+ * @private
+ */
+Tooltip.prototype._getDelegateConfig = function () {
+ var config = {}
+ var defaults = this.constructor['Defaults']
+
+ if (this.config) {
+ for (var key in this.config) {
+ var value = this.config[key]
+ if (defaults[key] != value) config[key] = value
}
+ }
+
+ return config
+}
+
+
+
+/**
+ * @param {boolean} isWithAutoPlacement
+ * @param {string} placement
+ * @param {Object} position
+ * @param {number} actualWidth
+ * @param {number} actualHeight
+ * @return {string}
+ * @private
+ */
+Tooltip.prototype._getCalculatedAutoPlacement = function (isWithAutoPlacement, placement, position, actualWidth, actualHeight) {
+ if (isWithAutoPlacement) {
+ var originalPlacement = placement
+ var container = this.config['container'] ? $(this.config['container'])[0] : this.element.parentNode
+ var containerDim = this._getPosition(/** @type {Element} */ (container))
+
+ placement = placement == Tooltip.Direction.BOTTOM && position.bottom + actualHeight > containerDim.bottom ? Tooltip.Direction.TOP :
+ placement == Tooltip.Direction.TOP && position.top - actualHeight < containerDim.top ? Tooltip.Direction.BOTTOM :
+ placement == Tooltip.Direction.RIGHT && position.right + actualWidth > containerDim.width ? Tooltip.Direction.LEFT :
+ placement == Tooltip.Direction.LEFT && position.left - actualWidth < containerDim.left ? Tooltip.Direction.RIGHT :
+ placement
+
+ $(this._tip)
+ .removeClass(Tooltip._NAME + '-' + originalPlacement)
+ .addClass(Tooltip._NAME + '-' + placement)
+ }
+ return placement
+}
+
+
+/**
+ * @param {string} placement
+ * @param {Object} position
+ * @param {number} actualWidth
+ * @param {number} actualHeight
+ * @return {{left: number, top: number}}
+ * @private
+ */
+Tooltip.prototype._getCalculatedOffset = function (placement, position, actualWidth, actualHeight) {
+ return placement == Tooltip.Direction.BOTTOM ? { top: position.top + position.height, left: position.left + position.width / 2 - actualWidth / 2 } :
+ placement == Tooltip.Direction.TOP ? { top: position.top - actualHeight, left: position.left + position.width / 2 - actualWidth / 2 } :
+ placement == Tooltip.Direction.LEFT ? { top: position.top + position.height / 2 - actualHeight / 2, left: position.left - actualWidth } :
+ /* placement == Tooltip.Direction.RIGHT */ { top: position.top + position.height / 2 - actualHeight / 2, left: position.left + position.width }
+}
+
+
+/**
+ * @param {string} placement
+ * @param {Object} position
+ * @param {number} actualWidth
+ * @param {number} actualHeight
+ * @return {Object}
+ * @private
+ */
+Tooltip.prototype._getViewportAdjustedDelta = function (placement, position, actualWidth, actualHeight) {
+ var delta = { top: 0, left: 0 }
+
+ if (!this._viewport) {
return delta
}
- Tooltip.prototype.getTitle = function () {
- var title
- var $e = this.$element
- var o = this.options
+ var viewportPadding = this.config['viewport'] && this.config['viewport']['padding'] || 0
+ var viewportDimensions = this._getPosition(this._viewport)
- title = $e.attr('data-original-title')
- || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
+ if (placement === Tooltip.Direction.RIGHT || placement === Tooltip.Direction.LEFT) {
+ var topEdgeOffset = position.top - viewportPadding - viewportDimensions.scroll
+ var bottomEdgeOffset = position.top + viewportPadding - viewportDimensions.scroll + actualHeight
- return title
+ if (topEdgeOffset < viewportDimensions.top) { // top overflow
+ delta.top = viewportDimensions.top - topEdgeOffset
+
+ } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
+ delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
+ }
+
+ } else {
+ var leftEdgeOffset = position.left - viewportPadding
+ var rightEdgeOffset = position.left + viewportPadding + actualWidth
+
+ if (leftEdgeOffset < viewportDimensions.left) { // left overflow
+ delta.left = viewportDimensions.left - leftEdgeOffset
+
+ } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
+ delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
+ }
}
- Tooltip.prototype.getUID = function (prefix) {
- do prefix += ~~(Math.random() * 1000000)
- while (document.getElementById(prefix))
- return prefix
+ return delta
+}
+
+
+/**
+ * @param {Element=} opt_element
+ * @return {Object}
+ * @private
+ */
+Tooltip.prototype._getPosition = function (opt_element) {
+ var element = opt_element || this.element
+ var isBody = element.tagName == 'BODY'
+ var rect = element.getBoundingClientRect()
+ var offset = isBody ? { top: 0, left: 0 } : $(element).offset()
+ var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : this.element.scrollTop }
+ var outerDims = isBody ? { width: window.innerWidth, height: window.innerHeight } : null
+
+ return $.extend({}, rect, scroll, outerDims, offset)
+}
+
+
+/**
+ * @param {{left: number, top: number}} offset
+ * @param {string} placement
+ * @private
+ */
+Tooltip.prototype._applyCalculatedPlacement = function (offset, placement) {
+ var tip = this.getTipElement()
+ var width = tip.offsetWidth
+ var height = tip.offsetHeight
+
+ // manually read margins because getBoundingClientRect includes difference
+ var marginTop = parseInt(tip.style.marginTop, 10)
+ var marginLeft = parseInt(tip.style.marginLeft, 10)
+
+ // we must check for NaN for ie 8/9
+ if (isNaN(marginTop)) {
+ marginTop = 0
+ }
+ if (isNaN(marginLeft)) {
+ marginLeft = 0
}
- Tooltip.prototype.tip = function () {
- return (this.$tip = this.$tip || $(this.options.template))
+ offset.top = offset.top + marginTop
+ offset.left = offset.left + marginLeft
+
+ // $.fn.offset doesn't round pixel values
+ // so we use setOffset directly with our own function B-0
+ $.offset.setOffset(tip, $.extend({
+ using: function (props) {
+ tip.style.top = Math.round(props.top) + 'px'
+ tip.style.left = Math.round(props.left) + 'px'
+ }
+ }, offset), 0)
+
+ $(tip).addClass(Tooltip._ClassName.IN)
+
+ // check to see if placing tip in new offset caused the tip to resize itself
+ var actualWidth = tip.offsetWidth
+ var actualHeight = tip.offsetHeight
+
+ if (placement == Tooltip.Direction.TOP && actualHeight != height) {
+ offset.top = offset.top + height - actualHeight
}
- Tooltip.prototype.arrow = function () {
- return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
+ var delta = this._getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
+
+ if (delta.left) {
+ offset.left += delta.left
+ } else {
+ offset.top += delta.top
}
- Tooltip.prototype.enable = function () {
- this.enabled = true
+ var isVertical = placement === Tooltip.Direction.TOP || placement === Tooltip.Direction.BOTTOM
+ var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
+ var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
+
+ $(tip).offset(offset)
+
+ this._replaceArrow(arrowDelta, tip[arrowOffsetPosition], isVertical)
+}
+
+
+/**
+ * @param {number} delta
+ * @param {number} dimension
+ * @param {boolean} isHorizontal
+ * @private
+ */
+Tooltip.prototype._replaceArrow = function (delta, dimension, isHorizontal) {
+ var arrow = this.getArrowElement()
+
+ arrow.style[isHorizontal ? 'left' : 'top'] = 50 * (1 - delta / dimension) + '%'
+ arrow.style[isHorizontal ? 'top' : 'left'] = ''
+}
+
+
+
+/**
+ * @private
+ */
+Tooltip.prototype._fixTitle = function () {
+ if (this.element.getAttribute('title') || typeof this.element.getAttribute('data-original-title') != 'string') {
+ this.element.setAttribute('data-original-title', this.element.getAttribute('title') || '')
+ this.element.setAttribute('title', '')
}
+}
+
- Tooltip.prototype.disable = function () {
- this.enabled = false
+/**
+ * @param {Event=} opt_event
+ * @param {Object=} opt_context
+ * @private
+ */
+Tooltip.prototype._enter = function (opt_event, opt_context) {
+ var dataKey = this.getDataKey()
+ var context = opt_context || $(opt_event.currentTarget).data(dataKey)
+
+ if (context && context._tip && context._tip.offsetWidth) {
+ context._hoverState = Tooltip._HoverState.IN
+ return
}
- Tooltip.prototype.toggleEnabled = function () {
- this.enabled = !this.enabled
+ if (!context) {
+ context = new this.constructor(opt_event.currentTarget, this._getDelegateConfig())
+ $(opt_event.currentTarget).data(dataKey, context)
}
- Tooltip.prototype.toggle = function (e) {
- var self = this
- if (e) {
- self = $(e.currentTarget).data('bs.' + this.type)
- if (!self) {
- self = new this.constructor(e.currentTarget, this.getDelegateOptions())
- $(e.currentTarget).data('bs.' + this.type, self)
- }
- }
+ clearTimeout(context._timeout)
- self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
- }
+ context._hoverState = Tooltip._HoverState.IN
- Tooltip.prototype.destroy = function () {
- var that = this
- clearTimeout(this.timeout)
- this.hide(function () {
- that.$element.off('.' + that.type).removeData('bs.' + that.type)
- })
+ if (!context.config['delay'] || !context.config['delay']['show']) {
+ context['show']()
+ return
}
+ context._timeout = setTimeout(function () {
+ if (context._hoverState == Tooltip._HoverState.IN) {
+ context['show']()
+ }
+ }, context.config['delay']['show'])
+}
+
+
+/**
+ * @param {Event=} opt_event
+ * @param {Object=} opt_context
+ * @private
+ */
+Tooltip.prototype._leave = function (opt_event, opt_context) {
+ var dataKey = this.getDataKey()
+ var context = opt_context || $(opt_event.currentTarget).data(dataKey)
+
+ if (!context) {
+ context = new this.constructor(opt_event.currentTarget, this._getDelegateConfig())
+ $(opt_event.currentTarget).data(dataKey, context)
+ }
- // TOOLTIP PLUGIN DEFINITION
- // =========================
+ clearTimeout(context._timeout)
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.tooltip')
- var options = typeof option == 'object' && option
+ context._hoverState = Tooltip._HoverState.OUT
- if (!data && option == 'destroy') return
- if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
- if (typeof option == 'string') data[option]()
- })
+ if (!context.config['delay'] || !context.config['delay']['hide']) {
+ context['hide']()
+ return
}
- var old = $.fn.tooltip
+ context._timeout = setTimeout(function () {
+ if (context._hoverState == Tooltip._HoverState.OUT) {
+ context['hide']()
+ }
+ }, context.config['delay']['hide'])
+}
- $.fn.tooltip = Plugin
- $.fn.tooltip.Constructor = Tooltip
- // TOOLTIP NO CONFLICT
- // ===================
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Tooltip._NAME] = Tooltip._jQueryInterface
+
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Tooltip._NAME]['Constructor'] = Tooltip
- $.fn.tooltip.noConflict = function () {
- $.fn.tooltip = old
- return this
- }
-}(jQuery);
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Tooltip._NAME]['noConflict'] = function () {
+ $.fn[Tooltip._NAME] = Tooltip._JQUERY_NO_CONFLICT
+ return this
+}
diff --git a/js/util.js b/js/util.js
new file mode 100644
index 0000000000..294a5a9601
--- /dev/null
+++ b/js/util.js
@@ -0,0 +1,165 @@
+/** =======================================================================
+ * Bootstrap: util.js v4.0.0
+ * http://getbootstrap.com/javascript/#alerts
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ========================================================================
+ * @fileoverview - Bootstrap's private util helper. Adds private util
+ * helpers for things like accesibility and transitions. These methods are
+ * shared across all bootstrap plugins.
+ * ========================================================================
+ */
+
+'use strict';
+
+
+/**
+ * @type {Object}
+ */
+var Bootstrap = {}
+
+
+/**
+ * @const
+ * @type {string}
+ */
+Bootstrap.TRANSITION_END = 'bsTransitionEnd'
+
+
+/**
+ * @const
+ * @type {Object}
+ */
+Bootstrap.TransitionEndEvent = {
+ 'WebkitTransition' : 'webkitTransitionEnd',
+ 'MozTransition' : 'transitionend',
+ 'OTransition' : 'oTransitionEnd otransitionend',
+ 'transition' : 'transitionend'
+}
+
+
+/**
+ * @param {Function} childConstructor
+ * @param {Function} parentConstructor
+ */
+Bootstrap.inherits = function(childConstructor, parentConstructor) {
+ /** @constructor */
+ function tempConstructor() {}
+ tempConstructor.prototype = parentConstructor.prototype
+ childConstructor.prototype = new tempConstructor()
+ /** @override */
+ childConstructor.prototype.constructor = childConstructor
+}
+
+
+/**
+ * @param {Element} element
+ * @return {string|null}
+ */
+Bootstrap.getSelectorFromElement = function (element) {
+ var selector = element.getAttribute('data-target')
+
+ if (!selector) {
+ selector = element.getAttribute('href') || ''
+ selector = /^#[a-z]/i.test(selector) ? selector : null
+ }
+
+ return selector
+}
+
+
+/**
+ * @param {string} prefix
+ * @return {string}
+ */
+Bootstrap.getUID = function (prefix) {
+ do prefix += ~~(Math.random() * 1000000)
+ while (document.getElementById(prefix))
+ return prefix
+}
+
+
+/**
+ * @return {Object}
+ */
+Bootstrap.getSpecialTransitionEndEvent = function () {
+ return {
+ bindType: Bootstrap.transition.end,
+ delegateType: Bootstrap.transition.end,
+ handle: /** @param {jQuery.Event} event */ (function (event) {
+ if ($(event.target).is(this)) {
+ return event.handleObj.handler.apply(this, arguments)
+ }
+ })
+ }
+}
+
+
+/**
+ * @param {Element} element
+ */
+Bootstrap.reflow = function (element) {
+ new Function('bs',"return bs")(element.offsetHeight)
+}
+
+
+/**
+ * @return {Object|boolean}
+ */
+Bootstrap.transitionEndTest = function () {
+ if (window['QUnit']) {
+ return false
+ }
+
+ var el = document.createElement('bootstrap')
+ for (var name in Bootstrap.TransitionEndEvent) {
+ if (el.style[name] !== undefined) {
+ return { end: Bootstrap.TransitionEndEvent[name] }
+ }
+ }
+ return false
+}
+
+
+/**
+ * @param {number} duration
+ * @this {Element}
+ * @return {Object}
+ */
+Bootstrap.transitionEndEmulator = function (duration) {
+ var called = false
+
+ $(this).one(Bootstrap.TRANSITION_END, function () {
+ called = true
+ })
+
+ var callback = function () {
+ if (!called) {
+ $(this).trigger(Bootstrap.transition.end)
+ }
+ }.bind(this)
+
+ setTimeout(callback, duration)
+
+ return this
+}
+
+
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface
+ * ------------------------------------------------------------------------
+ */
+
+$.fn.emulateTransitionEnd = Bootstrap.transitionEndEmulator
+
+$(function () {
+ Bootstrap.transition = Bootstrap.transitionEndTest()
+
+ if (!Bootstrap.transition) {
+ return
+ }
+
+ $.event.special[Bootstrap.TRANSITION_END] = Bootstrap.getSpecialTransitionEndEvent()
+})