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
diff options
context:
space:
mode:
-rw-r--r--Gruntfile.js74
-rw-r--r--dist/js/bootstrap.js5347
-rw-r--r--dist/js/bootstrap.min.js74
-rw-r--r--dist/js/npm.js7
-rw-r--r--grunt/bs-commonjs-generator.js1
-rw-r--r--js/.jscsrc5
-rw-r--r--js/affix.js162
-rw-r--r--js/alert.js280
-rw-r--r--js/button.js279
-rw-r--r--js/carousel.js669
-rw-r--r--js/collapse.js526
-rw-r--r--js/dropdown.js377
-rw-r--r--js/externs/bootstrap.js6
-rw-r--r--js/externs/jQuery.js2159
-rw-r--r--js/modal.js771
-rw-r--r--js/popover.js317
-rw-r--r--js/scrollspy.js424
-rw-r--r--js/tab.js397
-rw-r--r--js/tests/closure.html83
-rw-r--r--js/tests/index.html4
-rw-r--r--js/tests/unit/affix.js107
-rw-r--r--js/tests/unit/button.js160
-rw-r--r--js/tests/unit/carousel.js300
-rw-r--r--js/tests/unit/collapse.js2
-rw-r--r--js/tests/unit/modal.js249
-rw-r--r--js/tests/unit/phantom.js4
-rw-r--r--js/tests/unit/popover.js44
-rw-r--r--js/tests/unit/tooltip.js657
-rw-r--r--js/tests/visual/affix-with-sticky-footer.html317
-rw-r--r--js/tests/visual/affix.html306
-rw-r--r--js/tests/visual/alert.html9
-rw-r--r--js/tests/visual/button.html13
-rw-r--r--js/tests/visual/carousel.html15
-rw-r--r--js/tests/visual/collapse.html9
-rw-r--r--js/tests/visual/dropdown.html9
-rw-r--r--js/tests/visual/modal.html90
-rw-r--r--js/tests/visual/popover.html9
-rw-r--r--js/tests/visual/scrollspy.html9
-rw-r--r--js/tests/visual/tab.html9
-rw-r--r--js/tests/visual/tooltip.html9
-rw-r--r--js/tooltip.js1124
-rw-r--r--js/util.js165
-rw-r--r--package.json4
43 files changed, 5145 insertions, 10437 deletions
diff --git a/Gruntfile.js b/Gruntfile.js
index a54d4781b2..f3c3af8f8e 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -108,80 +108,38 @@ module.exports = function (grunt) {
},
bootstrap: {
src: [
- 'js/util.js',
+ 'js/transition.js',
'js/alert.js',
'js/button.js',
'js/carousel.js',
'js/collapse.js',
'js/dropdown.js',
'js/modal.js',
- 'js/scrollspy.js',
'js/tooltip.js',
'js/popover.js',
- 'js/tab.js'
+ 'js/scrollspy.js',
+ 'js/tab.js',
+ 'js/affix.js'
],
dest: 'dist/js/<%= pkg.name %>.js'
}
},
- closureCompiler: {
-
+ uglify: {
options: {
- compilerFile: require('superstartup-closure-compiler').getPath(),
- checkModified: false,
-
- compilerOpts: {
- // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
- // jscomp_warning: 'reportUnknownTypes', someday - maybe we will get to 100% typed, this helps track those down
- compilation_level: 'ADVANCED_OPTIMIZATIONS',
- warning_level: 'verbose',
- summary_detail_level: 3,
- output_wrapper:
- '"<%= banner %><%= jqueryCheck %><%= jqueryVersionCheck %>'
- + '(function($){%output%})(jQuery);"',
- externs: 'js/externs/*.js'
- // jscs:enable requireCamelCaseOrUpperCaseIdentifiers
+ compress: {
+ warnings: false
},
-
- execOpts: {
- maxBuffer: 999999 * 1024
- },
-
- // [OPTIONAL] Java VM optimization options
- // see https://code.google.com/p/closure-compiler/wiki/FAQ#What_are_the_recommended_Java_VM_command-line_options?
- // Setting one of these to 'true' is strongly recommended,
- // and can reduce compile times by 50-80% depending on compilation size
- // and hardware.
- // On server-class hardware, such as with Github's Travis hook,
- // TieredCompilation should be used; on standard developer hardware,
- // d32 may be better. Set as appropriate for your environment.
- // Default for both is 'false'; do not set both to 'true'.
- d32: false, // will use 'java -client -d32 -jar compiler.jar'
- TieredCompilation: false // will use 'java -server -XX:+TieredCompilation -jar compiler.jar'
+ mangle: true,
+ preserveComments: 'some'
},
-
- targetName: {
- src: [
- 'js/util.js',
- 'js/alert.js',
- 'js/button.js',
- 'js/carousel.js',
- 'js/collapse.js',
- 'js/dropdown.js',
- 'js/modal.js',
- 'js/scrollspy.js',
- 'js/tooltip.js',
- 'js/popover.js',
- 'js/tab.js'
- ],
+ core: {
+ src: '<%= concat.bootstrap.dest %>',
dest: 'dist/js/<%= pkg.name %>.min.js'
- }
-
- },
-
- uglify: {
- options: {
- preserveComments: 'some'
+ },
+ customize: {
+ src: configBridge.paths.customizerJs,
+ dest: 'docs/assets/js/customize.min.js'
},
docsJs: {
src: configBridge.paths.docsJs,
@@ -443,7 +401,7 @@ module.exports = function (grunt) {
grunt.registerTask('test-js', ['jshint:core', 'jshint:test', 'jshint:grunt', 'jscs:core', 'jscs:test', 'jscs:grunt', 'qunit']);
// JS distribution task.
- grunt.registerTask('dist-js', ['concat', 'closureCompiler', 'commonjs']);
+ grunt.registerTask('dist-js', ['concat', 'uglify:core', 'commonjs']);
grunt.registerTask('test-scss', ['scsslint:scss']);
diff --git a/dist/js/bootstrap.js b/dist/js/bootstrap.js
index b22c41b9ac..a6827d652c 100644
--- a/dist/js/bootstrap.js
+++ b/dist/js/bootstrap.js
@@ -15,4417 +15,2348 @@ if (typeof jQuery === 'undefined') {
}
}(jQuery);
-/** =======================================================================
- * Bootstrap: util.js v4.0.0
- * http://getbootstrap.com/javascript/#alerts
+/* ========================================================================
+ * Bootstrap: transition.js v3.3.4
+ * http://getbootstrap.com/javascript/#transitions
* ========================================================================
* 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
-}
++function ($) {
+ 'use strict';
+ // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
+ // ============================================================
-/**
- * @param {string} prefix
- * @return {string}
- */
-Bootstrap.getUID = function (prefix) {
- do prefix += ~~(Math.random() * 1000000)
- while (document.getElementById(prefix))
- return prefix
-}
+ function transitionEnd() {
+ var el = document.createElement('bootstrap')
+ var transEndEventNames = {
+ WebkitTransition : 'webkitTransitionEnd',
+ MozTransition : 'transitionend',
+ OTransition : 'oTransitionEnd otransitionend',
+ transition : 'transitionend'
+ }
-/**
- * @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)
+ for (var name in transEndEventNames) {
+ if (el.style[name] !== undefined) {
+ return { end: transEndEventNames[name] }
}
- })
- }
-}
-
-
-/**
- * @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
+ return false // explicit for ie8 ( ._.)
}
- var el = document.createElement('bootstrap')
- for (var name in Bootstrap.TransitionEndEvent) {
- if (el.style[name] !== undefined) {
- return { end: Bootstrap.TransitionEndEvent[name] }
- }
+ // http://blog.alexmaccaw.com/css-transitions
+ $.fn.emulateTransitionEnd = function (duration) {
+ var called = false
+ var $el = this
+ $(this).one('bsTransitionEnd', function () { called = true })
+ var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
+ setTimeout(callback, duration)
+ return this
}
- return false
-}
+ $(function () {
+ $.support.transition = transitionEnd()
-/**
- * @param {number} duration
- * @this {Element}
- * @return {Object}
- */
-Bootstrap.transitionEndEmulator = function (duration) {
- var called = false
-
- $(this).one(Bootstrap.TRANSITION_END, function () {
- called = true
- })
+ if (!$.support.transition) return
- var callback = function () {
- if (!called) {
- $(this).trigger(Bootstrap.transition.end)
+ $.event.special.bsTransitionEnd = {
+ bindType: $.support.transition.end,
+ delegateType: $.support.transition.end,
+ handle: function (e) {
+ if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)
+ }
}
- }.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()
-})
+}(jQuery);
-/** =======================================================================
- * Bootstrap: alert.js v4.0.0
+/* ========================================================================
+ * Bootstrap: alert.js v3.3.4
* 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
- *
- * ========================================================================
- */
-
-'use strict';
-
-
-/**
- * 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))
- }
-}
-
-
-/**
- * @const
- * @type {string}
- */
-Alert['VERSION'] = '4.0.0'
+ * ======================================================================== */
-/**
- * @const
- * @type {string}
- * @private
- */
-Alert._NAME = 'alert'
-
-
-/**
- * @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
-
-
-/**
- * @const
- * @type {Function}
- * @private
- */
-Alert._JQUERY_NO_CONFLICT = $.fn[Alert._NAME]
-
++function ($) {
+ 'use strict';
-/**
- * @const
- * @enum {string}
- * @private
- */
-Alert._Event = {
- CLOSE : 'close.bs.alert',
- CLOSED : 'closed.bs.alert'
-}
+ // ALERT CLASS DEFINITION
+ // ======================
+ var dismiss = '[data-dismiss="alert"]'
+ var Alert = function (el) {
+ $(el).on('click', dismiss, this.close)
+ }
-/**
- * @const
- * @enum {string}
- * @private
- */
-Alert._ClassName = {
- ALERT : 'alert',
- FADE : 'fade',
- IN : 'in'
-}
+ Alert.VERSION = '3.3.4'
+ Alert.TRANSITION_DURATION = 150
-/**
- * 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)
+ Alert.prototype.close = function (e) {
+ var $this = $(this)
+ var selector = $this.attr('data-target')
- if (!data) {
- data = new Alert(this)
- $this.data(Alert._DATA_KEY, data)
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
}
- if (opt_config === 'close') {
- data[opt_config](this)
- }
- })
-}
+ var $parent = $(selector)
+ if (e) e.preventDefault()
-/**
- * Close the alert component
- * @param {Alert} alertInstance
- * @return {Function}
- * @private
- */
-Alert._handleDismiss = function (alertInstance) {
- return function (event) {
- if (event) {
- event.preventDefault()
+ if (!$parent.length) {
+ $parent = $this.closest('.alert')
}
- 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
+ $parent.trigger(e = $.Event('close.bs.alert'))
- this._removeElement(rootElement)
-}
+ if (e.isDefaultPrevented()) return
+ $parent.removeClass('in')
-/**
- * Tries to get the alert's root element
- * @return {Element}
- * @private
- */
-Alert.prototype._getRootElement = function (element) {
- var parent = false
- var selector = Bootstrap.getSelectorFromElement(element)
+ function removeElement() {
+ // detach from parent, fire event then clean up data
+ $parent.detach().trigger('closed.bs.alert').remove()
+ }
- if (selector) {
- parent = $(selector)[0]
+ $.support.transition && $parent.hasClass('fade') ?
+ $parent
+ .one('bsTransitionEnd', removeElement)
+ .emulateTransitionEnd(Alert.TRANSITION_DURATION) :
+ removeElement()
}
- if (!parent) {
- parent = $(element).closest('.' + Alert._ClassName.ALERT)[0]
- }
- return parent
-}
-
-
-/**
- * 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 PLUGIN DEFINITION
+ // =======================
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.alert')
-/**
- * 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
+ if (!data) $this.data('bs.alert', (data = new Alert(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
}
- $(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()
-}
-
+ var old = $.fn.alert
-/**
- * ------------------------------------------------------------------------
- * jQuery Interface + noConflict implementaiton
- * ------------------------------------------------------------------------
- */
+ $.fn.alert = Plugin
+ $.fn.alert.Constructor = Alert
-/**
- * @const
- * @type {Function}
- */
-$.fn[Alert._NAME] = Alert._jQueryInterface
+ // ALERT NO CONFLICT
+ // =================
-/**
- * @const
- * @type {Function}
- */
-$.fn[Alert._NAME]['Constructor'] = Alert
+ $.fn.alert.noConflict = function () {
+ $.fn.alert = old
+ return this
+ }
-/**
- * @return {Function}
- */
-$.fn[Alert._NAME]['noConflict'] = function () {
- $.fn[Alert._NAME] = Alert._JQUERY_NO_CONFLICT
- return Alert._jQueryInterface
-}
-
+ // ALERT DATA-API
+ // ==============
-/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
- */
+ $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
-$(document).on('click.bs.alert.data-api', Alert._DISMISS_SELECTOR, Alert._handleDismiss(new Alert))
+}(jQuery);
-/** =======================================================================
- * Bootstrap: button.js v4.0.0
+/* ========================================================================
+ * Bootstrap: button.js v3.3.4
* http://getbootstrap.com/javascript/#buttons
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ========================================================================
- * @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]
++function ($) {
+ 'use strict';
+ // BUTTON PUBLIC CLASS DEFINITION
+ // ==============================
-/**
- * @const
- * @enum {string}
- * @private
- */
-Button._ClassName = {
- ACTIVE : 'active',
- BUTTON : 'btn',
- FOCUS : 'focus'
-}
+ var Button = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, Button.DEFAULTS, options)
+ this.isLoading = false
+ }
+ Button.VERSION = '3.3.4'
-/**
- * @const
- * @enum {string}
- * @private
- */
-Button._Selector = {
- DATA_TOGGLE_CARROT : '[data-toggle^="button"]',
- DATA_TOGGLE : '[data-toggle="buttons"]',
- INPUT : 'input',
- ACTIVE : '.active',
- BUTTON : '.btn'
-}
+ 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()
-/**
- * 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)
+ state += 'Text'
- if (!data) {
- data = new Button(this)
- $(this).data(Button._DATA_KEY, data)
- }
-
- if (opt_config === 'toggle') {
- data[opt_config]()
- }
- })
-}
+ 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])
-/**
- * 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)
- }
- }
+ 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 (triggerChangeEvent) {
- input.checked = !$(this._element).hasClass(Button._ClassName.ACTIVE)
- $(this._element).trigger('change')
+ }, 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')) changed = false
+ $parent.find('.active').removeClass('active')
+ this.$element.addClass('active')
+ } else if ($input.prop('type') == 'checkbox') {
+ if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false
+ this.$element.toggleClass('active')
}
+ $input.prop('checked', this.$element.hasClass('active'))
+ if (changed) $input.trigger('change')
+ } else {
+ this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
+ this.$element.toggleClass('active')
}
- } else {
- this._element.setAttribute('aria-pressed', !$(this._element).hasClass(Button._ClassName.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
+ // ===============
- if (!$(button).hasClass(Button._ClassName.BUTTON)) {
- button = $(button).closest(Button._Selector.BUTTON)
- }
+ $(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')
+ if (!($(e.target).is('input[type="radio"]') || $(e.target).is('input[type="checkbox"]'))) 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))
+ })
- 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))
- })
+}(jQuery);
-/** =======================================================================
- * Bootstrap: carousel.js v4.0.0
+/* ========================================================================
+ * Bootstrap: carousel.js v3.3.4
* 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
-
- /** @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'
-}
++function ($) {
+ 'use strict';
-/**
- * @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)
-
- } else if (action) {
- data[action]()
+ // CAROUSEL CLASS DEFINITION
+ // =========================
- } 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 = null
+ this.sliding = null
+ this.interval = null
+ this.$active = null
+ 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)
-
- if (!selector) {
- return
+ 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))
}
- var target = $(selector)[0]
-
- if (!target || !$(target).hasClass(Carousel._ClassName.CAROUSEL)) {
- return
- }
+ Carousel.VERSION = '3.3.4'
- var config = $.extend({}, $(target).data(), $(this).data())
+ Carousel.TRANSITION_DURATION = 600
- var slideIndex = this.getAttribute('data-slide-to')
- if (slideIndex) {
- config.interval = false
+ Carousel.DEFAULTS = {
+ interval: 5000,
+ pause: 'hover',
+ wrap: true,
+ keyboard: true
}
- Carousel._jQueryInterface.call($(target), config)
+ 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
+ }
- if (slideIndex) {
- $(target).data(Carousel._DATA_KEY).to(slideIndex)
+ e.preventDefault()
}
- event.preventDefault()
-}
-
+ Carousel.prototype.cycle = function (e) {
+ e || (this.paused = false)
-/**
- * Advance the carousel to the next slide
- */
-Carousel.prototype['next'] = function () {
- if (!this._isSliding) {
- this._slide(Carousel._Direction.NEXT)
- }
-}
+ this.interval && clearInterval(this.interval)
+ this.options.interval
+ && !this.paused
+ && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
-/**
- * Return the carousel to the previous slide
- */
-Carousel.prototype['prev'] = function () {
- if (!this._isSliding) {
- this._slide(Carousel._Direction.PREVIOUS)
+ return this
}
-}
-
-/**
- * Pause the carousel cycle
- * @param {Event=} opt_event
- */
-Carousel.prototype['pause'] = function (opt_event) {
- if (!opt_event) {
- this._isPaused = true
+ Carousel.prototype.getItemIndex = function (item) {
+ this.$items = item.parent().children('.item')
+ return this.$items.index(item || this.$active)
}
- if ($(this._element).find(Carousel._Selector.NEXT_PREV)[0] && Bootstrap.transition) {
- $(this._element).trigger(Bootstrap.transition.end)
- this['cycle'](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)
}
- clearInterval(this._interval)
- this._interval = null
-}
+ Carousel.prototype.to = function (pos) {
+ var that = this
+ var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active'))
+ 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._interval) {
- clearInterval(this._interval)
- this._interval = null
- }
+ 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._config['interval'] && !this._isPaused) {
- this._interval = setInterval(this['next'].bind(this), this._config['interval'])
+ return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos))
}
-}
-
-
-/**
- * @return {Object}
- */
-Carousel.prototype['getConfig'] = function () {
- return this._config
-}
+ Carousel.prototype.pause = function (e) {
+ e || (this.paused = true)
-/**
- * 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]
+ if (this.$element.find('.next, .prev').length && $.support.transition) {
+ this.$element.trigger($.support.transition.end)
+ this.cycle(true)
+ }
- var activeIndex = this._getItemIndex(this._activeElement)
+ this.interval = clearInterval(this.interval)
- if (index > (this._items.length - 1) || index < 0) {
- return
+ return this
}
- if (this._isSliding) {
- $(this._element).one(Carousel._Event.SLID, function () { this.to(index) }.bind(this))
- return
+ Carousel.prototype.next = function () {
+ if (this.sliding) return
+ return this.slide('next')
}
- if (activeIndex == index) {
- this['pause']()
- this['cycle']()
- return
+ Carousel.prototype.prev = function () {
+ if (this.sliding) return
+ return this.slide('prev')
}
- var direction = index > activeIndex ?
- Carousel._Direction.NEXT :
- Carousel._Direction.PREVIOUS
+ Carousel.prototype.slide = function (type, next) {
+ var $active = this.$element.find('.item.active')
+ var $next = next || this.getItemForDirection(type, $active)
+ var isCycling = this.interval
+ var direction = type == 'next' ? 'left' : 'right'
+ var that = this
- this._slide(direction, this._items[index])
-}
+ if ($next.hasClass('active')) return (this.sliding = false)
+ 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
+
+ isCycling && this.pause()
+
+ 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)
+ }
-/**
- * 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.cycle()
- 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))
+ return this
}
-}
-/**
- * Keydown handler
- * @param {Event} event
- * @private
- */
-Carousel.prototype._keydown = function (event) {
- event.preventDefault()
+ // CAROUSEL PLUGIN DEFINITION
+ // ==========================
- if (/input|textarea/i.test(event.target.tagName)) return
+ 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
- switch (event.which) {
- case 37: this['prev'](); break
- case 39: this['next'](); break
- default: return
+ 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()
+ })
}
-}
+ var old = $.fn.carousel
-/**
- * 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)
-}
+ $.fn.carousel = Plugin
+ $.fn.carousel.Constructor = Carousel
-/**
- * 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))
+ // CAROUSEL NO CONFLICT
+ // ====================
- if (isGoingToWrap && !this._config['wrap']) {
- return activeElement
+ $.fn.carousel.noConflict = function () {
+ $.fn.carousel = old
+ return this
}
- var delta = direction == Carousel._Direction.PREVIOUS ? -1 : 1
- var itemIndex = (activeIndex + delta) % this._items.length
-
- return itemIndex === -1 ? this._items[this._items.length - 1] : this._items[itemIndex]
-}
-
-
-/**
- * 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)
+ // CAROUSEL DATA-API
+ // =================
- return slideEvent
-}
+ 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
+ Plugin.call($target, options)
-/**
- * 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)
+ if (slideIndex) {
+ $target.data('bs.carousel').to(slideIndex)
}
- }
-}
-
-
-/**
- * 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 directionalClassName = direction == Carousel._Direction.NEXT ?
- Carousel._ClassName.LEFT :
- Carousel._ClassName.RIGHT
-
- 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
+ e.preventDefault()
}
- this._isSliding = true
-
- if (isCycling) {
- this['pause']()
- }
-
- this._setActiveIndicatorElement(nextElement)
-
- var slidEvent = $.Event(Carousel._Event.SLID, { relatedTarget: nextElement, direction: directionalClassName })
-
- if (Bootstrap.transition && $(this._element).hasClass(Carousel._ClassName.SLIDE)) {
- $(nextElement).addClass(direction)
-
- Bootstrap.reflow(nextElement)
-
- $(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)
-
- 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']()
- }
-}
-
-
-/**
- * ------------------------------------------------------------------------
- * jQuery Interface + noConflict implementaiton
- * ------------------------------------------------------------------------
- */
-
-/**
- * @const
- * @type {Function}
- */
-$.fn[Carousel._NAME] = Carousel._jQueryInterface
-
-
-/**
- * @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)
+ $(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)
- Carousel._jQueryInterface.call($carousel, /** @type {Object} */ ($carousel.data()))
+ $(window).on('load', function () {
+ $('[data-ride="carousel"]').each(function () {
+ var $carousel = $(this)
+ Plugin.call($carousel, $carousel.data())
+ })
})
-})
-/** =======================================================================
- * Bootstrap: collapse.js v4.0.0
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: collapse.js v3.3.4
* 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';
-/**
- * Our collapse class.
- * @param {Element!} element
- * @param {Object=} opt_config
- * @constructor
- */
-var Collapse = function (element, opt_config) {
+ // COLLAPSE PUBLIC CLASS DEFINITION
+ // ================================
- /** @private {Element} */
- this._element = element
+ var Collapse = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, Collapse.DEFAULTS, options)
+ this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' +
+ '[data-toggle="collapse"][data-target="#' + element.id + '"]')
+ this.transitioning = null
- /** @private {Object} */
- this._config = $.extend({}, Collapse['Defaults'], opt_config)
+ if (this.options.parent) {
+ this.$parent = this.getParent()
+ } else {
+ this.addAriaAndCollapsedClass(this.$element, this.$trigger)
+ }
- /** @private {Element} */
- this._trigger = typeof this._config['trigger'] == 'string' ?
- $(this._config['trigger'])[0] : this._config['trigger']
+ if (this.options.toggle) this.toggle()
+ }
- /** @private {boolean} */
- this._isTransitioning = false
+ Collapse.VERSION = '3.3.4'
- /** @private {?Element} */
- this._parent = this._config['parent'] ? this._getParent() : null
+ Collapse.TRANSITION_DURATION = 350
- if (!this._config['parent']) {
- this._addAriaAndCollapsedClass(this._element, this._trigger)
+ Collapse.DEFAULTS = {
+ toggle: true
}
- if (this._config['toggle']) {
- this['toggle']()
+ Collapse.prototype.dimension = function () {
+ var hasWidth = this.$element.hasClass('width')
+ return hasWidth ? 'width' : 'height'
}
-}
-
-
-/**
- * @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'
+ 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 {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 (!data && config['toggle'] && opt_config == 'show') {
- config['toggle'] = false
+ if (actives && actives.length) {
+ activesData = actives.data('bs.collapse')
+ if (activesData && activesData.transitioning) return
}
- if (!data) {
- data = new Collapse(this, config)
- $this.data(Collapse._DATA_KEY, data)
- }
+ var startEvent = $.Event('show.bs.collapse')
+ this.$element.trigger(startEvent)
+ if (startEvent.isDefaultPrevented()) return
- if (typeof opt_config == 'string') {
- data[opt_config]()
+ if (actives && actives.length) {
+ Plugin.call(actives, 'hide')
+ activesData || actives.data('bs.collapse', null)
}
- })
-}
-
-
-/**
- * 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
- }
-
- var activesData, actives
+ this.transitioning = 1
- if (this._parent) {
- actives = $.makeArray($(Collapse._Selector.ACTIVES))
- if (!actives.length) {
- actives = null
+ var complete = function () {
+ this.$element
+ .removeClass('collapsing')
+ .addClass('collapse in')[dimension]('')
+ this.transitioning = 0
+ this.$element
+ .trigger('shown.bs.collapse')
}
- }
- if (actives) {
- activesData = $(actives).data(Collapse._DATA_KEY)
- if (activesData && activesData._isTransitioning) {
- return
- }
- }
+ if (!$.support.transition) return complete.call(this)
- var startEvent = $.Event(Collapse._Event.SHOW)
- $(this._element).trigger(startEvent)
- if (startEvent.isDefaultPrevented()) {
- return
- }
+ var scrollSize = $.camelCase(['scroll', dimension].join('-'))
- if (actives) {
- Collapse._jQueryInterface.call($(actives), 'hide')
- if (!activesData) {
- $(actives).data(Collapse._DATA_KEY, null)
- }
+ this.$element
+ .one('bsTransitionEnd', $.proxy(complete, this))
+ .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
}
- var dimension = this._getDimension()
+ Collapse.prototype.hide = function () {
+ if (this.transitioning || !this.$element.hasClass('in')) return
- $(this._element)
- .removeClass(Collapse._ClassName.COLLAPSE)
- .addClass(Collapse._ClassName.COLLAPSING)
+ var startEvent = $.Event('hide.bs.collapse')
+ this.$element.trigger(startEvent)
+ if (startEvent.isDefaultPrevented()) return
- this._element.style[dimension] = 0
- this._element.setAttribute('aria-expanded', true)
+ var dimension = this.dimension()
- if (this._trigger) {
- $(this._trigger).removeClass(Collapse._ClassName.COLLAPSED)
- this._trigger.setAttribute('aria-expanded', true)
- }
+ this.$element[dimension](this.$element[dimension]())[0].offsetHeight
- this['setTransitioning'](true)
+ this.$element
+ .addClass('collapsing')
+ .removeClass('collapse in')
+ .attr('aria-expanded', false)
- var complete = function () {
- $(this._element)
- .removeClass(Collapse._ClassName.COLLAPSING)
- .addClass(Collapse._ClassName.COLLAPSE)
- .addClass(Collapse._ClassName.IN)
+ this.$trigger
+ .addClass('collapsed')
+ .attr('aria-expanded', false)
- this._element.style[dimension] = ''
+ this.transitioning = 1
- this['setTransitioning'](false)
+ var complete = function () {
+ this.transitioning = 0
+ this.$element
+ .removeClass('collapsing')
+ .addClass('collapse')
+ .trigger('hidden.bs.collapse')
+ }
- $(this._element).trigger(Collapse._Event.SHOWN)
- }.bind(this)
+ if (!$.support.transition) return complete.call(this)
- if (!Bootstrap.transition) {
- complete()
- return
+ this.$element
+ [dimension](0)
+ .one('bsTransitionEnd', $.proxy(complete, this))
+ .emulateTransitionEnd(Collapse.TRANSITION_DURATION)
}
- 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.toggle = function () {
+ this[this.$element.hasClass('in') ? 'hide' : 'show']()
}
- 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.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()
}
- 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)
-
- this._element.style[dimension] = 0
+ Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
+ var isOpen = $element.hasClass('in')
- if (!Bootstrap.transition) {
- return complete()
+ $element.attr('aria-expanded', isOpen)
+ $trigger
+ .toggleClass('collapsed', !isOpen)
+ .attr('aria-expanded', isOpen)
}
- $(this._element)
- .one(Bootstrap.TRANSITION_END, complete)
- .emulateTransitionEnd(Collapse._TRANSITION_DURATION)
-}
-
-
-
-/**
- * @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)))
+ function getTargetFromTrigger($trigger) {
+ var href
+ var target = $trigger.attr('data-target')
+ || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
- for (var i = 0; i < elements.length; i++) {
- this._addAriaAndCollapsedClass(Collapse._getTargetFromElement(elements[i]), elements[i])
+ return $(target)
}
- return parent
-}
+ // COLLAPSE PLUGIN DEFINITION
+ // ==========================
-/**
- * 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 (trigger) {
- trigger.setAttribute('aria-expanded', isOpen)
- $(trigger).toggleClass(Collapse._ClassName.COLLAPSED, !isOpen)
- }
- }
-}
+ 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)
+ if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false
+ if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+ var old = $.fn.collapse
-/**
- * ------------------------------------------------------------------------
- * jQuery Interface + noConflict implementaiton
- * ------------------------------------------------------------------------
- */
+ $.fn.collapse = Plugin
+ $.fn.collapse.Constructor = Collapse
-/**
- * @const
- * @type {Function}
- */
-$.fn[Collapse._NAME] = Collapse._jQueryInterface
+ // COLLAPSE NO CONFLICT
+ // ====================
-/**
- * @const
- * @type {Function}
- */
-$.fn[Collapse._NAME]['Constructor'] = Collapse
+ $.fn.collapse.noConflict = function () {
+ $.fn.collapse = old
+ return this
+ }
-/**
- * @const
- * @type {Function}
- */
-$.fn[Collapse._NAME]['noConflict'] = function () {
- $.fn[Collapse._NAME] = Collapse._JQUERY_NO_CONFLICT
- return this
-}
+ // COLLAPSE DATA-API
+ // =================
+ $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
+ var $this = $(this)
-/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
- */
-
-$(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (event) {
- event.preventDefault()
+ if (!$this.attr('data-target')) e.preventDefault()
- var target = Collapse._getTargetFromElement(this)
+ var $target = getTargetFromTrigger($this)
+ var data = $target.data('bs.collapse')
+ var option = data ? 'toggle' : $this.data()
- var data = $(target).data(Collapse._DATA_KEY)
- var config = data ? 'toggle' : $.extend({}, $(this).data(), { trigger: this })
+ Plugin.call($target, option)
+ })
- Collapse._jQueryInterface.call($(target), config)
-})
+}(jQuery);
-/** =======================================================================
- * Bootstrap: dropdown.js v4.0.0
- * http://getbootstrap.com/javascript/#dropdown
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.3.4
+ * http://getbootstrap.com/javascript/#dropdowns
* ========================================================================
* 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'
-}
++function ($) {
+ 'use strict';
-/**
- * @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'
-}
+ // DROPDOWN CLASS DEFINITION
+ // =========================
+ var backdrop = '.dropdown-backdrop'
+ var toggle = '[data-toggle="dropdown"]'
+ var Dropdown = function (element) {
+ $(element).on('click.bs.dropdown', this.toggle)
+ }
-/**
- * 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)
+ Dropdown.VERSION = '3.3.4'
- if (!data) {
- $(this).data(Dropdown._DATA_KEY, (data = new Dropdown(this)))
- }
+ function getParent($this) {
+ var selector = $this.attr('data-target')
- if (typeof opt_config === 'string') {
- data[opt_config].call(this)
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
}
- })
-}
+ var $parent = selector && $(selector)
-/**
- * @param {Event=} opt_event
- * @private
- */
-Dropdown._clearMenus = function (opt_event) {
- if (opt_event && opt_event.which == 3) {
- return
- }
-
- var backdrop = $(Dropdown._Selector.BACKDROP)[0]
- if (backdrop) {
- backdrop.parentNode.removeChild(backdrop)
+ return $parent && $parent.length ? $parent : $this.parent()
}
- var toggles = /** @type {Array.<Element>} */ ($.makeArray($(Dropdown._Selector.DATA_TOGGLE)))
+ 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 }
- for (var i = 0; i < toggles.length; i++) {
- var parent = Dropdown._getParentFromElement(toggles[i])
- var relatedTarget = { 'relatedTarget': toggles[i] }
+ if (!$parent.hasClass('open')) return
- if (!$(parent).hasClass(Dropdown._ClassName.OPEN)) {
- continue
- }
+ if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return
- var hideEvent = $.Event(Dropdown._Event.HIDE, relatedTarget)
- $(parent).trigger(hideEvent)
- if (hideEvent.isDefaultPrevented()) {
- continue
- }
+ $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
- toggles[i].setAttribute('aria-expanded', 'false')
+ if (e.isDefaultPrevented()) return
- $(parent)
- .removeClass(Dropdown._ClassName.OPEN)
- .trigger(Dropdown._Event.HIDDEN, relatedTarget)
+ $this.attr('aria-expanded', 'false')
+ $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
+ })
}
-}
+ Dropdown.prototype.toggle = function (e) {
+ var $this = $(this)
-/**
- * @param {Element} element
- * @return {Element}
- * @private
- */
-Dropdown._getParentFromElement = function (element) {
- var selector = Bootstrap.getSelectorFromElement(element)
-
- if (selector) {
- var parent = $(selector)[0]
- }
+ if ($this.is('.disabled, :disabled')) return
- return /** @type {Element} */ (parent || element.parentNode)
-}
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+ clearMenus()
-/**
- * @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
- }
+ if (!isActive) {
+ if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+ // if mobile we use a backdrop because click events don't delegate
+ $(document.createElement('div'))
+ .addClass('dropdown-backdrop')
+ .insertAfter($(this))
+ .on('click', clearMenus)
+ }
- event.preventDefault()
- event.stopPropagation()
+ var relatedTarget = { relatedTarget: this }
+ $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
- if (this.disabled || $(this).hasClass(Dropdown._ClassName.DISABLED)) {
- return
- }
+ if (e.isDefaultPrevented()) return
- var parent = Dropdown._getParentFromElement(this)
- var isActive = $(parent).hasClass(Dropdown._ClassName.OPEN)
+ $this
+ .trigger('focus')
+ .attr('aria-expanded', 'true')
- 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')
+ $parent
+ .toggleClass('open')
+ .trigger('shown.bs.dropdown', relatedTarget)
}
- $(this).trigger('click')
- return
- }
-
- var items = $.makeArray($(Dropdown._Selector.VISIBLE_ITEMS))
-
- items = items.filter(function (item) {
- return item.offsetWidth || item.offsetHeight
- })
- if (!items.length) {
- return
+ return false
}
- var index = items.indexOf(event.target)
-
- if (event.which == 38 && index > 0) index-- // up
- if (event.which == 40 && index < items.length - 1) index++ // down
- if (!~index) index = 0
+ Dropdown.prototype.keydown = function (e) {
+ if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return
- items[index].focus()
-}
+ var $this = $(this)
+ e.preventDefault()
+ e.stopPropagation()
-/**
- * Toggles the dropdown
- * @this {Element}
- * @return {boolean|undefined}
- */
-Dropdown.prototype['toggle'] = function () {
- if (this.disabled || $(this).hasClass(Dropdown._ClassName.DISABLED)) {
- return
- }
+ if ($this.is('.disabled, :disabled')) return
- var parent = Dropdown._getParentFromElement(this)
- var isActive = $(parent).hasClass(Dropdown._ClassName.OPEN)
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
- Dropdown._clearMenus()
+ if (!isActive && e.which != 27 || isActive && e.which == 27) {
+ if (e.which == 27) $parent.find(toggle).trigger('focus')
+ return $this.trigger('click')
+ }
- if (isActive) {
- return false
- }
+ var desc = ' li:not(.disabled):visible a'
+ var $items = $parent.find('[role="menu"]' + desc + ', [role="listbox"]' + desc)
- 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 (!$items.length) return
- var relatedTarget = { 'relatedTarget': this }
- var showEvent = $.Event(Dropdown._Event.SHOW, relatedTarget)
+ var index = $items.index(e.target)
- $(parent).trigger(showEvent)
+ if (e.which == 38 && index > 0) index-- // up
+ if (e.which == 40 && index < $items.length - 1) index++ // down
+ if (!~index) index = 0
- if (showEvent.isDefaultPrevented()) {
- return
+ $items.eq(index).trigger('focus')
}
- this.focus()
- this.setAttribute('aria-expanded', 'true')
- $(parent).toggleClass(Dropdown._ClassName.OPEN)
+ // DROPDOWN PLUGIN DEFINITION
+ // ==========================
- $(parent).trigger(Dropdown._Event.SHOWN, relatedTarget)
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.dropdown')
- return false
-}
+ if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+ var old = $.fn.dropdown
-/**
- * ------------------------------------------------------------------------
- * jQuery Interface + noConflict implementaiton
- * ------------------------------------------------------------------------
- */
+ $.fn.dropdown = Plugin
+ $.fn.dropdown.Constructor = Dropdown
-/**
- * @const
- * @type {Function}
- */
-$.fn[Dropdown._NAME] = Dropdown._jQueryInterface
+ // DROPDOWN NO CONFLICT
+ // ====================
-/**
- * @const
- * @type {Function}
- */
-$.fn[Dropdown._NAME]['Constructor'] = Dropdown
+ $.fn.dropdown.noConflict = function () {
+ $.fn.dropdown = old
+ return this
+ }
-/**
- * @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', '.dropdown-menu', 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)
-
-/** =======================================================================
- * Bootstrap: modal.js v4.0.0
- * http://getbootstrap.com/javascript/#modal
+/* ========================================================================
+ * Bootstrap: modal.js v3.3.4
+ * http://getbootstrap.com/javascript/#modals
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ========================================================================
- * @fileoverview - Bootstrap's modal plugin. Modals are streamlined, but
- * flexible, dialog prompts with the minimum required functionality and
- * smart defaults.
- *
- * Public Methods & Properties:
- *
- * + $.modal
- * + $.modal.noConflict
- * + $.modal.Constructor
- * + $.modal.Constructor.VERSION
- * + $.modal.Constructor.Defaults
- * + $.modal.Constructor.Defaults.backdrop
- * + $.modal.Constructor.Defaults.keyboard
- * + $.modal.Constructor.Defaults.show
- * + $.modal.Constructor.prototype.toggle
- * + $.modal.Constructor.prototype.show
- * + $.modal.Constructor.prototype.hide
- *
- * ========================================================================
- */
-
-'use strict';
-
-
-/**
- * Our modal class.
- * @param {Element} element
- * @param {Object} config
- * @constructor
- */
-var Modal = function (element, config) {
-
- /** @private {Object} */
- this._config = config
-
- /** @private {Element} */
- this._element = element
-
- /** @private {Element} */
- this._backdrop = null
-
- /** @private {boolean} */
- this._isShown = false
-
- /** @private {boolean} */
- this._isBodyOverflowing = false
-
- /** @private {number} */
- this._scrollbarWidth = 0
-
-}
+ * ======================================================================== */
-/**
- * @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'
-
++function ($) {
+ 'use strict';
-/**
- * @const
- * @type {string}
- * @private
- */
-Modal._DATA_KEY = 'bs.modal'
+ // MODAL CLASS DEFINITION
+ // ======================
+ var Modal = function (element, options) {
+ this.options = options
+ this.$body = $(document.body)
+ this.$element = $(element)
+ this.$dialog = this.$element.find('.modal-dialog')
+ this.$backdrop = null
+ this.isShown = null
+ this.originalBodyPad = null
+ this.scrollbarWidth = 0
+ this.ignoreBackdropClick = false
-/**
- * @const
- * @type {number}
- * @private
- */
-Modal._TRANSITION_DURATION = 300
+ if (this.options.remote) {
+ this.$element
+ .find('.modal-content')
+ .load(this.options.remote, $.proxy(function () {
+ this.$element.trigger('loaded.bs.modal')
+ }, this))
+ }
+ }
+ Modal.VERSION = '3.3.4'
-/**
- * @const
- * @type {number}
- * @private
- */
-Modal._BACKDROP_TRANSITION_DURATION = 150
+ Modal.TRANSITION_DURATION = 300
+ Modal.BACKDROP_TRANSITION_DURATION = 150
+ Modal.DEFAULTS = {
+ backdrop: true,
+ keyboard: true,
+ show: true
+ }
-/**
- * @const
- * @type {Function}
- * @private
- */
-Modal._JQUERY_NO_CONFLICT = $.fn[Modal._NAME]
+ Modal.prototype.toggle = function (_relatedTarget) {
+ return this.isShown ? this.hide() : this.show(_relatedTarget)
+ }
+ Modal.prototype.show = function (_relatedTarget) {
+ var that = this
+ var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
-/**
- * @const
- * @enum {string}
- * @private
- */
-Modal._Event = {
- HIDE : 'hide.bs.modal',
- HIDDEN : 'hidden.bs.modal',
- SHOW : 'show.bs.modal',
- SHOWN : 'shown.bs.modal'
-}
+ this.$element.trigger(e)
+ if (this.isShown || e.isDefaultPrevented()) return
-/**
- * @const
- * @enum {string}
- * @private
- */
-Modal._ClassName = {
- BACKDROP : 'modal-backdrop',
- OPEN : 'modal-open',
- FADE : 'fade',
- IN : 'in'
-}
+ this.isShown = true
+ this.checkScrollbar()
+ this.setScrollbar()
+ this.$body.addClass('modal-open')
-/**
- * @const
- * @enum {string}
- * @private
- */
-Modal._Selector = {
- DIALOG : '.modal-dialog',
- DATA_TOGGLE : '[data-toggle="modal"]',
- DATA_DISMISS : '[data-dismiss="modal"]',
- SCROLLBAR_MEASURER : 'modal-scrollbar-measure'
-}
+ this.escape()
+ this.resize()
+ this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
+ this.$dialog.on('mousedown.dismiss.bs.modal', function () {
+ that.$element.one('mouseup.dismiss.bs.modal', function (e) {
+ if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
+ })
+ })
-/**
- * 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)
- }
+ this.backdrop(function () {
+ var transition = $.support.transition && that.$element.hasClass('fade')
- if (typeof opt_config == 'string') {
- data[opt_config](opt_relatedTarget)
+ if (!that.$element.parent().length) {
+ that.$element.appendTo(that.$body) // don't move modals dom position
+ }
- } else if (config['show']) {
- data['show'](opt_relatedTarget)
- }
- })
-}
+ that.$element
+ .show()
+ .scrollTop(0)
+ that.adjustDialog()
-/**
- * @param {Element} relatedTarget
- */
-Modal.prototype['toggle'] = function (relatedTarget) {
- return this._isShown ? this['hide']() : this['show'](relatedTarget)
-}
+ if (transition) {
+ that.$element[0].offsetWidth // force reflow
+ }
+ that.$element.addClass('in')
-/**
- * @param {Element} relatedTarget
- */
-Modal.prototype['show'] = function (relatedTarget) {
- var showEvent = $.Event(Modal._Event.SHOW, { relatedTarget: relatedTarget })
+ that.enforceFocus()
- $(this._element).trigger(showEvent)
+ var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
- if (this._isShown || showEvent.isDefaultPrevented()) {
- return
+ transition ?
+ that.$dialog // wait for modal to slide in
+ .one('bsTransitionEnd', function () {
+ that.$element.trigger('focus').trigger(e)
+ })
+ .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
+ that.$element.trigger('focus').trigger(e)
+ })
}
- this._isShown = true
+ Modal.prototype.hide = function (e) {
+ if (e) e.preventDefault()
- this._checkScrollbar()
- this._setScrollbar()
+ e = $.Event('hide.bs.modal')
- $(document.body).addClass(Modal._ClassName.OPEN)
+ this.$element.trigger(e)
- this._escape()
- this._resize()
+ if (!this.isShown || e.isDefaultPrevented()) return
- $(this._element).on('click.dismiss.bs.modal', Modal._Selector.DATA_DISMISS, this['hide'].bind(this))
+ this.isShown = false
- this._showBackdrop(this._showElement.bind(this, relatedTarget))
-}
+ this.escape()
+ this.resize()
+ $(document).off('focusin.bs.modal')
-/**
- * @param {Event} event
- */
-Modal.prototype['hide'] = function (event) {
- if (event) {
- event.preventDefault()
- }
+ this.$element
+ .removeClass('in')
+ .off('click.dismiss.bs.modal')
+ .off('mouseup.dismiss.bs.modal')
- var hideEvent = $.Event(Modal._Event.HIDE)
+ this.$dialog.off('mousedown.dismiss.bs.modal')
- $(this._element).trigger(hideEvent)
-
- if (!this._isShown || hideEvent.isDefaultPrevented()) {
- return
+ $.support.transition && this.$element.hasClass('fade') ?
+ this.$element
+ .one('bsTransitionEnd', $.proxy(this.hideModal, this))
+ .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
+ this.hideModal()
}
- this._isShown = false
-
- this._escape()
- this._resize()
-
- $(document).off('focusin.bs.modal')
-
- $(this._element).removeClass(Modal._ClassName.IN)
- this._element.setAttribute('aria-hidden', true)
-
- $(this._element).off('click.dismiss.bs.modal')
-
- 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))
}
-}
-
-/**
- * @param {Element} relatedTarget
- * @private
- */
-Modal.prototype._showElement = function (relatedTarget) {
- var transition = Bootstrap.transition && $(this._element).hasClass(Modal._ClassName.FADE)
-
- if (!this._element.parentNode || this._element.parentNode.nodeType != Node.ELEMENT_NODE) {
- document.body.appendChild(this._element) // don't move modals dom position
+ Modal.prototype.escape = function () {
+ if (this.isShown && this.options.keyboard) {
+ this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
+ e.which == 27 && this.hide()
+ }, this))
+ } else if (!this.isShown) {
+ this.$element.off('keydown.dismiss.bs.modal')
+ }
}
- this._element.style.display = 'block'
- this._element.scrollTop = 0
+ Modal.prototype.resize = function () {
+ if (this.isShown) {
+ $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
+ } else {
+ $(window).off('resize.bs.modal')
+ }
+ }
- if (this._config['backdrop']) {
- this._adjustBackdrop()
+ 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')
+ })
}
- if (transition) {
- Bootstrap.reflow(this._element)
+ Modal.prototype.removeBackdrop = function () {
+ this.$backdrop && this.$backdrop.remove()
+ this.$backdrop = null
}
- $(this._element).addClass(Modal._ClassName.IN)
- this._element.setAttribute('aria-hidden', false)
+ Modal.prototype.backdrop = function (callback) {
+ var that = this
+ var animate = this.$element.hasClass('fade') ? 'fade' : ''
- this._enforceFocus()
+ if (this.isShown && this.options.backdrop) {
+ var doAnimate = $.support.transition && animate
- var shownEvent = $.Event(Modal._Event.SHOWN, { relatedTarget: relatedTarget })
+ this.$backdrop = $(document.createElement('div'))
+ .addClass('modal-backdrop ' + animate)
+ .appendTo(this.$body)
- var transitionComplete = function () {
- this._element.focus()
- $(this._element).trigger(shownEvent)
- }.bind(this)
+ this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
+ if (this.ignoreBackdropClick) {
+ this.ignoreBackdropClick = false
+ return
+ }
+ if (e.target !== e.currentTarget) return
+ this.options.backdrop == 'static'
+ ? this.$element[0].focus()
+ : this.hide()
+ }, this))
- if (transition) {
- var dialog = $(this._element).find(Modal._Selector.DIALOG)[0]
- $(dialog)
- .one(Bootstrap.TRANSITION_END, transitionComplete)
- .emulateTransitionEnd(Modal._TRANSITION_DURATION)
- } else {
- transitionComplete()
- }
-}
+ if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
+ this.$backdrop.addClass('in')
+ if (!callback) return
-/**
- * @private
- */
-Modal.prototype._enforceFocus = function () {
- $(document)
- .off('focusin.bs.modal') // guard against infinite focus loop
- .on('focusin.bs.modal', function (e) {
- if (this._element !== e.target && !$(this._element).has(e.target).length) {
- this._element.focus()
- }
- }.bind(this))
-}
+ doAnimate ?
+ this.$backdrop
+ .one('bsTransitionEnd', callback)
+ .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
+ callback()
+ } else if (!this.isShown && this.$backdrop) {
+ this.$backdrop.removeClass('in')
-/**
- * @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']()
+ var callbackRemove = function () {
+ that.removeBackdrop()
+ callback && callback()
}
- }.bind(this))
+ $.support.transition && this.$element.hasClass('fade') ?
+ this.$backdrop
+ .one('bsTransitionEnd', callbackRemove)
+ .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
+ callbackRemove()
- } else if (!this._isShown) {
- $(this._element).off('keydown.dismiss.bs.modal')
+ } else if (callback) {
+ callback()
+ }
}
-}
+ // these following methods are used to handle overflowing modals
-/**
- * @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.handleUpdate = function () {
+ this.adjustDialog()
}
-}
-
-
-/**
- * @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))
-}
+ Modal.prototype.adjustDialog = function () {
+ var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
-/**
- * @private
- */
-Modal.prototype._removeBackdrop = function () {
- if (this._backdrop) {
- this._backdrop.parentNode.removeChild(this._backdrop)
- this._backdrop = null
+ 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 : ''
-
- if (this._isShown && this._config['backdrop']) {
- var doAnimate = Bootstrap.transition && animate
-
- this._backdrop = document.createElement('div')
- this._backdrop.className = Modal._ClassName.BACKDROP
-
- if (animate) {
- $(this._backdrop).addClass(animate)
- }
- $(this._element).prepend(this._backdrop)
-
- $(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)
- }
-
- $(this._backdrop).addClass(Modal._ClassName.IN)
-
- if (!callback) {
- return
- }
-
- if (!doAnimate) {
- callback()
- return
- }
-
- $(this._backdrop)
- .one(Bootstrap.TRANSITION_END, callback)
- .emulateTransitionEnd(Modal._BACKDROP_TRANSITION_DURATION)
-
- } else if (!this._isShown && this._backdrop) {
- $(this._backdrop).removeClass(Modal._ClassName.IN)
-
- var callbackRemove = function () {
- this._removeBackdrop()
- if (callback) {
- callback()
- }
- }.bind(this)
+ Modal.prototype.resetAdjustments = function () {
+ this.$element.css({
+ paddingLeft: '',
+ paddingRight: ''
+ })
+ }
- if (Bootstrap.transition && $(this._element).hasClass(Modal._ClassName.FADE)) {
- $(this._backdrop)
- .one(Bootstrap.TRANSITION_END, callbackRemove)
- .emulateTransitionEnd(Modal._BACKDROP_TRANSITION_DURATION)
- } else {
- callbackRemove()
+ Modal.prototype.checkScrollbar = function () {
+ var fullWindowWidth = window.innerWidth
+ if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
+ var documentElementRect = document.documentElement.getBoundingClientRect()
+ fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)
}
-
- } else if (callback) {
- callback()
+ this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth
+ this.scrollbarWidth = this.measureScrollbar()
}
-}
-
-/**
- * ------------------------------------------------------------------------
- * the following methods are used to handle overflowing modals
- * todo (fat): these should probably be refactored into a
- * ------------------------------------------------------------------------
- */
-
-
-/**
- * @private
- */
-Modal.prototype._handleUpdate = function () {
- if (this._config['backdrop']) this._adjustBackdrop()
- this._adjustDialog()
-}
-
-/**
- * @private
- */
-Modal.prototype._adjustBackdrop = function () {
- this._backdrop.style.height = 0 // todo (fat): no clue why we do this
- this._backdrop.style.height = this._element.scrollHeight + 'px'
-}
-
-
-/**
- * @private
- */
-Modal.prototype._adjustDialog = function () {
- var isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight
-
- if (!this._isBodyOverflowing && isModalOverflowing) {
- this._element.style.paddingLeft = this._scrollbarWidth + 'px'
+ Modal.prototype.setScrollbar = function () {
+ var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
+ this.originalBodyPad = document.body.style.paddingRight || ''
+ if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
}
- if (this._isBodyOverflowing && !isModalOverflowing) {
- this._element.style.paddingRight = this._scrollbarWidth + 'px'
+ Modal.prototype.resetScrollbar = function () {
+ this.$body.css('padding-right', this.originalBodyPad)
}
-}
-
-
-/**
- * @private
- */
-Modal.prototype._resetAdjustments = function () {
- this._element.style.paddingLeft = ''
- this._element.style.paddingRight = ''
-}
-
-
-/**
- * @private
- */
-Modal.prototype._checkScrollbar = function () {
- this._isBodyOverflowing = document.body.scrollHeight > document.documentElement.clientHeight
- this._scrollbarWidth = this._getScrollbarWidth()
-}
-
-
-/**
- * @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'
+ Modal.prototype.measureScrollbar = function () { // thx walsh
+ var scrollDiv = document.createElement('div')
+ scrollDiv.className = 'modal-scrollbar-measure'
+ this.$body.append(scrollDiv)
+ var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
+ this.$body[0].removeChild(scrollDiv)
+ return scrollbarWidth
}
-}
-
-
-/**
- * @private
- */
-Modal.prototype._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
+ // MODAL PLUGIN DEFINITION
+ // =======================
+ function Plugin(option, _relatedTarget) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.modal')
+ var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
-/**
- * @const
- * @type {Function}
- */
-$.fn[Modal._NAME]['Constructor'] = Modal
+ if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
+ if (typeof option == 'string') data[option](_relatedTarget)
+ else if (options.show) data.show(_relatedTarget)
+ })
+ }
+ var old = $.fn.modal
-/**
- * @const
- * @type {Function}
- */
-$.fn[Modal._NAME]['noConflict'] = function () {
- $.fn[Modal._NAME] = Modal._JQUERY_NO_CONFLICT
- return this
-}
+ $.fn.modal = Plugin
+ $.fn.modal.Constructor = Modal
-/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
- */
+ // MODAL NO CONFLICT
+ // =================
-$(document).on('click.bs.modal.data-api', Modal._Selector.DATA_TOGGLE, function (event) {
- var selector = Bootstrap.getSelectorFromElement(this)
-
- if (selector) {
- var target = $(selector)[0]
+ $.fn.modal.noConflict = function () {
+ $.fn.modal = old
+ return this
}
- var config = $(target).data(Modal._DATA_KEY) ? 'toggle' : $.extend({}, $(target).data(), $(this).data())
- if (this.tagName == 'A') {
- event.preventDefault()
- }
+ // MODAL DATA-API
+ // ==============
- var $target = $(target).one(Modal._Event.SHOW, function (showEvent) {
- if (showEvent.isDefaultPrevented()) {
- return // only register focus restorer if modal will actually get shown
- }
+ $(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())
- $target.one(Modal._Event.HIDDEN, function () {
- if ($(this).is(':visible')) {
- this.focus()
- }
- }.bind(this))
- }.bind(this))
+ if ($this.is('a')) e.preventDefault()
- Modal._jQueryInterface.call($(target), config, this)
-})
+ $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)
+ })
-/** =======================================================================
- * Bootstrap: scrollspy.js v4.0.0
- * http://getbootstrap.com/javascript/#scrollspy
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: tooltip.js v3.3.4
+ * 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 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';
-
-
-/**
- * Our scrollspy class.
- * @param {Element!} element
- * @param {Object=} opt_config
- * @constructor
- */
-function ScrollSpy(element, opt_config) {
-
- /** @private {Element|Window} */
- this._scrollElement = element.tagName == 'BODY' ? window : element
-
- /** @private {Object} */
- this._config = $.extend({}, ScrollSpy['Defaults'], opt_config)
-
- /** @private {string} */
- this._selector = (this._config.target || '') + ' .nav li > a'
-
- /** @private {Array} */
- this._offsets = []
-
- /** @private {Array} */
- this._targets = []
-
- /** @private {Element} */
- this._activeTarget = null
-
- /** @private {number} */
- this._scrollHeight = 0
-
- $(this._scrollElement).on('scroll.bs.scrollspy', this._process.bind(this))
-
- this['refresh']()
-
- this._process()
-}
-
-
-/**
- * @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'
-}
++function ($) {
+ 'use strict';
+ // TOOLTIP PUBLIC CLASS DEFINITION
+ // ===============================
-/**
- * @const
- * @enum {string}
- * @private
- */
-ScrollSpy._ClassName = {
- DROPDOWN_MENU : 'dropdown-menu',
- ACTIVE : 'active'
-}
+ var Tooltip = function (element, options) {
+ this.type = null
+ this.options = null
+ this.enabled = null
+ this.timeout = null
+ this.hoverState = null
+ this.$element = null
+ this.inState = null
+ this.init('tooltip', element, options)
+ }
-/**
- * @const
- * @enum {string}
- * @private
- */
-ScrollSpy._Selector = {
- DATA_SPY : '[data-spy="scroll"]',
- ACTIVE : '.active',
- LI_DROPDOWN : 'li.dropdown',
- LI : 'li'
-}
+ Tooltip.VERSION = '3.3.4'
+ Tooltip.TRANSITION_DURATION = 150
-/**
- * @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)
+ 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
}
-
- 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()
}
- this._offsets = []
- this._targets = []
+ 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 && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport))
+ this.inState = { click: false, hover: false, focus: false }
- this._scrollHeight = this._getScrollHeight()
+ if (this.$element[0] instanceof document.constructor && !this.options.selector) {
+ throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!')
+ }
- var targets = /** @type {Array.<Element>} */ ($.makeArray($(this._selector)))
+ var triggers = this.options.trigger.split(' ')
- targets
- .map(function (element, index) {
- var target
- var targetSelector = Bootstrap.getSelectorFromElement(element)
+ for (var i = triggers.length; i--;) {
+ var trigger = triggers[i]
- if (targetSelector) {
- target = $(targetSelector)[0]
- }
+ 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'
- if (target && (target.offsetWidth || target.offsetHeight)) {
- // todo (fat): remove sketch reliance on jQuery position/offset
- return [$(target)[offsetMethod]().top + offsetBase, targetSelector]
+ 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))
}
- })
- .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)
}
- }
- if (this._activeTarget && scrollTop < this._offsets[0]) {
- this._activeTarget = null
- this._clear()
- return
+ this.options.selector ?
+ (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
+ this.fixTitle()
}
- 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])
- }
+ Tooltip.prototype.getDefaults = function () {
+ return Tooltip.DEFAULTS
}
-}
-
-
-/**
- * @param {Element} target
- * @private
- */
-ScrollSpy.prototype._activate = function (target) {
- this._activeTarget = target
-
- this._clear()
-
- 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
+ Tooltip.prototype.getOptions = function (options) {
+ options = $.extend({}, this.getDefaults(), this.$element.data(), options)
- if (itemParent && $(itemParent).hasClass(ScrollSpy._ClassName.DROPDOWN_MENU)) {
- var closestDropdown = $(itemParent).closest(ScrollSpy._Selector.LI_DROPDOWN)[0]
- $(closestDropdown).addClass(ScrollSpy._ClassName.ACTIVE)
+ if (options.delay && typeof options.delay == 'number') {
+ options.delay = {
+ show: options.delay,
+ hide: options.delay
+ }
}
- }
-
- $(this._scrollElement).trigger(ScrollSpy._Event.ACTIVATE, {
- relatedTarget: target
- })
-}
-
-/**
- * @private
- */
-ScrollSpy.prototype._clear = function () {
- var activeParents = $(this._selector).parentsUntil(this._config.target, ScrollSpy._Selector.ACTIVE)
-
- for (var i = activeParents.length; i--;) {
- $(activeParents[i]).removeClass(ScrollSpy._ClassName.ACTIVE)
+ return options
}
-}
+ Tooltip.prototype.getDelegateOptions = function () {
+ var options = {}
+ var defaults = this.getDefaults()
-/**
- * ------------------------------------------------------------------------
- * jQuery Interface + noConflict implementaiton
- * ------------------------------------------------------------------------
- */
+ this._options && $.each(this._options, function (key, value) {
+ if (defaults[key] != value) options[key] = value
+ })
-/**
- * @const
- * @type {Function}
- */
-$.fn[ScrollSpy._NAME] = ScrollSpy._jQueryInterface
+ return options
+ }
+ Tooltip.prototype.enter = function (obj) {
+ var self = obj instanceof this.constructor ?
+ obj : $(obj.currentTarget).data('bs.' + this.type)
-/**
- * @const
- * @type {Function}
- */
-$.fn[ScrollSpy._NAME]['Constructor'] = ScrollSpy
+ if (!self) {
+ self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
+ $(obj.currentTarget).data('bs.' + this.type, self)
+ }
+ if (obj instanceof $.Event) {
+ self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true
+ }
-/**
- * @const
- * @type {Function}
- */
-$.fn[ScrollSpy._NAME]['noConflict'] = function () {
- $.fn[ScrollSpy._NAME] = ScrollSpy._JQUERY_NO_CONFLICT
- return this
-}
+ if (self.tip().hasClass('in') || self.hoverState == 'in') {
+ self.hoverState = 'in'
+ return
+ }
+ clearTimeout(self.timeout)
-/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
- */
+ self.hoverState = 'in'
-$(window).on('load.bs.scrollspy.data-api', function () {
- var scrollSpys = /** @type {Array.<Element>} */ ($.makeArray($(ScrollSpy._Selector.DATA_SPY)))
+ if (!self.options.delay || !self.options.delay.show) return self.show()
- for (var i = scrollSpys.length; i--;) {
- var $spy = $(scrollSpys[i])
- ScrollSpy._jQueryInterface.call($spy, /** @type {Object|null} */ ($spy.data()))
+ self.timeout = setTimeout(function () {
+ if (self.hoverState == 'in') self.show()
+ }, self.options.delay.show)
}
-})
-/** =======================================================================
- * Bootstrap: tooltip.js v4.0.0
- * http://getbootstrap.com/javascript/#tooltip
- * ========================================================================
- * 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';
-
-
-/**
- * Our tooltip class.
- * @param {Element!} element
- * @param {Object=} opt_config
- * @constructor
- */
-var Tooltip = function (element, opt_config) {
-
- /** @private {boolean} */
- this._isEnabled = true
+ Tooltip.prototype.isInStateTrue = function () {
+ for (var key in this.inState) {
+ if (this.inState[key]) return true
+ }
- /** @private {number} */
- this._timeout = 0
+ return false
+ }
- /** @private {string} */
- this._hoverState = ''
+ Tooltip.prototype.leave = function (obj) {
+ var self = obj instanceof this.constructor ?
+ obj : $(obj.currentTarget).data('bs.' + this.type)
- /** @protected {Element} */
- this.element = element
+ if (!self) {
+ self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
+ $(obj.currentTarget).data('bs.' + this.type, self)
+ }
- /** @protected {Object} */
- this.config = this._getConfig(opt_config)
+ if (obj instanceof $.Event) {
+ self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false
+ }
- /** @protected {Element} */
- this.tip = null
+ if (self.isInStateTrue()) return
- /** @protected {Element} */
- this.arrow = null
+ clearTimeout(self.timeout)
- if (this.config['viewport']) {
+ self.hoverState = 'out'
- /** @private {Element} */
- this._viewport = $(this.config['viewport']['selector'] || this.config['viewport'])[0]
+ if (!self.options.delay || !self.options.delay.hide) return self.hide()
+ self.timeout = setTimeout(function () {
+ if (self.hoverState == 'out') self.hide()
+ }, self.options.delay.hide)
}
- this._setListeners()
-}
+ Tooltip.prototype.show = function () {
+ var e = $.Event('show.bs.' + this.type)
+ if (this.hasContent() && this.enabled) {
+ this.$element.trigger(e)
-/**
- * @const
- * @type {string}
- */
-Tooltip['VERSION'] = '4.0.0'
+ var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
+ if (e.isDefaultPrevented() || !inDom) return
+ var that = this
+ var $tip = this.tip()
-/**
- * @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
- }
-}
+ var tipId = this.getUID(this.type)
+ this.setContent()
+ $tip.attr('id', tipId)
+ this.$element.attr('aria-describedby', tipId)
-/**
- * @const
- * @enum {string}
- * @protected
- */
-Tooltip.Direction = {
- TOP: 'top',
- LEFT: 'left',
- RIGHT: 'right',
- BOTTOM: 'bottom'
-}
+ if (this.options.animation) $tip.addClass('fade')
+ var placement = typeof this.options.placement == 'function' ?
+ this.options.placement.call(this, $tip[0], this.$element[0]) :
+ this.options.placement
-/**
- * @const
- * @type {string}
- * @private
- */
-Tooltip._NAME = 'tooltip'
+ var autoToken = /\s?auto?\s?/i
+ var autoPlace = autoToken.test(placement)
+ if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
+ $tip
+ .detach()
+ .css({ top: 0, left: 0, display: 'block' })
+ .addClass(placement)
+ .data('bs.' + this.type, this)
-/**
- * @const
- * @type {string}
- * @private
- */
-Tooltip._DATA_KEY = 'bs.tooltip'
+ this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
+ this.$element.trigger('inserted.bs.' + this.type)
+ var pos = this.getPosition()
+ var actualWidth = $tip[0].offsetWidth
+ var actualHeight = $tip[0].offsetHeight
-/**
- * @const
- * @type {number}
- * @private
- */
-Tooltip._TRANSITION_DURATION = 150
+ if (autoPlace) {
+ var orgPlacement = placement
+ var viewportDim = this.getPosition(this.$viewport)
+ placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' :
+ placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' :
+ placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' :
+ placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' :
+ placement
-/**
- * @const
- * @enum {string}
- * @private
- */
-Tooltip._HoverState = {
- IN: 'in',
- OUT: 'out'
-}
+ $tip
+ .removeClass(orgPlacement)
+ .addClass(placement)
+ }
+ var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
-/**
- * @const
- * @enum {string}
- * @private
- */
-Tooltip._Event = {
- HIDE : 'hide.bs.tooltip',
- HIDDEN : 'hidden.bs.tooltip',
- SHOW : 'show.bs.tooltip',
- SHOWN : 'shown.bs.tooltip'
-}
+ this.applyPlacement(calculatedOffset, placement)
+ var complete = function () {
+ var prevHoverState = that.hoverState
+ that.$element.trigger('shown.bs.' + that.type)
+ that.hoverState = null
-/**
- * @const
- * @enum {string}
- * @private
- */
-Tooltip._ClassName = {
- FADE : 'fade',
- IN : 'in'
-}
+ if (prevHoverState == 'out') that.leave(that)
+ }
+ $.support.transition && this.$tip.hasClass('fade') ?
+ $tip
+ .one('bsTransitionEnd', complete)
+ .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
+ complete()
+ }
+ }
-/**
- * @const
- * @enum {string}
- * @private
- */
-Tooltip._Selector = {
- TOOLTIP : '.tooltip',
- TOOLTIP_INNER : '.tooltip-inner',
- TOOLTIP_ARROW : '.tooltip-arrow'
-}
+ 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)
-/**
- * @const
- * @type {Function}
- * @private
- */
-Tooltip._JQUERY_NO_CONFLICT = $.fn[Tooltip._NAME]
+ // we must check for NaN for ie 8/9
+ if (isNaN(marginTop)) marginTop = 0
+ if (isNaN(marginLeft)) marginLeft = 0
+ offset.top += marginTop
+ offset.left += marginLeft
-/**
- * @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
+ // $.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)
- if (!data && opt_config == 'destroy') {
- return
- }
+ $tip.addClass('in')
- if (!data) {
- data = new Tooltip(this, config)
- $(this).data(Tooltip._DATA_KEY, data)
- }
+ // 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 (typeof opt_config === 'string') {
- data[opt_config]()
+ if (placement == 'top' && actualHeight != height) {
+ offset.top = offset.top + height - actualHeight
}
- })
-}
+ var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
-/**
- * Enable tooltip
- */
-Tooltip.prototype['enable'] = function () {
- this._isEnabled = true
-}
+ if (delta.left) offset.left += delta.left
+ else offset.top += delta.top
+ var isVertical = /top|bottom/.test(placement)
+ var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
+ var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
-/**
- * Disable tooltip
- */
-Tooltip.prototype['disable'] = function () {
- this._isEnabled = false
-}
+ $tip.offset(offset)
+ this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
+ }
+ Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) {
+ this.arrow()
+ .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
+ .css(isVertical ? 'top' : 'left', '')
+ }
-/**
- * Toggle the tooltip enable state
- */
-Tooltip.prototype['toggleEnabled'] = function () {
- this._isEnabled = !this._isEnabled
-}
+ Tooltip.prototype.setContent = function () {
+ var $tip = this.tip()
+ var title = this.getTitle()
-/**
- * Toggle the tooltips display
- * @param {Event} opt_event
- */
-Tooltip.prototype['toggle'] = function (opt_event) {
- var context = this
- var dataKey = this.getDataKey()
+ $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
+ $tip.removeClass('fade in top bottom left right')
+ }
- if (opt_event) {
- context = $(opt_event.currentTarget).data(dataKey)
+ Tooltip.prototype.hide = function (callback) {
+ var that = this
+ var $tip = $(this.$tip)
+ var e = $.Event('hide.bs.' + this.type)
- if (!context) {
- context = new this.constructor(opt_event.currentTarget, this._getDelegateConfig())
- $(opt_event.currentTarget).data(dataKey, context)
+ function complete() {
+ if (that.hoverState != 'in') $tip.detach()
+ that.$element
+ .removeAttr('aria-describedby')
+ .trigger('hidden.bs.' + that.type)
+ callback && callback()
}
- }
-
- $(context.getTipElement()).hasClass(Tooltip._ClassName.IN) ?
- context._leave(null, context) :
- context._enter(null, context)
-}
+ this.$element.trigger(e)
-/**
- * Remove tooltip functionality
- */
-Tooltip.prototype['destroy'] = function () {
- clearTimeout(this._timeout)
- this['hide'](function () {
- $(this.element)
- .off(Tooltip._Selector.TOOLTIP)
- .removeData(this.getDataKey())
- }.bind(this))
-}
+ if (e.isDefaultPrevented()) return
+ $tip.removeClass('in')
-/**
- * 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)
+ $.support.transition && $tip.hasClass('fade') ?
+ $tip
+ .one('bsTransitionEnd', complete)
+ .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
+ complete()
- if (this.isWithContent() && this._isEnabled) {
- $(this.element).trigger(showEvent)
+ this.hoverState = null
- var isInTheDom = $.contains(this.element.ownerDocument.documentElement, this.element)
+ return this
+ }
- if (showEvent.isDefaultPrevented() || !isInTheDom) {
- return
+ 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', '')
}
+ }
- var tip = this.getTipElement()
- var tipId = Bootstrap.getUID(this.getName())
+ Tooltip.prototype.hasContent = function () {
+ return this.getTitle()
+ }
- tip.setAttribute('id', tipId)
- this.element.setAttribute('aria-describedby', tipId)
+ Tooltip.prototype.getPosition = function ($element) {
+ $element = $element || this.$element
- this.setContent()
+ var el = $element[0]
+ var isBody = el.tagName == 'BODY'
- if (this.config['animation']) {
- $(tip).addClass(Tooltip._ClassName.FADE)
+ 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 })
}
+ 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
- var placement = typeof this.config['placement'] == 'function' ?
- this.config['placement'].call(this, tip, this.element) :
- this.config['placement']
-
- var autoToken = /\s?auto?\s?/i
- var isWithAutoPlacement = autoToken.test(placement)
-
- if (isWithAutoPlacement) {
- placement = placement.replace(autoToken, '') || Tooltip.Direction.TOP
- }
+ return $.extend({}, elRect, scroll, outerDims, elOffset)
+ }
- if (tip.parentNode && tip.parentNode.nodeType == Node.ELEMENT_NODE) {
- tip.parentNode.removeChild(tip)
- }
+ 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 }
- tip.style.top = 0
- tip.style.left = 0
- tip.style.display = 'block'
+ }
- $(tip).addClass(Tooltip._NAME + '-' + placement)
+ Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
+ var delta = { top: 0, left: 0 }
+ if (!this.$viewport) return delta
- $(tip).data(this.getDataKey(), this)
+ var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
+ var viewportDimensions = this.getPosition(this.$viewport)
- if (this.config['container']) {
- $(this.config['container'])[0].appendChild(tip)
+ 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 {
- this.element.parentNode.insertBefore(tip, this.element.nextSibling)
+ 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.right) { // right overflow
+ delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
+ }
}
- 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)
-
- this._applyCalculatedPlacement(calculatedOffset, calculatedPlacement)
-
- var complete = function () {
- var prevHoverState = this.hoverState
- $(this.element).trigger(this.getEventObject().SHOWN)
- this.hoverState = null
-
- 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()
+ return delta
}
-}
+ Tooltip.prototype.getTitle = function () {
+ var title
+ var $e = this.$element
+ var o = this.options
-/**
- * Hide the tooltip breh
- */
-Tooltip.prototype['hide'] = function (callback) {
- var tip = this.getTipElement()
- var hideEvent = $.Event(this.getEventObject().HIDE)
+ title = $e.attr('data-original-title')
+ || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
- var complete = function () {
- if (this._hoverState != Tooltip._HoverState.IN) {
- tip.parentNode.removeChild(tip)
- }
+ return title
+ }
- this.element.removeAttribute('aria-describedby')
- $(this.element).trigger(this.getEventObject().HIDDEN)
+ Tooltip.prototype.getUID = function (prefix) {
+ do prefix += ~~(Math.random() * 1000000)
+ while (document.getElementById(prefix))
+ return prefix
+ }
- if (callback) {
- callback()
+ Tooltip.prototype.tip = function () {
+ if (!this.$tip) {
+ this.$tip = $(this.options.template)
+ if (this.$tip.length != 1) {
+ throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!')
+ }
}
- }.bind(this)
+ return this.$tip
+ }
- $(this.element).trigger(hideEvent)
+ Tooltip.prototype.arrow = function () {
+ return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
+ }
- if (hideEvent.isDefaultPrevented()) return
+ Tooltip.prototype.enable = function () {
+ this.enabled = true
+ }
- $(tip).removeClass(Tooltip._ClassName.IN)
+ Tooltip.prototype.disable = function () {
+ this.enabled = false
+ }
- if (Bootstrap.transition && $(this._tip).hasClass(Tooltip._ClassName.FADE)) {
- $(tip)
- .one(Bootstrap.TRANSITION_END, complete)
- .emulateTransitionEnd(Tooltip._TRANSITION_DURATION)
- } else {
- complete()
+ Tooltip.prototype.toggleEnabled = function () {
+ this.enabled = !this.enabled
}
- this._hoverState = ''
-}
+ 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)
+ }
+ }
+ if (e) {
+ self.inState.click = !self.inState.click
+ if (self.isInStateTrue()) self.enter(self)
+ else self.leave(self)
+ } else {
+ self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
+ }
+ }
-/**
- * @return {string}
- */
-Tooltip.prototype['getHoverState'] = function (callback) {
- return this._hoverState
-}
+ Tooltip.prototype.destroy = function () {
+ var that = this
+ clearTimeout(this.timeout)
+ this.hide(function () {
+ that.$element.off('.' + that.type).removeData('bs.' + that.type)
+ if (that.$tip) {
+ that.$tip.detach()
+ }
+ that.$tip = null
+ that.$arrow = null
+ that.$viewport = null
+ })
+ }
-/**
- * @return {string}
- * @protected
- */
-Tooltip.prototype.getName = function () {
- return Tooltip._NAME
-}
+ // TOOLTIP PLUGIN DEFINITION
+ // =========================
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.tooltip')
+ var options = typeof option == 'object' && option
-/**
- * @return {string}
- * @protected
- */
-Tooltip.prototype.getDataKey = function () {
- return Tooltip._DATA_KEY
-}
+ if (!data && /destroy|hide/.test(option)) return
+ if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+ var old = $.fn.tooltip
-/**
- * @return {Object}
- * @protected
- */
-Tooltip.prototype.getEventObject = function () {
- return Tooltip._Event
-}
+ $.fn.tooltip = Plugin
+ $.fn.tooltip.Constructor = Tooltip
-/**
- * @return {string}
- * @protected
- */
-Tooltip.prototype.getTitle = function () {
- var title = this.element.getAttribute('data-original-title')
+ // TOOLTIP NO CONFLICT
+ // ===================
- if (!title) {
- title = typeof this.config['title'] === 'function' ?
- this.config['title'].call(this.element) :
- this.config['title']
+ $.fn.tooltip.noConflict = function () {
+ $.fn.tooltip = old
+ return this
}
- return /** @type {string} */ (title)
-}
+}(jQuery);
+/* ========================================================================
+ * Bootstrap: popover.js v3.3.4
+ * http://getbootstrap.com/javascript/#popovers
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
-/**
- * @return {Element}
- * @protected
- */
-Tooltip.prototype.getTipElement = function () {
- return (this._tip = this._tip || $(this.config['template'])[0])
-}
++function ($) {
+ 'use strict';
-/**
- * @return {Element}
- * @protected
- */
-Tooltip.prototype.getArrowElement = function () {
- return (this.arrow = this.arrow || $(this.getTipElement()).find(Tooltip._Selector.TOOLTIP_ARROW)[0])
-}
+ // POPOVER PUBLIC CLASS DEFINITION
+ // ===============================
+ var Popover = function (element, options) {
+ this.init('popover', element, options)
+ }
-/**
- * @return {boolean}
- * @protected
- */
-Tooltip.prototype.isWithContent = function () {
- return !!this.getTitle()
-}
+ if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
+ Popover.VERSION = '3.3.4'
-/**
- * @protected
- */
-Tooltip.prototype.setContent = function () {
- var tip = this.getTipElement()
- var title = this.getTitle()
+ Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
+ placement: 'right',
+ trigger: 'click',
+ content: '',
+ template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
+ })
- $(tip).find(Tooltip._Selector.TOOLTIP_INNER)[0][this.config['html'] ? 'innerHTML' : 'innerText'] = title
- $(tip)
- .removeClass(Tooltip._ClassName.FADE)
- .removeClass(Tooltip._ClassName.IN)
+ // NOTE: POPOVER EXTENDS tooltip.js
+ // ================================
- for (var direction in Tooltip.Direction) {
- $(tip).removeClass(Tooltip._NAME + '-' + direction)
- }
-}
+ Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
+ Popover.prototype.constructor = Popover
-/**
- * @private
- */
-Tooltip.prototype._setListeners = function () {
- var triggers = this.config['trigger'].split(' ')
+ Popover.prototype.getDefaults = function () {
+ return Popover.DEFAULTS
+ }
- triggers.forEach(function (trigger) {
- if (trigger == 'click') {
- $(this.element).on('click.bs.tooltip', this.config['selector'], this['toggle'].bind(this))
+ Popover.prototype.setContent = function () {
+ var $tip = this.tip()
+ var title = this.getTitle()
+ var content = this.getContent()
- } else if (trigger != 'manual') {
- var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
- var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
+ $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)
- $(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))
+ $tip.removeClass('fade top bottom left right in')
- if (this.config['selector']) {
- this.config = $.extend({}, this.config, { 'trigger': 'manual', 'selector': '' })
- } else {
- this._fixTitle()
+ // 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()
}
-}
-
-
-/**
- * @param {Object=} opt_config
- * @return {Object}
- * @private
- */
-Tooltip.prototype._getConfig = function (opt_config) {
- var config = $.extend({}, this.constructor['Defaults'], $(this.element).data(), opt_config)
- if (config['delay'] && typeof config['delay'] == 'number') {
- config['delay'] = {
- 'show': config['delay'],
- 'hide': config['delay']
- }
+ Popover.prototype.hasContent = function () {
+ return this.getTitle() || this.getContent()
}
- return config
-}
+ Popover.prototype.getContent = function () {
+ var $e = this.$element
+ var o = this.options
+ return $e.attr('data-content')
+ || (typeof o.content == 'function' ?
+ o.content.call($e[0]) :
+ o.content)
+ }
-/**
- * @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
- }
+ Popover.prototype.arrow = function () {
+ return (this.$arrow = this.$arrow || this.tip().find('.arrow'))
}
- return config
-}
+ // 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
-/**
- * @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)
+ if (!data && /destroy|hide/.test(option)) return
+ if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
}
- return placement
-}
-
+ var old = $.fn.popover
-/**
- * @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 }
-}
+ $.fn.popover = Plugin
+ $.fn.popover.Constructor = Popover
-/**
- * @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 }
+ // POPOVER NO CONFLICT
+ // ===================
- if (!this._viewport) {
- return delta
+ $.fn.popover.noConflict = function () {
+ $.fn.popover = old
+ return this
}
- var viewportPadding = this.config['viewport'] && this.config['viewport']['padding'] || 0
- var viewportDimensions = this._getPosition(this._viewport)
+}(jQuery);
- if (placement === Tooltip.Direction.RIGHT || placement === Tooltip.Direction.LEFT) {
- var topEdgeOffset = position.top - viewportPadding - viewportDimensions.scroll
- var bottomEdgeOffset = position.top + viewportPadding - viewportDimensions.scroll + actualHeight
+/* ========================================================================
+ * Bootstrap: scrollspy.js v3.3.4
+ * http://getbootstrap.com/javascript/#scrollspy
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
- 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
- }
++function ($) {
+ 'use strict';
- } else {
- var leftEdgeOffset = position.left - viewportPadding
- var rightEdgeOffset = position.left + viewportPadding + actualWidth
+ // SCROLLSPY CLASS DEFINITION
+ // ==========================
- if (leftEdgeOffset < viewportDimensions.left) { // left overflow
- delta.left = viewportDimensions.left - leftEdgeOffset
+ function ScrollSpy(element, options) {
+ this.$body = $(document.body)
+ this.$scrollElement = $(element).is(document.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
- } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
- delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
- }
+ this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this))
+ this.refresh()
+ this.process()
}
- return delta
-}
+ ScrollSpy.VERSION = '3.3.4'
-
-/**
- * @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
+ ScrollSpy.DEFAULTS = {
+ offset: 10
}
- if (isNaN(marginLeft)) {
- marginLeft = 0
+
+ ScrollSpy.prototype.getScrollHeight = function () {
+ return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
}
- offset.top = offset.top + marginTop
- offset.left = offset.left + marginLeft
+ ScrollSpy.prototype.refresh = function () {
+ var that = this
+ var offsetMethod = 'offset'
+ var offsetBase = 0
- // $.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)
+ this.offsets = []
+ this.targets = []
+ this.scrollHeight = this.getScrollHeight()
- $(tip).addClass(Tooltip._ClassName.IN)
+ if (!$.isWindow(this.$scrollElement[0])) {
+ offsetMethod = 'position'
+ offsetBase = this.$scrollElement.scrollTop()
+ }
- // check to see if placing tip in new offset caused the tip to resize itself
- var actualWidth = tip.offsetWidth
- var actualHeight = tip.offsetHeight
+ this.$body
+ .find(this.selector)
+ .map(function () {
+ var $el = $(this)
+ var href = $el.data('target') || $el.attr('href')
+ var $href = /^#./.test(href) && $(href)
- if (placement == Tooltip.Direction.TOP && actualHeight != height) {
- offset.top = offset.top + height - actualHeight
+ return ($href
+ && $href.length
+ && $href.is(':visible')
+ && [[$href[offsetMethod]().top + offsetBase, href]]) || null
+ })
+ .sort(function (a, b) { return a[0] - b[0] })
+ .each(function () {
+ that.offsets.push(this[0])
+ that.targets.push(this[1])
+ })
}
- var delta = this._getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
+ 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 (delta.left) {
- offset.left += delta.left
- } else {
- offset.top += delta.top
- }
+ if (this.scrollHeight != scrollHeight) {
+ this.refresh()
+ }
- 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'
+ if (scrollTop >= maxScroll) {
+ return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
+ }
- $(tip).offset(offset)
+ if (activeTarget && scrollTop < offsets[0]) {
+ this.activeTarget = null
+ return this.clear()
+ }
- this._replaceArrow(arrowDelta, tip[arrowOffsetPosition], isVertical)
-}
+ for (i = offsets.length; i--;) {
+ activeTarget != targets[i]
+ && scrollTop >= offsets[i]
+ && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1])
+ && this.activate(targets[i])
+ }
+ }
+ ScrollSpy.prototype.activate = function (target) {
+ this.activeTarget = target
-/**
- * @param {number} delta
- * @param {number} dimension
- * @param {boolean} isHorizontal
- * @private
- */
-Tooltip.prototype._replaceArrow = function (delta, dimension, isHorizontal) {
- var arrow = this.getArrowElement()
+ this.clear()
- arrow.style[isHorizontal ? 'left' : 'top'] = 50 * (1 - delta / dimension) + '%'
- arrow.style[isHorizontal ? 'top' : 'left'] = ''
-}
+ var selector = this.selector +
+ '[data-target="' + target + '"],' +
+ this.selector + '[href="' + target + '"]'
+ var active = $(selector)
+ .parents('li')
+ .addClass('active')
+ if (active.parent('.dropdown-menu').length) {
+ active = active
+ .closest('li.dropdown')
+ .addClass('active')
+ }
-/**
- * @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', '')
+ active.trigger('activate.bs.scrollspy')
}
-}
-
-/**
- * @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
+ ScrollSpy.prototype.clear = function () {
+ $(this.selector)
+ .parentsUntil(this.options.target, '.active')
+ .removeClass('active')
}
- if (!context) {
- context = new this.constructor(opt_event.currentTarget, this._getDelegateConfig())
- $(opt_event.currentTarget).data(dataKey, context)
- }
- clearTimeout(context._timeout)
+ // SCROLLSPY PLUGIN DEFINITION
+ // ===========================
- context._hoverState = Tooltip._HoverState.IN
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.scrollspy')
+ var options = typeof option == 'object' && option
- if (!context.config['delay'] || !context.config['delay']['show']) {
- context['show']()
- return
+ if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
}
- context._timeout = setTimeout(function () {
- if (context._hoverState == Tooltip._HoverState.IN) {
- context['show']()
- }
- }, context.config['delay']['show'])
-}
-
+ var old = $.fn.scrollspy
-/**
- * @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)
- }
+ $.fn.scrollspy = Plugin
+ $.fn.scrollspy.Constructor = ScrollSpy
- clearTimeout(context._timeout)
- context._hoverState = Tooltip._HoverState.OUT
+ // SCROLLSPY NO CONFLICT
+ // =====================
- if (!context.config['delay'] || !context.config['delay']['hide']) {
- context['hide']()
- return
+ $.fn.scrollspy.noConflict = function () {
+ $.fn.scrollspy = old
+ return this
}
- context._timeout = setTimeout(function () {
- if (context._hoverState == Tooltip._HoverState.OUT) {
- context['hide']()
- }
- }, context.config['delay']['hide'])
-}
-
-
-/**
- * ------------------------------------------------------------------------
- * jQuery Interface + noConflict implementaiton
- * ------------------------------------------------------------------------
- */
-
-/**
- * @const
- * @type {Function}
- */
-$.fn[Tooltip._NAME] = Tooltip._jQueryInterface
-
-
-/**
- * @const
- * @type {Function}
- */
-$.fn[Tooltip._NAME]['Constructor'] = Tooltip
+ // SCROLLSPY DATA-API
+ // ==================
+ $(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[Tooltip._NAME]['noConflict'] = function () {
- $.fn[Tooltip._NAME] = Tooltip._JQUERY_NO_CONFLICT
- return this
-}
+}(jQuery);
-/** =======================================================================
- * Bootstrap: popover.js v4.0.0
- * http://getbootstrap.com/javascript/#popovers
+/* ========================================================================
+ * Bootstrap: tab.js v3.3.4
+ * http://getbootstrap.com/javascript/#tabs
* ========================================================================
* 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
- *
- * ========================================================================
- */
-
-
-'use strict';
+ * ======================================================================== */
-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)
++function ($) {
+ 'use strict';
+ // TAB CLASS DEFINITION
+ // ====================
-/**
- * @const
- * @type {string}
- */
-Popover['VERSION'] = '4.0.0'
+ var Tab = function (element) {
+ // jscs:disable requireDollarBeforejQueryAssignment
+ this.element = $(element)
+ // jscs:enable requireDollarBeforejQueryAssignment
+ }
+ Tab.VERSION = '3.3.4'
-/**
- * @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'
+ Tab.TRANSITION_DURATION = 150
+ Tab.prototype.show = function () {
+ var $this = this.element
+ var $ul = $this.closest('ul:not(.dropdown-menu)')
+ var selector = $this.data('target')
-/**
- * @const
- * @type {string}
- * @private
- */
-Popover._DATA_KEY = 'bs.popover'
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+ }
+ if ($this.parent('li').hasClass('active')) return
-/**
- * @const
- * @enum {string}
- * @private
- */
-Popover._Event = {
- HIDE : 'hide.bs.popover',
- HIDDEN : 'hidden.bs.popover',
- SHOW : 'show.bs.popover',
- SHOWN : 'shown.bs.popover'
-}
+ 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]
+ })
+ $previous.trigger(hideEvent)
+ $this.trigger(showEvent)
-/**
- * @const
- * @enum {string}
- * @private
- */
-Popover._ClassName = {
- FADE : 'fade',
- IN : 'in'
-}
+ if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
+ var $target = $(selector)
-/**
- * @const
- * @enum {string}
- * @private
- */
-Popover._Selector = {
- TITLE : '.popover-title',
- CONTENT : '.popover-content',
- ARROW : '.popover-arrow'
-}
-
-
-/**
- * @const
- * @type {Function}
- * @private
- */
-Popover._JQUERY_NO_CONFLICT = $.fn[Popover._NAME]
+ 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]
+ })
+ })
+ }
+ 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')
+ }
-/**
- * @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 (element.parent('.dropdown-menu').length) {
+ element
+ .closest('li.dropdown')
+ .addClass('active')
+ .end()
+ .find('[data-toggle="tab"]')
+ .attr('aria-expanded', true)
+ }
- if (!data && opt_config === 'destroy') {
- return
+ callback && callback()
}
- if (!data) {
- data = new Popover(this, config)
- $(this).data(Popover._DATA_KEY, data)
- }
+ $active.length && transition ?
+ $active
+ .one('bsTransitionEnd', next)
+ .emulateTransitionEnd(Tab.TRANSITION_DURATION) :
+ next()
- if (typeof opt_config === 'string') {
- data[opt_config]()
- }
- })
-}
+ $active.removeClass('in')
+ }
-/**
- * @return {string}
- * @protected
- */
-Popover.prototype.getName = function () {
- return Popover._NAME
-}
+ // TAB PLUGIN DEFINITION
+ // =====================
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.tab')
-/**
- * @override
- */
-Popover.prototype.getDataKey = function () {
- return Popover._DATA_KEY
-}
+ if (!data) $this.data('bs.tab', (data = new Tab(this)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+ var old = $.fn.tab
-/**
- * @override
- */
-Popover.prototype.getEventObject = function () {
- return Popover._Event
-}
+ $.fn.tab = Plugin
+ $.fn.tab.Constructor = Tab
-/**
- * @override
- */
-Popover.prototype.getArrowElement = function () {
- return (this.arrow = this.arrow || $(this.getTipElement()).find(Popover._Selector.ARROW)[0])
-}
-
+ // TAB NO CONFLICT
+ // ===============
-/**
- * @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
+ $.fn.tab.noConflict = function () {
+ $.fn.tab = old
+ return this
}
- // 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)
- $(tip)
- .removeClass(Popover._ClassName.FADE)
- .removeClass(Popover._ClassName.IN)
+ // TAB DATA-API
+ // ============
- for (var direction in Tooltip.Direction) {
- $(tip).removeClass(Popover._NAME + '-' + Tooltip.Direction[direction])
+ var clickHandler = function (e) {
+ e.preventDefault()
+ Plugin.call($(this), 'show')
}
-}
-
-
-/**
- * @override
- */
-Popover.prototype.isWithContent = function () {
- return this.getTitle() || this._getContent()
-}
-
-
-/**
- * @override
- */
-Popover.prototype.getTipElement = function () {
- return (this.tip = this.tip || $(this.config['template'])[0])
-}
-
-
-/**
- * @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'])
-}
-
-
-
-/**
- * ------------------------------------------------------------------------
- * jQuery Interface + noConflict implementaiton
- * ------------------------------------------------------------------------
- */
-
-/**
- * @const
- * @type {Function}
- */
-$.fn[Popover._NAME] = Popover._jQueryInterface
-
-
-/**
- * @const
- * @type {Function}
- */
-$.fn[Popover._NAME]['Constructor'] = Popover
+ $(document)
+ .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
+ .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
-/**
- * @const
- * @type {Function}
- */
-$.fn[Popover._NAME]['noConflict'] = function () {
- $.fn[Popover._NAME] = Popover._JQUERY_NO_CONFLICT
- return this
-}
+}(jQuery);
-/** =======================================================================
- * Bootstrap: tab.js v4.0.0
- * http://getbootstrap.com/javascript/#tabs
+/* ========================================================================
+ * Bootstrap: affix.js v3.3.4
+ * http://getbootstrap.com/javascript/#affix
* ========================================================================
* 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
+ * ======================================================================== */
-}
++function ($) {
+ 'use strict';
-/**
- * @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'
-}
-
+ // AFFIX CLASS DEFINITION
+ // ======================
-/**
- * @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'
-}
+ var Affix = function (element, options) {
+ this.options = $.extend({}, Affix.DEFAULTS, options)
+ this.$target = $(this.options.target)
+ .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
+ .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
-/**
- * @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)
+ this.$element = $(element)
+ this.affixed = null
+ this.unpin = null
+ this.pinnedOffset = null
- if (!data) {
- data = data = new Tab(this)
- $this.data(Tab._DATA_KEY, data)
- }
+ this.checkPosition()
+ }
- if (typeof opt_config === 'string') {
- data[opt_config]()
- }
- })
-}
+ Affix.VERSION = '3.3.4'
+ Affix.RESET = 'affix affix-top affix-bottom'
-/**
- * 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
+ Affix.DEFAULTS = {
+ offset: 0,
+ target: window
}
- var ulElement = $(this._element).closest(Tab._Selector.UL)[0]
- var selector = Bootstrap.getSelectorFromElement(this._element)
+ Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
+ var scrollTop = this.$target.scrollTop()
+ var position = this.$element.offset()
+ var targetHeight = this.$target.height()
- if (ulElement) {
- var previous = /** @type {Array.<Element>} */ ($.makeArray($(ulElement).find(Tab._Selector.ACTIVE)))
- previous = previous[previous.length - 1]
+ if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false
- if (previous) {
- previous = $(previous).find('a')[0]
+ if (this.affixed == 'bottom') {
+ if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
+ return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
}
- }
- var hideEvent = $.Event(Tab._Event.HIDE, {
- relatedTarget: this._element
- })
+ var initializing = this.affixed == null
+ var colliderTop = initializing ? scrollTop : position.top
+ var colliderHeight = initializing ? targetHeight : height
- var showEvent = $.Event(Tab._Event.SHOW, {
- relatedTarget: previous
- })
+ if (offsetTop != null && scrollTop <= offsetTop) return 'top'
+ if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'
- if (previous) {
- $(previous).trigger(hideEvent)
+ return false
}
- $(this._element).trigger(showEvent)
-
- if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
-
- if (selector) {
- var target = $(selector)[0]
+ Affix.prototype.getPinnedOffset = function () {
+ if (this.pinnedOffset) return this.pinnedOffset
+ this.$element.removeClass(Affix.RESET).addClass('affix')
+ var scrollTop = this.$target.scrollTop()
+ var position = this.$element.offset()
+ return (this.pinnedOffset = position.top - scrollTop)
}
- this._activate($(this._element).closest(Tab._Selector.LI)[0], ulElement)
+ Affix.prototype.checkPositionWithEventLoop = function () {
+ setTimeout($.proxy(this.checkPosition, this), 1)
+ }
- var complete = function () {
- var hiddenEvent = $.Event(Tab._Event.HIDDEN, {
- relatedTarget: this._element
- })
+ Affix.prototype.checkPosition = function () {
+ if (!this.$element.is(':visible')) return
- var shownEvent = $.Event(Tab._Event.SHOWN, {
- relatedTarget: previous
- })
+ var height = this.$element.height()
+ var offset = this.options.offset
+ var offsetTop = offset.top
+ var offsetBottom = offset.bottom
+ var scrollHeight = Math.max($(document).height(), $(document.body).height())
- $(previous).trigger(hiddenEvent)
- $(this._element).trigger(shownEvent)
- }.bind(this)
+ if (typeof offset != 'object') offsetBottom = offsetTop = offset
+ if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element)
+ if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
- if (target) {
- this._activate(target, /** @type {Element} */ (target.parentNode), complete)
- } else {
- complete()
- }
-}
+ var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)
+ if (this.affixed != affix) {
+ if (this.unpin != null) this.$element.css('top', '')
-/**
- * @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()
- }
+ var affixType = 'affix' + (affix ? '-' + affix : '')
+ var e = $.Event(affixType + '.bs.affix')
- if (active) {
- $(active).removeClass(Tab._ClassName.IN)
- }
-}
+ this.$element.trigger(e)
+ if (e.isDefaultPrevented()) return
-/**
- * @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)
+ this.affixed = affix
+ this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
- var dropdownChild = $(active).find(Tab._Selector.DROPDOWN_ACTIVE_CHILD)[0]
- if (dropdownChild) {
- $(dropdownChild).removeClass(Tab._ClassName.ACTIVE)
+ this.$element
+ .removeClass(Affix.RESET)
+ .addClass(affixType)
+ .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
}
- var activeToggle = $(active).find(Tab._Selector.DATA_TOGGLE)[0]
- if (activeToggle) {
- activeToggle.setAttribute('aria-expanded', false)
+ if (affix == 'bottom') {
+ this.$element.offset({
+ top: scrollHeight - height - offsetBottom
+ })
}
}
- $(element).addClass(Tab._ClassName.ACTIVE)
-
- 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)
- }
- 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)
- }
+ // AFFIX PLUGIN DEFINITION
+ // =======================
- elementToggle = $(element).find(Tab._Selector.DATA_TOGGLE)[0]
- if (elementToggle) {
- elementToggle.setAttribute('aria-expanded', true)
- }
- }
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.affix')
+ var options = typeof option == 'object' && option
- if (opt_callback) {
- opt_callback()
+ if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
}
-}
+ var old = $.fn.affix
-/**
- * ------------------------------------------------------------------------
- * jQuery Interface + noConflict implementaiton
- * ------------------------------------------------------------------------
- */
+ $.fn.affix = Plugin
+ $.fn.affix.Constructor = Affix
-/**
- * @const
- * @type {Function}
- */
-$.fn[Tab._NAME] = Tab._jQueryInterface
+ // AFFIX NO CONFLICT
+ // =================
-/**
- * @const
- * @type {Function}
- */
-$.fn[Tab._NAME]['Constructor'] = Tab
+ $.fn.affix.noConflict = function () {
+ $.fn.affix = old
+ return this
+ }
-/**
- * @const
- * @type {Function}
- */
-$.fn[Tab._NAME]['noConflict'] = function () {
- $.fn[Tab._NAME] = Tab._JQUERY_NO_CONFLICT
- return this
-}
+ // AFFIX DATA-API
+ // ==============
+ $(window).on('load', function () {
+ $('[data-spy="affix"]').each(function () {
+ var $spy = $(this)
+ var data = $spy.data()
+ data.offset = data.offset || {}
-// TAB DATA-API
-// ============
+ if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom
+ if (data.offsetTop != null) data.offset.top = data.offsetTop
-var clickHandler = function (e) {
- e.preventDefault()
- Tab._jQueryInterface.call($(this), 'show')
-}
+ Plugin.call($spy, data)
+ })
+ })
-$(document)
- .on('click.bs.tab.data-api', Tab._Selector.DATA_TOGGLE, clickHandler)
+}(jQuery);
diff --git a/dist/js/bootstrap.min.js b/dist/js/bootstrap.min.js
index 089780400d..7665759238 100644
--- a/dist/js/bootstrap.min.js
+++ b/dist/js/bootstrap.min.js
@@ -3,75 +3,5 @@
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
-if (typeof jQuery === 'undefined') {
- throw new Error('Bootstrap\'s JavaScript requires jQuery')
-}
-+function ($) {
- var version = $.fn.jquery.split(' ')[0].split('.')
- if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1)) {
- throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher')
- }
-}(jQuery);
-
-(function($){var h,k,l={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};function m(a){var b=a.getAttribute("data-target");b||(b=a.getAttribute("href")||"",b=/^#[a-z]/i.test(b)?b:null);return b}function aa(a){do a+=~~(1E6*Math.random());while(document.getElementById(a));return a}function ba(){return{ha:k.end,ia:k.end,handle:function(a){if($(a.target).is(this))return a.handleObj.handler.apply(this,arguments)}}}
-function n(a){(new Function("bs","return bs"))(a.offsetHeight)}function ca(){if(window.QUnit)return!1;var a=document.createElement("bootstrap"),b;for(b in l)if(void 0!==a.style[b])return{end:l[b]};return!1}$.fn.f=function(a){var b=!1;$(this).one("bsTransitionEnd",function(){b=!0});var c=function(){b||$(this).trigger(k.end)}.bind(this);setTimeout(c,a)};$(function(){(k=ca())&&($.event.special.bsTransitionEnd=ba())});function p(a){if(a)$(a).on("click",'[data-dismiss="alert"]',q(this))}p.VERSION="4.0.0";var da=$.fn.alert;function s(a){return this.each(function(){var b=$(this),c=b.data("bs.alert");c||(c=new p(this),b.data("bs.alert",c));if("close"===a)c[a](this)})}function q(a){return function(b){b&&b.preventDefault();a.close(this)}}
-p.prototype.close=function(a){var b=!1,c=m(a);c&&(b=$(c)[0]);b||(b=$(a).closest(".alert")[0]);a=b;b=$.Event("close.bs.alert");$(a).trigger(b);b.isDefaultPrevented()||($(a).removeClass("in"),k&&$(a).hasClass("fade")?$(a).one("bsTransitionEnd",this.N.bind(this,a)).f(150):this.N(a))};p.prototype.N=function(a){$(a).detach().trigger("closed.bs.alert").remove()};$.fn.alert=s;$.fn.alert.Constructor=p;$.fn.alert.noConflict=function(){$.fn.alert=da;return s};
-$(document).on("click.bs.alert.data-api",'[data-dismiss="alert"]',q(new p));function t(a){this.a=a}t.VERSION="4.0.0";var ea=$.fn.button;function u(a){return this.each(function(){var b=$(this).data("bs.button");b||(b=new t(this),$(this).data("bs.button",b));if("toggle"===a)b[a]()})}
-t.prototype.toggle=function(){var a=!0,b=$(this.a).closest('[data-toggle="buttons"]')[0];if(b){var c=$(this.a).find("input")[0];c&&("radio"==c.type&&(c.checked&&$(this.a).hasClass("active")?a=!1:(b=$(b).find(".active")[0])&&$(b).removeClass("active")),a&&(c.checked=!$(this.a).hasClass("active"),$(this.a).trigger("change")))}else this.a.setAttribute("aria-pressed",!$(this.a).hasClass("active"));a&&$(this.a).toggleClass("active")};$.fn.button=u;$.fn.button.Constructor=t;
-$.fn.button.noConflict=function(){$.fn.button=ea;return this};$(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(a){a.preventDefault();a=a.target;$(a).hasClass("btn")||(a=$(a).closest(".btn"));u.call($(a),"toggle")}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(a){var b=$(a.target).closest(".btn")[0];$(b).toggleClass("focus",/^focus(in)?$/.test(a.type))});function v(a,b){this.a=$(a)[0];this.F=$(this.a).find(fa)[0];this.c=b||null;this.l=this.G=!1;this.h=this.M=this.k=null;if(this.c.keyboard)$(this.a).on("keydown.bs.carousel",this.Y.bind(this));if("hover"==this.c.pause&&!("ontouchstart"in document.documentElement))$(this.a).on("mouseenter.bs.carousel",this.pause.bind(this)).on("mouseleave.bs.carousel",this.cycle.bind(this))}v.VERSION="4.0.0";v.Defaults={interval:5E3,pause:"hover",wrap:!0,keyboard:!0,slide:!1};var fa=".carousel-indicators",ga=$.fn.carousel;
-function w(a){return this.each(function(){var b=$(this).data("bs.carousel"),c=$.extend({},v.Defaults,$(this).data(),"object"==typeof a&&a),d="string"==typeof a?a:c.ja;b||(b=new v(this,c),$(this).data("bs.carousel",b));if("number"==typeof a)x(b,a);else if(d)b[d]();else c.interval&&(b.pause(),b.cycle())})}v.prototype.next=function(){this.l||z(this,"next")};v.prototype.prev=function(){this.l||z(this,"prev")};
-v.prototype.pause=function(a){a||(this.G=!0);$(this.a).find(".next, .prev")[0]&&k&&($(this.a).trigger(k.end),this.cycle(!0));clearInterval(this.k);this.k=null};v.prototype.cycle=function(a){a||(this.G=!1);this.k&&(clearInterval(this.k),this.k=null);this.c.interval&&!this.G&&(this.k=setInterval(this.next.bind(this),this.c.interval))};v.prototype.getConfig=function(){return this.c};
-function x(a,b){a.M=$(a.a).find(".active.carousel-item")[0];var c=A(a,a.M);if(!(b>a.h.length-1||0>b))if(a.l)$(a.a).one("slid.bs.carousel",function(){x(this,b)}.bind(a));else c==b?(a.pause(),a.cycle()):z(a,b>c?"next":"prev",a.h[b])}v.prototype.Y=function(a){a.preventDefault();if(!/input|textarea/i.test(a.target.tagName))switch(a.which){case 37:this.prev();break;case 39:this.next()}};function A(a,b){a.h=$.makeArray($(b).parent().find(".carousel-item"));return a.h.indexOf(b)}
-function ha(a,b,c){var d=A(a,c);if(("prev"===b&&0===d||"next"===b&&d==a.h.length-1)&&!a.c.wrap)return c;b=(d+("prev"==b?-1:1))%a.h.length;return-1===b?a.h[a.h.length-1]:a.h[b]}function ia(a,b,c){b=$.Event("slide.bs.carousel",{relatedTarget:b,direction:c});$(a.a).trigger(b);return b}function ja(a,b){if(a.F){$(a.F).find(".active").removeClass("active");var c=a.F.children[A(a,b)];c&&$(c).addClass("active")}}
-function z(a,b,c){var d=$(a.a).find(".active.carousel-item")[0],e=c||d&&ha(a,b,d);c=!!a.k;var f="next"==b?"left":"right";if(e&&$(e).hasClass("active"))a.l=!1;else if(!ia(a,e,f).isDefaultPrevented()&&d&&e){a.l=!0;c&&a.pause();ja(a,e);var g=$.Event("slid.bs.carousel",{relatedTarget:e,direction:f});k&&$(a.a).hasClass("slide")?($(e).addClass(b),n(e),$(d).addClass(f),$(e).addClass(f),$(d).one("bsTransitionEnd",function(){$(e).removeClass(f).removeClass(b);$(e).addClass("active");$(d).removeClass("active").removeClass(b).removeClass(f);
-this.l=!1;setTimeout(function(){$(this.a).trigger(g)}.bind(this),0)}.bind(a)).f(600)):($(d).removeClass("active"),$(e).addClass("active"),a.l=!1,$(a.a).trigger(g));c&&a.cycle()}}$.fn.carousel=w;$.fn.carousel.Constructor=v;$.fn.carousel.noConflict=function(){$.fn.carousel=ga;return this};
-$(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(a){var b=m(this);if(b&&(b=$(b)[0])&&$(b).hasClass("carousel")){var c=$.extend({},$(b).data(),$(this).data()),d=this.getAttribute("data-slide-to");d&&(c.interval=!1);w.call($(b),c);d&&x($(b).data("bs.carousel"),d);a.preventDefault()}});$(window).on("load",function(){$('[data-ride="carousel"]').each(function(){var a=$(this);w.call(a,a.data())})});function B(a,b){this.a=a;this.c=$.extend({},B.Defaults,b);this.o="string"==typeof this.c.trigger?$(this.c.trigger)[0]:this.c.trigger;this.A=!1;var c;if(this.c.parent){var d='[data-toggle="collapse"][data-parent="'+this.c.parent+'"]';c=$(this.c.parent)[0];for(var d=$.makeArray($(c).find(d)),e=0;e<d.length;e++)C(D(d[e]),d[e])}else c=null;this.Z=c;this.c.parent||C(this.a,this.o);this.c.toggle&&this.toggle()}B.VERSION="4.0.0";B.Defaults={toggle:!0,trigger:'[data-toggle="collapse"]',parent:null};
-var ka=$.fn.collapse;function E(a){return this.each(function(){var b=$(this),c=b.data("bs.collapse"),d=$.extend({},B.Defaults,b.data(),"object"==typeof a&&a);!c&&d.toggle&&"show"==a&&(d.toggle=!1);c||(c=new B(this,d),b.data("bs.collapse",c));if("string"==typeof a)c[a]()})}function D(a){return(a=m(a))?$(a)[0]:null}B.prototype.toggle=function(){$(this.a).hasClass("in")?this.hide():this.show()};
-B.prototype.show=function(){if(!this.A&&!$(this.a).hasClass("in")){var a,b;this.Z&&(b=$.makeArray($(".panel > .in, .panel > .collapsing")),b.length||(b=null));if(b&&(a=$(b).data("bs.collapse"))&&a.A)return;var c=$.Event("show.bs.collapse");$(this.a).trigger(c);if(!c.isDefaultPrevented()){b&&(E.call($(b),"hide"),a||$(b).data("bs.collapse",null));var d=F(this);$(this.a).removeClass("collapse").addClass("collapsing");this.a.style[d]=0;this.a.setAttribute("aria-expanded",!0);this.o&&($(this.o).removeClass("collapsed"),
-this.o.setAttribute("aria-expanded",!0));this.setTransitioning(!0);a=function(){$(this.a).removeClass("collapsing").addClass("collapse").addClass("in");this.a.style[d]="";this.setTransitioning(!1);$(this.a).trigger("shown.bs.collapse")}.bind(this);k?(b="scroll"+(d[0].toUpperCase()+d.slice(1)),$(this.a).one("bsTransitionEnd",a).f(600),this.a.style[d]=this.a[b]+"px"):a()}}};
-B.prototype.hide=function(){if(!this.A&&$(this.a).hasClass("in")){var a=$.Event("hide.bs.collapse");$(this.a).trigger(a);if(!a.isDefaultPrevented()){a=F(this);this.a.style[a]=this.a["width"===a?"offsetWidth":"offsetHeight"]+"px";n(this.a);$(this.a).addClass("collapsing").removeClass("collapse").removeClass("in");this.a.setAttribute("aria-expanded",!1);this.o&&($(this.o).addClass("collapsed"),this.o.setAttribute("aria-expanded",!1));this.setTransitioning(!0);var b=function(){this.setTransitioning(!1);
-$(this.a).removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")}.bind(this);this.a.style[a]=0;if(!k)return b();$(this.a).one("bsTransitionEnd",b).f(600)}}};B.prototype.setTransitioning=function(a){this.A=a};function F(a){return $(a.a).hasClass("width")?"width":"height"}function C(a,b){if(a){var c=$(a).hasClass("in");a.setAttribute("aria-expanded",c);b&&(b.setAttribute("aria-expanded",c),$(b).toggleClass("collapsed",!c))}}$.fn.collapse=E;$.fn.collapse.Constructor=B;
-$.fn.collapse.noConflict=function(){$.fn.collapse=ka;return this};$(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(a){a.preventDefault();a=D(this);var b=$(a).data("bs.collapse")?"toggle":$.extend({},$(this).data(),{trigger:this});E.call($(a),b)});function G(a){$(a).on("click.bs.dropdown",this.toggle)}G.VERSION="4.0.0";var la=$.fn.dropdown;
-function H(a){if(!a||3!=a.which){(a=$(".dropdown-backdrop")[0])&&a.parentNode.removeChild(a);a=$.makeArray($('[data-toggle="dropdown"]'));for(var b=0;b<a.length;b++){var c=I(a[b]),d={relatedTarget:a[b]};if($(c).hasClass("open")){var e=$.Event("hide.bs.dropdown",d);$(c).trigger(e);e.isDefaultPrevented()||(a[b].setAttribute("aria-expanded","false"),$(c).removeClass("open").trigger("hidden.bs.dropdown",d))}}}}function I(a){var b=m(a);if(b)var c=$(b)[0];return c||a.parentNode}
-function J(a){if(/(38|40|27|32)/.test(a.which)&&!/input|textarea/i.test(a.target.tagName)&&(a.preventDefault(),a.stopPropagation(),!this.disabled&&!$(this).hasClass("disabled"))){var b=I(this),c=$(b).hasClass("open");!c&&27!=a.which||c&&27==a.which?(27==a.which&&(a=$(b).find('[data-toggle="dropdown"]')[0],$(a).trigger("focus")),$(this).trigger("click")):(b=$.makeArray($('[role="menu"] li:not(.divider) a, [role="listbox"] li:not(.divider) a')),b=b.filter(function(a){return a.offsetWidth||a.offsetHeight}),
-b.length&&(c=b.indexOf(a.target),38==a.which&&0<c&&c--,40==a.which&&c<b.length-1&&c++,~c||(c=0),b[c].focus()))}}
-G.prototype.toggle=function(){if(!this.disabled&&!$(this).hasClass("disabled")){var a=I(this),b=$(a).hasClass("open");H();if(b)return!1;"ontouchstart"in document.documentElement&&!$(a).closest(".navbar-nav").length&&(b=document.createElement("div"),b.className="dropdown-backdrop",this.parentNode.insertBefore(this,b),$(b).on("click",H));var b={relatedTarget:this},c=$.Event("show.bs.dropdown",b);$(a).trigger(c);if(!c.isDefaultPrevented())return this.focus(),this.setAttribute("aria-expanded","true"),
-$(a).toggleClass("open"),$(a).trigger("shown.bs.dropdown",b),!1}};$.fn.dropdown=function(a){return this.each(function(){var b=$(this).data("bs.dropdown");b||$(this).data("bs.dropdown",b=new G(this));"string"===typeof a&&b[a].call(this)})};$.fn.dropdown.Constructor=G;$.fn.dropdown.noConflict=function(){$.fn.dropdown=la;return this};
-$(document).on("click.bs.dropdown.data-api",H).on("click.bs.dropdown.data-api",".dropdown form",function(a){a.stopPropagation()}).on("click.bs.dropdown.data-api",'[data-toggle="dropdown"]',G.prototype.toggle).on("keydown.bs.dropdown.data-api",'[data-toggle="dropdown"]',J).on("keydown.bs.dropdown.data-api",'[role="menu"]',J).on("keydown.bs.dropdown.data-api",'[role="listbox"]',J);function K(a,b){this.c=b;this.a=a;this.d=null;this.w=this.g=!1;this.B=0}K.VERSION="4.0.0";K.Defaults={backdrop:!0,keyboard:!0,show:!0};var ma=$.fn.modal;function L(a,b){return this.each(function(){var c=$(this).data("bs.modal"),d=$.extend({},K.Defaults,$(this).data(),"object"==typeof a&&a);c||(c=new K(this,d),$(this).data("bs.modal",c));if("string"==typeof a)c[a](b);else d.show&&c.show(b)})}K.prototype.toggle=function(a){return this.g?this.hide():this.show(a)};
-K.prototype.show=function(a){var b=$.Event("show.bs.modal",{relatedTarget:a});$(this.a).trigger(b);if(!this.g&&!b.isDefaultPrevented()){this.g=!0;this.w=document.body.scrollHeight>document.documentElement.clientHeight;b=document.createElement("div");b.className="modal-scrollbar-measure";document.body.appendChild(b);var c=b.offsetWidth-b.clientWidth;document.body.removeChild(b);this.B=c;b=parseInt($(document.body).css("padding-right")||0,10);this.w&&(document.body.style.paddingRight=b+this.B+"px");
-$(document.body).addClass("modal-open");M(this);N(this);$(this.a).on("click.dismiss.bs.modal",'[data-dismiss="modal"]',this.hide.bind(this));O(this,this.$.bind(this,a))}};
-K.prototype.hide=function(a){a&&a.preventDefault();a=$.Event("hide.bs.modal");$(this.a).trigger(a);this.g&&!a.isDefaultPrevented()&&(this.g=!1,M(this),N(this),$(document).off("focusin.bs.modal"),$(this.a).removeClass("in"),this.a.setAttribute("aria-hidden",!0),$(this.a).off("click.dismiss.bs.modal"),k&&$(this.a).hasClass("fade")?$(this.a).one("bsTransitionEnd",this.P.bind(this)).f(300):this.P())};
-K.prototype.$=function(a){var b=k&&$(this.a).hasClass("fade");this.a.parentNode&&this.a.parentNode.nodeType==Node.ELEMENT_NODE||document.body.appendChild(this.a);this.a.style.display="block";this.a.scrollTop=0;this.c.backdrop&&P(this);b&&n(this.a);$(this.a).addClass("in");this.a.setAttribute("aria-hidden",!1);na(this);var c=$.Event("shown.bs.modal",{relatedTarget:a});a=function(){this.a.focus();$(this.a).trigger(c)}.bind(this);b?(b=$(this.a).find(".modal-dialog")[0],$(b).one("bsTransitionEnd",a).f(300)):
-a()};function na(a){$(document).off("focusin.bs.modal").on("focusin.bs.modal",function(a){this.a===a.target||$(this.a).has(a.target).length||this.a.focus()}.bind(a))}function M(a){if(a.g&&a.c.keyboard)$(a.a).on("keydown.dismiss.bs.modal",function(a){27===a.which&&this.hide()}.bind(a));else a.g||$(a.a).off("keydown.dismiss.bs.modal")}function N(a){if(a.g)$(window).on("resize.bs.modal",a.X.bind(a));else $(window).off("resize.bs.modal")}
-K.prototype.P=function(){this.a.style.display="none";O(this,function(){$(document.body).removeClass("modal-open");this.a.style.paddingLeft="";this.a.style.paddingRight="";document.body.style.paddingRight="";$(this.a).trigger("hidden.bs.modal")}.bind(this))};
-function O(a,b){var c=$(a.a).hasClass("fade")?"fade":"";if(a.g&&a.c.backdrop){var d=k&&c;a.d=document.createElement("div");a.d.className="modal-backdrop";c&&$(a.d).addClass(c);$(a.a).prepend(a.d);$(a.d).on("click.dismiss.bs.modal",function(a){a.target===a.currentTarget&&("static"===this.c.backdrop?this.a.focus():this.hide())}.bind(a));d&&n(a.d);$(a.d).addClass("in");b&&(d?$(a.d).one("bsTransitionEnd",b).f(150):b())}else!a.g&&a.d?($(a.d).removeClass("in"),c=function(){this.d&&(this.d.parentNode.removeChild(this.d),
-this.d=null);b&&b()}.bind(a),k&&$(a.a).hasClass("fade")?$(a.d).one("bsTransitionEnd",c).f(150):c()):b&&b()}K.prototype.X=function(){this.c.backdrop&&P(this);var a=this.a.scrollHeight>document.documentElement.clientHeight;!this.w&&a&&(this.a.style.paddingLeft=this.B+"px");this.w&&!a&&(this.a.style.paddingRight=this.B+"px")};function P(a){a.d.style.height=0;a.d.style.height=a.a.scrollHeight+"px"}$.fn.modal=L;$.fn.modal.Constructor=K;$.fn.modal.noConflict=function(){$.fn.modal=ma;return this};
-$(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(a){var b=m(this);if(b)var c=$(b)[0];b=$(c).data("bs.modal")?"toggle":$.extend({},$(c).data(),$(this).data());"A"==this.tagName&&a.preventDefault();var d=$(c).one("show.bs.modal",function(a){if(!a.isDefaultPrevented())d.one("hidden.bs.modal",function(){$(this).is(":visible")&&this.focus()}.bind(this))}.bind(this));L.call($(c),b,this)});function Q(a,b){this.e="BODY"==a.tagName?window:a;this.c=$.extend({},Q.Defaults,b);this.C=(this.c.target||"")+" .nav li > a";this.m=[];this.p=[];this.s=null;this.R=0;$(this.e).on("scroll.bs.scrollspy",this.Q.bind(this));this.refresh();this.Q()}Q.VERSION="4.0.0";Q.Defaults={offset:10};var oa=$.fn.scrollspy;function R(a){return this.each(function(){var b=$(this).data("bs.scrollspy"),c="object"===typeof a&&a||null;b||(b=new Q(this,c),$(this).data("bs.scrollspy",b));if("string"===typeof a)b[a]()})}
-Q.prototype.refresh=function(){var a="offset",b=0;this.e!==this.e.window&&(a="position",b=this.e===window?this.e.scrollY:this.e.scrollTop);this.m=[];this.p=[];this.R=this.e.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight);$.makeArray($(this.C)).map(function(c){var d;(c=m(c))&&(d=$(c)[0]);if(d&&(d.offsetWidth||d.offsetHeight))return[$(d)[a]().top+b,c]}).filter(function(a){return a}).sort(function(a,b){return a[0]-b[0]}).forEach(function(a){this.m.push(a[0]);
-this.p.push(a[1])}.bind(this))};Q.prototype.Q=function(){var a=(this.e===window?this.e.scrollY:this.e.scrollTop)+this.c.offset,b=this.e.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight),c=this.c.offset+b-this.e.offsetHeight;this.R!=b&&this.refresh();a>=c&&(b=this.p[this.p.length-1],this.s!=b&&this.r(b));if(this.s&&a<this.m[0])this.s=null,S(this);else for(b=this.m.length;b--;)this.s!=this.p[b]&&a>=this.m[b]&&(!this.m[b+1]||a<this.m[b+1])&&this.r(this.p[b])};
-Q.prototype.r=function(a){this.s=a;S(this);for(var b=$(this.C+'[data-target="'+a+'"],'+this.C+'[href="'+a+'"]').parents("li"),c=b.length;c--;){$(b[c]).addClass("active");var d=b[c].parentNode;d&&$(d).hasClass("dropdown-menu")&&(d=$(d).closest("li.dropdown")[0],$(d).addClass("active"))}$(this.e).trigger("activate.bs.scrollspy",{relatedTarget:a})};function S(a){a=$(a.C).parentsUntil(a.c.target,".active");for(var b=a.length;b--;)$(a[b]).removeClass("active")}$.fn.scrollspy=R;
-$.fn.scrollspy.Constructor=Q;$.fn.scrollspy.noConflict=function(){$.fn.scrollspy=oa;return this};$(window).on("load.bs.scrollspy.data-api",function(){for(var a=$.makeArray($('[data-spy="scroll"]')),b=a.length;b--;){var c=$(a[b]);R.call(c,c.data())}});function T(a,b){this.t=!0;this.u=0;this.j="";this.element=a;var c=$.extend({},this.constructor.Defaults,$(this.element).data(),b);c.delay&&"number"==typeof c.delay&&(c.delay={show:c.delay,hide:c.delay});this.b=c;this.D=this.W=null;this.b.viewport&&(this.S=$(this.b.viewport.selector||this.b.viewport)[0]);pa(this)}T.VERSION="4.0.0";
-T.Defaults={container:!1,animation:!0,placement:"top",selector:!1,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,viewport:{selector:"body",padding:0}};var U={fa:"top",da:"left",ea:"right",ca:"bottom"},qa={J:"hide.bs.tooltip",I:"hidden.bs.tooltip",K:"show.bs.tooltip",L:"shown.bs.tooltip"},ra=$.fn.tooltip;T.prototype.enable=function(){this.t=!0};
-T.prototype.disable=function(){this.t=!1};T.prototype.toggleEnabled=function(){this.t=!this.t};T.prototype.toggle=function(a){var b=this,c=this.q();a&&(b=$(a.currentTarget).data(c),b||(b=new this.constructor(a.currentTarget,V(this)),$(a.currentTarget).data(c,b)));$(b.i()).hasClass("in")?b.H(null,b):b.O(null,b)};T.prototype.destroy=function(){clearTimeout(this.u);this.hide(function(){$(this.element).off(".tooltip").removeData(this.q())}.bind(this))};
-T.prototype.show=function(){var a=$.Event(this.v().K);if(this.U()&&this.t){$(this.element).trigger(a);var b=$.contains(this.element.ownerDocument.documentElement,this.element);if(!a.isDefaultPrevented()&&b){a=this.i();b=aa(this.getName());a.setAttribute("id",b);this.element.setAttribute("aria-describedby",b);this.V();this.b.animation&&$(a).addClass("fade");var b="function"==typeof this.b.placement?this.b.placement.call(this,a,this.element):this.b.placement,c=/\s?auto?\s?/i,d=c.test(b);d&&(b=b.replace(c,
-"")||"top");a.parentNode&&a.parentNode.nodeType==Node.ELEMENT_NODE&&a.parentNode.removeChild(a);a.style.top=0;a.style.left=0;a.style.display="block";$(a).addClass("tooltip-"+b);$(a).data(this.q(),this);this.b.container?$(this.b.container)[0].appendChild(a):this.element.parentNode.insertBefore(a,this.element.nextSibling);var c=W(this),e=a.offsetWidth,a=a.offsetHeight,b=sa(this,d,b,c,e,a);ta(this,"bottom"==b?{top:c.top+c.height,left:c.left+c.width/2-e/2}:"top"==b?{top:c.top-a,left:c.left+c.width/2-
-e/2}:"left"==b?{top:c.top+c.height/2-a/2,left:c.left-e}:{top:c.top+c.height/2-a/2,left:c.left+c.width},b);a=function(){var a=this.ba;$(this.element).trigger(this.v().L);this.ba=null;"out"==a&&this.H(null,this)}.bind(this);k&&$(this.n).hasClass("fade")?$(this.n).one("bsTransitionEnd",a).f(150):a()}}};
-T.prototype.hide=function(a){var b=this.i(),c=$.Event(this.v().J),d=function(){"in"!=this.j&&b.parentNode.removeChild(b);this.element.removeAttribute("aria-describedby");$(this.element).trigger(this.v().I);a&&a()}.bind(this);$(this.element).trigger(c);c.isDefaultPrevented()||($(b).removeClass("in"),k&&$(this.n).hasClass("fade")?$(b).one("bsTransitionEnd",d).f(150):d(),this.j="")};T.prototype.getHoverState=function(){return this.j};h=T.prototype;h.getName=function(){return"tooltip"};h.q=function(){return"bs.tooltip"};
-h.v=function(){return qa};function X(a){var b=a.element.getAttribute("data-original-title");b||(b="function"===typeof a.b.title?a.b.title.call(a.element):a.b.title);return b}h.i=function(){return this.n=this.n||$(this.b.template)[0]};h.T=function(){return this.D=this.D||$(this.i()).find(".tooltip-arrow")[0]};h.U=function(){return!!X(this)};
-h.V=function(){var a=this.i(),b=X(this);$(a).find(".tooltip-inner")[0][this.b.html?"innerHTML":"innerText"]=b;$(a).removeClass("fade").removeClass("in");for(var c in U)$(a).removeClass("tooltip-"+c)};
-function pa(a){a.b.trigger.split(" ").forEach(function(a){if("click"==a)$(this.element).on("click.bs.tooltip",this.b.selector,this.toggle.bind(this));else if("manual"!=a){var c="hover"==a?"mouseenter":"focusin";a="hover"==a?"mouseleave":"focusout";$(this.element).on(c+".bs.tooltip",this.b.selector,this.O.bind(this)).on(a+".bs.tooltip",this.b.selector,this.H.bind(this))}}.bind(a));a.b.selector?a.b=$.extend({},a.b,{trigger:"manual",selector:""}):ua(a)}
-function V(a){var b={},c=a.constructor.Defaults;if(a.b)for(var d in a.b){var e=a.b[d];c[d]!=e&&(b[d]=e)}return b}function sa(a,b,c,d,e,f){if(b){b=c;var g=a.b.container?$(a.b.container)[0]:a.element.parentNode,g=W(a,g);c="bottom"==c&&d.bottom+f>g.bottom?"top":"top"==c&&d.top-f<g.top?"bottom":"right"==c&&d.right+e>g.width?"left":"left"==c&&d.left-e<g.left?"right":c;$(a.n).removeClass("tooltip-"+b).addClass("tooltip-"+c)}return c}
-function va(a,b,c,d,e){var f={top:0,left:0};if(!a.S)return f;var g=a.b.viewport&&a.b.viewport.padding||0;a=W(a,a.S);"right"===b||"left"===b?(d=c.top-g-a.scroll,c=c.top+g-a.scroll+e,d<a.top?f.top=a.top-d:c>a.top+a.height&&(f.top=a.top+a.height-c)):(e=c.left-g,c=c.left+g+d,e<a.left?f.left=a.left-e:c>a.width&&(f.left=a.left+a.width-c));return f}
-function W(a,b){var c=b||a.element,d="BODY"==c.tagName,e=c.getBoundingClientRect(),c=d?{top:0,left:0}:$(c).offset();return $.extend({},e,{scroll:d?document.documentElement.scrollTop||document.body.scrollTop:a.element.scrollTop},d?{width:window.innerWidth,height:window.innerHeight}:null,c)}
-function ta(a,b,c){var d=a.i(),e=d.offsetWidth,f=d.offsetHeight,g=parseInt(d.style.marginTop,10),r=parseInt(d.style.marginLeft,10);isNaN(g)&&(g=0);isNaN(r)&&(r=0);b.top+=g;b.left+=r;$.offset.setOffset(d,$.extend({ka:function(a){d.style.top=Math.round(a.top)+"px";d.style.left=Math.round(a.left)+"px"}},b),0);$(d).addClass("in");g=d.offsetWidth;r=d.offsetHeight;"top"==c&&r!=f&&(b.top=b.top+f-r);var y=va(a,c,b,g,r);y.left?b.left+=y.left:b.top+=y.top;e=(c="top"===c||"bottom"===c)?2*y.left-e+g:2*y.top-
-f+r;f=c?"offsetWidth":"offsetHeight";$(d).offset(b);wa(a,e,d[f],c)}function wa(a,b,c,d){a=a.T();a.style[d?"left":"top"]=50*(1-b/c)+"%";a.style[d?"top":"left"]=""}function ua(a){if(a.element.getAttribute("title")||"string"!=typeof a.element.getAttribute("data-original-title"))a.element.setAttribute("data-original-title",a.element.getAttribute("title")||""),a.element.setAttribute("title","")}
-h.O=function(a,b){var c=this.q(),d=b||$(a.currentTarget).data(c);d&&d.n&&d.n.offsetWidth?d.j="in":(d||(d=new this.constructor(a.currentTarget,V(this)),$(a.currentTarget).data(c,d)),clearTimeout(d.u),d.j="in",d.b.delay&&d.b.delay.show?d.u=setTimeout(function(){"in"==d.j&&d.show()},d.b.delay.show):d.show())};
-h.H=function(a,b){var c=this.q(),d=b||$(a.currentTarget).data(c);d||(d=new this.constructor(a.currentTarget,V(this)),$(a.currentTarget).data(c,d));clearTimeout(d.u);d.j="out";d.b.delay&&d.b.delay.hide?d.u=setTimeout(function(){"out"==d.j&&d.hide()},d.b.delay.hide):d.hide()};$.fn.tooltip=function(a){return this.each(function(){var b=$(this).data("bs.tooltip"),c="object"==typeof a?a:null;if(b||"destroy"!=a)if(b||(b=new T(this,c),$(this).data("bs.tooltip",b)),"string"===typeof a)b[a]()})};
-$.fn.tooltip.Constructor=T;$.fn.tooltip.noConflict=function(){$.fn.tooltip=ra;return this};if(!T)throw Error("Popover requires tooltip.js");function Y(a,b){T.apply(this,arguments)}(function(){function a(){}a.prototype=T.prototype;Y.prototype=new a;Y.prototype.constructor=Y})();Y.VERSION="4.0.0";Y.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>'});
-var xa={J:"hide.bs.popover",I:"hidden.bs.popover",K:"show.bs.popover",L:"shown.bs.popover"},ya=$.fn.popover;h=Y.prototype;h.getName=function(){return"popover"};h.q=function(){return"bs.popover"};h.v=function(){return xa};h.T=function(){return this.D=this.D||$(this.i()).find(".popover-arrow")[0]};
-h.V=function(){var a=this.i(),b=X(this),c=za(this),d=$(a).find(".popover-title")[0];d&&(d[this.b.html?"innerHTML":"innerText"]=b);$(a).find(".popover-content").children().detach().end()[this.b.html?"string"==typeof c?"html":"append":"text"](c);$(a).removeClass("fade").removeClass("in");for(var e in U)$(a).removeClass("popover-"+U[e])};h.U=function(){return X(this)||za(this)};h.i=function(){return this.W=this.W||$(this.b.template)[0]};
-function za(a){return a.element.getAttribute("data-content")||("function"==typeof a.b.content?a.b.content.call(a.element):a.b.content)}$.fn.popover=function(a){return this.each(function(){var b=$(this).data("bs.popover"),c="object"===typeof a?a:null;if(b||"destroy"!==a)if(b||(b=new Y(this,c),$(this).data("bs.popover",b)),"string"===typeof a)b[a]()})};$.fn.popover.Constructor=Y;$.fn.popover.noConflict=function(){$.fn.popover=ya;return this};function Z(a){this.a=a}Z.VERSION="4.0.0";function Aa(a){return this.each(function(){var b=$(this),c=b.data("bs.tab");c||(c=c=new Z(this),b.data("bs.tab",c));if("string"===typeof a)c[a]()})}
-Z.prototype.show=function(){if(!this.a.parentNode||this.a.parentNode.nodeType!=Node.ELEMENT_NODE||!$(this.a).parent().hasClass("active")){var a=$(this.a).closest("ul:not(.dropdown-menu)")[0],b=m(this.a);if(a){var c=$.makeArray($(a).find(".active"));(c=c[c.length-1])&&(c=$(c).find("a")[0])}var d=$.Event("hide.bs.tab",{relatedTarget:this.a}),e=$.Event("show.bs.tab",{relatedTarget:c});c&&$(c).trigger(d);$(this.a).trigger(e);if(!e.isDefaultPrevented()&&!d.isDefaultPrevented()){if(b)var f=$(b)[0];this.r($(this.a).closest("li")[0],
-a);a=function(){var a=$.Event("hidden.bs.tab",{relatedTarget:this.a}),b=$.Event("shown.bs.tab",{relatedTarget:c});$(c).trigger(a);$(this.a).trigger(b)}.bind(this);f?this.r(f,f.parentNode,a):a()}}};Z.prototype.r=function(a,b,c){var d=$(b).find(":scope > .active")[0];b=c&&k&&(d&&$(d).hasClass("fade")||!!$(b).find(":scope > .fade")[0]);a=this.aa.bind(this,a,d,b,c);d&&b?$(d).one("bsTransitionEnd",a).f(150):a();d&&$(d).removeClass("in")};
-Z.prototype.aa=function(a,b,c,d){if(b){$(b).removeClass("active");var e=$(b).find(":scope > .dropdown-menu > .active")[0];e&&$(e).removeClass("active");(b=$(b).find('[data-toggle="tab"], [data-toggle="pill"]')[0])&&b.setAttribute("aria-expanded",!1)}$(a).addClass("active");(b=$(a).find('[data-toggle="tab"], [data-toggle="pill"]')[0])&&b.setAttribute("aria-expanded",!0);c?(n(a),$(a).addClass("in")):$(a).removeClass("fade");a.parentNode&&$(a.parentNode).hasClass("dropdown-menu")&&((c=$(a).closest("li.dropdown")[0])&&
-$(c).addClass("active"),(b=$(a).find('[data-toggle="tab"], [data-toggle="pill"]')[0])&&b.setAttribute("aria-expanded",!0));d&&d()};$.fn.tab=Aa;$.fn.tab.Constructor=Z;$.fn.tab.noConflict=function(){$.fn.tab=Z.ga;return this};$(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(a){a.preventDefault();Aa.call($(this),"show")});})(jQuery);
+if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.4",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.4",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.4",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.4",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.4",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find('[role="menu"]'+h+', [role="listbox"]'+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&j<i.length-1&&j++,~j||(j=0),i.eq(j).trigger("focus")}}}};var h=a.fn.dropdown;a.fn.dropdown=d,a.fn.dropdown.Constructor=g,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=h,this},a(document).on("click.bs.dropdown.data-api",c).on("click.bs.dropdown.data-api",".dropdown form",function(a){a.stopPropagation()}).on("click.bs.dropdown.data-api",f,g.prototype.toggle).on("keydown.bs.dropdown.data-api",f,g.prototype.keydown).on("keydown.bs.dropdown.data-api",".dropdown-menu",g.prototype.keydown)}(jQuery),+function(a){"use strict";function b(b,d){return this.each(function(){var e=a(this),f=e.data("bs.modal"),g=a.extend({},c.DEFAULTS,e.data(),"object"==typeof b&&b);f||e.data("bs.modal",f=new c(this,g)),"string"==typeof b?f[b](d):g.show&&f.show(d)})}var c=function(b,c){this.options=c,this.$body=a(document.body),this.$element=a(b),this.$dialog=this.$element.find(".modal-dialog"),this.$backdrop=null,this.isShown=null,this.originalBodyPad=null,this.scrollbarWidth=0,this.ignoreBackdropClick=!1,this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,a.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};c.VERSION="3.3.4",c.TRANSITION_DURATION=300,c.BACKDROP_TRANSITION_DURATION=150,c.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},c.prototype.toggle=function(a){return this.isShown?this.hide():this.show(a)},c.prototype.show=function(b){var d=this,e=a.Event("show.bs.modal",{relatedTarget:b});this.$element.trigger(e),this.isShown||e.isDefaultPrevented()||(this.isShown=!0,this.checkScrollbar(),this.setScrollbar(),this.$body.addClass("modal-open"),this.escape(),this.resize(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',a.proxy(this.hide,this)),this.$dialog.on("mousedown.dismiss.bs.modal",function(){d.$element.one("mouseup.dismiss.bs.modal",function(b){a(b.target).is(d.$element)&&(d.ignoreBackdropClick=!0)})}),this.backdrop(function(){var e=a.support.transition&&d.$element.hasClass("fade");d.$element.parent().length||d.$element.appendTo(d.$body),d.$element.show().scrollTop(0),d.adjustDialog(),e&&d.$element[0].offsetWidth,d.$element.addClass("in"),d.enforceFocus();var f=a.Event("shown.bs.modal",{relatedTarget:b});e?d.$dialog.one("bsTransitionEnd",function(){d.$element.trigger("focus").trigger(f)}).emulateTransitionEnd(c.TRANSITION_DURATION):d.$element.trigger("focus").trigger(f)}))},c.prototype.hide=function(b){b&&b.preventDefault(),b=a.Event("hide.bs.modal"),this.$element.trigger(b),this.isShown&&!b.isDefaultPrevented()&&(this.isShown=!1,this.escape(),this.resize(),a(document).off("focusin.bs.modal"),this.$element.removeClass("in").off("click.dismiss.bs.modal").off("mouseup.dismiss.bs.modal"),this.$dialog.off("mousedown.dismiss.bs.modal"),a.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",a.proxy(this.hideModal,this)).emulateTransitionEnd(c.TRANSITION_DURATION):this.hideModal())},c.prototype.enforceFocus=function(){a(document).off("focusin.bs.modal").on("focusin.bs.modal",a.proxy(function(a){this.$element[0]===a.target||this.$element.has(a.target).length||this.$element.trigger("focus")},this))},c.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keydown.dismiss.bs.modal",a.proxy(function(a){27==a.which&&this.hide()},this)):this.isShown||this.$element.off("keydown.dismiss.bs.modal")},c.prototype.resize=function(){this.isShown?a(window).on("resize.bs.modal",a.proxy(this.handleUpdate,this)):a(window).off("resize.bs.modal")},c.prototype.hideModal=function(){var a=this;this.$element.hide(),this.backdrop(function(){a.$body.removeClass("modal-open"),a.resetAdjustments(),a.resetScrollbar(),a.$element.trigger("hidden.bs.modal")})},c.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},c.prototype.backdrop=function(b){var d=this,e=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var f=a.support.transition&&e;if(this.$backdrop=a(document.createElement("div")).addClass("modal-backdrop "+e).appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(a){return this.ignoreBackdropClick?void(this.ignoreBackdropClick=!1):void(a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus():this.hide()))},this)),f&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;f?this.$backdrop.one("bsTransitionEnd",b).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):b()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var g=function(){d.removeBackdrop(),b&&b()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",g).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):g()}else b&&b()},c.prototype.handleUpdate=function(){this.adjustDialog()},c.prototype.adjustDialog=function(){var a=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth<a,this.scrollbarWidth=this.measureScrollbar()},c.prototype.setScrollbar=function(){var a=parseInt(this.$body.css("padding-right")||0,10);this.originalBodyPad=document.body.style.paddingRight||"",this.bodyIsOverflowing&&this.$body.css("padding-right",a+this.scrollbarWidth)},c.prototype.resetScrollbar=function(){this.$body.css("padding-right",this.originalBodyPad)},c.prototype.measureScrollbar=function(){var a=document.createElement("div");a.className="modal-scrollbar-measure",this.$body.append(a);var b=a.offsetWidth-a.clientWidth;return this.$body[0].removeChild(a),b};var d=a.fn.modal;a.fn.modal=b,a.fn.modal.Constructor=c,a.fn.modal.noConflict=function(){return a.fn.modal=d,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(c){var d=a(this),e=d.attr("href"),f=a(d.attr("data-target")||e&&e.replace(/.*(?=#[^\s]+$)/,"")),g=f.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(e)&&e},f.data(),d.data());d.is("a")&&c.preventDefault(),f.one("show.bs.modal",function(a){a.isDefaultPrevented()||f.one("hidden.bs.modal",function(){d.is(":visible")&&d.trigger("focus")})}),b.call(f,g,this)})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.tooltip",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.type=null,this.options=null,this.enabled=null,this.timeout=null,this.hoverState=null,this.$element=null,this.inState=null,this.init("tooltip",a,b)};c.VERSION="3.3.4",c.TRANSITION_DURATION=150,c.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-m<o.top?"bottom":"right"==h&&k.right+l>o.width?"left":"left"==h&&k.left-l<o.left?"right":h,f.removeClass(n).addClass(h)}var p=this.getCalculatedOffset(h,k,l,m);this.applyPlacement(p,h);var q=function(){var a=e.hoverState;e.$element.trigger("shown.bs."+e.type),e.hoverState=null,"out"==a&&e.leave(e)};a.support.transition&&this.$tip.hasClass("fade")?f.one("bsTransitionEnd",q).emulateTransitionEnd(c.TRANSITION_DURATION):q()}},c.prototype.applyPlacement=function(b,c){var d=this.tip(),e=d[0].offsetWidth,f=d[0].offsetHeight,g=parseInt(d.css("margin-top"),10),h=parseInt(d.css("margin-left"),10);isNaN(g)&&(g=0),isNaN(h)&&(h=0),b.top+=g,b.left+=h,a.offset.setOffset(d[0],a.extend({using:function(a){d.css({top:Math.round(a.top),left:Math.round(a.left)})}},b),0),d.addClass("in");var i=d[0].offsetWidth,j=d[0].offsetHeight;"top"==c&&j!=f&&(b.top=b.top+f-j);var k=this.getViewportAdjustedDelta(c,b,i,j);k.left?b.left+=k.left:b.top+=k.top;var l=/top|bottom/.test(c),m=l?2*k.left-e+i:2*k.top-f+j,n=l?"offsetWidth":"offsetHeight";d.offset(b),this.replaceArrow(m,d[0][n],l)},c.prototype.replaceArrow=function(a,b,c){this.arrow().css(c?"left":"top",50*(1-a/b)+"%").css(c?"top":"left","")},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.options.html?"html":"text"](b),a.removeClass("fade in top bottom left right")},c.prototype.hide=function(b){function d(){"in"!=e.hoverState&&f.detach(),e.$element.removeAttr("aria-describedby").trigger("hidden.bs."+e.type),b&&b()}var e=this,f=a(this.$tip),g=a.Event("hide.bs."+this.type);return this.$element.trigger(g),g.isDefaultPrevented()?void 0:(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one("bsTransitionEnd",d).emulateTransitionEnd(c.TRANSITION_DURATION):d(),this.hoverState=null,this)},c.prototype.fixTitle=function(){var a=this.$element;(a.attr("title")||"string"!=typeof a.attr("data-original-title"))&&a.attr("data-original-title",a.attr("title")||"").attr("title","")},c.prototype.hasContent=function(){return this.getTitle()},c.prototype.getPosition=function(b){b=b||this.$element;var c=b[0],d="BODY"==c.tagName,e=c.getBoundingClientRect();null==e.width&&(e=a.extend({},e,{width:e.right-e.left,height:e.bottom-e.top}));var f=d?{top:0,left:0}:b.offset(),g={scroll:d?document.documentElement.scrollTop||document.body.scrollTop:b.scrollTop()},h=d?{width:a(window).width(),height:a(window).height()}:null;return a.extend({},e,g,h,f)},c.prototype.getCalculatedOffset=function(a,b,c,d){return"bottom"==a?{top:b.top+b.height,left:b.left+b.width/2-c/2}:"top"==a?{top:b.top-d,left:b.left+b.width/2-c/2}:"left"==a?{top:b.top+b.height/2-d/2,left:b.left-c}:{top:b.top+b.height/2-d/2,left:b.left+b.width}},c.prototype.getViewportAdjustedDelta=function(a,b,c,d){var e={top:0,left:0};if(!this.$viewport)return e;var f=this.options.viewport&&this.options.viewport.padding||0,g=this.getPosition(this.$viewport);if(/right|left/.test(a)){var h=b.top-f-g.scroll,i=b.top+f-g.scroll+d;h<g.top?e.top=g.top-h:i>g.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;j<g.left?e.left=g.left-j:k>g.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.4",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.4",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b<e[0])return this.activeTarget=null,this.clear();for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(void 0===e[a+1]||b<e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,this.clear();var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),
+d.trigger("activate.bs.scrollspy")},b.prototype.clear=function(){a(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var d=a.fn.scrollspy;a.fn.scrollspy=c,a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=d,this},a(window).on("load.bs.scrollspy.data-api",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new c(this)),"string"==typeof b&&e[b]()})}var c=function(b){this.element=a(b)};c.VERSION="3.3.4",c.TRANSITION_DURATION=150,c.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a"),f=a.Event("hide.bs.tab",{relatedTarget:b[0]}),g=a.Event("show.bs.tab",{relatedTarget:e[0]});if(e.trigger(f),b.trigger(g),!g.isDefaultPrevented()&&!f.isDefaultPrevented()){var h=a(d);this.activate(b.closest("li"),c),this.activate(h,h.parent(),function(){e.trigger({type:"hidden.bs.tab",relatedTarget:b[0]}),b.trigger({type:"shown.bs.tab",relatedTarget:e[0]})})}}},c.prototype.activate=function(b,d,e){function f(){g.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.4",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file
diff --git a/dist/js/npm.js b/dist/js/npm.js
index f9e8027f18..bf6aa80602 100644
--- a/dist/js/npm.js
+++ b/dist/js/npm.js
@@ -1,12 +1,13 @@
// This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.
-require('../../js/util.js')
+require('../../js/transition.js')
require('../../js/alert.js')
require('../../js/button.js')
require('../../js/carousel.js')
require('../../js/collapse.js')
require('../../js/dropdown.js')
require('../../js/modal.js')
-require('../../js/scrollspy.js')
require('../../js/tooltip.js')
require('../../js/popover.js')
-require('../../js/tab.js') \ No newline at end of file
+require('../../js/scrollspy.js')
+require('../../js/tab.js')
+require('../../js/affix.js') \ No newline at end of file
diff --git a/grunt/bs-commonjs-generator.js b/grunt/bs-commonjs-generator.js
index b0642cd8f1..5a3fc156d1 100644
--- a/grunt/bs-commonjs-generator.js
+++ b/grunt/bs-commonjs-generator.js
@@ -1,4 +1,5 @@
'use strict';
+
var fs = require('fs');
var path = require('path');
diff --git a/js/.jscsrc b/js/.jscsrc
index caf1938f21..9544d2d2b5 100644
--- a/js/.jscsrc
+++ b/js/.jscsrc
@@ -17,8 +17,11 @@
"requireCamelCaseOrUpperCaseIdentifiers": true,
"requireCapitalizedConstructors": true,
"requireCommaBeforeLineBreak": true,
+ "requireDollarBeforejQueryAssignment": true,
"requireDotNotation": true,
"requireLineFeedAtFileEnd": true,
+ "requirePaddingNewLinesAfterUseStrict": true,
+ "requirePaddingNewLinesBeforeExport": true,
"requireSpaceAfterBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", "<", ">=", "<="],
"requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"],
"requireSpaceAfterLineComment": true,
@@ -31,7 +34,9 @@
"requireSpacesInFunctionExpression": { "beforeOpeningCurlyBrace": true },
"requireSpacesInNamedFunctionExpression": { "beforeOpeningCurlyBrace": true },
"requireSpacesInsideObjectBrackets": "allButNested",
+ "validateAlignedFunctionParameters": true,
"validateIndentation": 2,
"validateLineBreaks": "LF",
+ "validateNewlineAfterArrayElements": true,
"validateQuoteMarks": "'"
}
diff --git a/js/affix.js b/js/affix.js
new file mode 100644
index 0000000000..9997379123
--- /dev/null
+++ b/js/affix.js
@@ -0,0 +1,162 @@
+/* ========================================================================
+ * Bootstrap: affix.js v3.3.4
+ * http://getbootstrap.com/javascript/#affix
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // AFFIX CLASS DEFINITION
+ // ======================
+
+ var Affix = function (element, options) {
+ this.options = $.extend({}, Affix.DEFAULTS, options)
+
+ this.$target = $(this.options.target)
+ .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
+ .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
+
+ this.$element = $(element)
+ this.affixed = null
+ this.unpin = null
+ this.pinnedOffset = null
+
+ this.checkPosition()
+ }
+
+ Affix.VERSION = '3.3.4'
+
+ Affix.RESET = 'affix affix-top affix-bottom'
+
+ Affix.DEFAULTS = {
+ offset: 0,
+ target: window
+ }
+
+ Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
+ var scrollTop = this.$target.scrollTop()
+ var position = this.$element.offset()
+ var targetHeight = this.$target.height()
+
+ if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false
+
+ if (this.affixed == 'bottom') {
+ if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
+ return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
+ }
+
+ var initializing = this.affixed == null
+ var colliderTop = initializing ? scrollTop : position.top
+ var colliderHeight = initializing ? targetHeight : height
+
+ if (offsetTop != null && scrollTop <= offsetTop) return 'top'
+ if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'
+
+ return false
+ }
+
+ Affix.prototype.getPinnedOffset = function () {
+ if (this.pinnedOffset) return this.pinnedOffset
+ this.$element.removeClass(Affix.RESET).addClass('affix')
+ var scrollTop = this.$target.scrollTop()
+ var position = this.$element.offset()
+ return (this.pinnedOffset = position.top - scrollTop)
+ }
+
+ Affix.prototype.checkPositionWithEventLoop = function () {
+ setTimeout($.proxy(this.checkPosition, this), 1)
+ }
+
+ Affix.prototype.checkPosition = function () {
+ if (!this.$element.is(':visible')) return
+
+ var height = this.$element.height()
+ var offset = this.options.offset
+ var offsetTop = offset.top
+ var offsetBottom = offset.bottom
+ var scrollHeight = Math.max($(document).height(), $(document.body).height())
+
+ if (typeof offset != 'object') offsetBottom = offsetTop = offset
+ if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element)
+ if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
+
+ var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)
+
+ if (this.affixed != affix) {
+ if (this.unpin != null) this.$element.css('top', '')
+
+ var affixType = 'affix' + (affix ? '-' + affix : '')
+ var e = $.Event(affixType + '.bs.affix')
+
+ this.$element.trigger(e)
+
+ if (e.isDefaultPrevented()) return
+
+ this.affixed = affix
+ this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
+
+ this.$element
+ .removeClass(Affix.RESET)
+ .addClass(affixType)
+ .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
+ }
+
+ if (affix == 'bottom') {
+ this.$element.offset({
+ top: scrollHeight - height - offsetBottom
+ })
+ }
+ }
+
+
+ // AFFIX PLUGIN DEFINITION
+ // =======================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.affix')
+ var options = typeof option == 'object' && option
+
+ if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ var old = $.fn.affix
+
+ $.fn.affix = Plugin
+ $.fn.affix.Constructor = Affix
+
+
+ // AFFIX NO CONFLICT
+ // =================
+
+ $.fn.affix.noConflict = function () {
+ $.fn.affix = old
+ return this
+ }
+
+
+ // AFFIX DATA-API
+ // ==============
+
+ $(window).on('load', function () {
+ $('[data-spy="affix"]').each(function () {
+ var $spy = $(this)
+ var data = $spy.data()
+
+ data.offset = data.offset || {}
+
+ if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom
+ if (data.offsetTop != null) data.offset.top = data.offsetTop
+
+ Plugin.call($spy, data)
+ })
+ })
+
+}(jQuery);
diff --git a/js/alert.js b/js/alert.js
index 38bce06a4d..1925ef011c 100644
--- a/js/alert.js
+++ b/js/alert.js
@@ -1,260 +1,94 @@
-/** =======================================================================
- * Bootstrap: alert.js v4.0.0
+/* ========================================================================
+ * Bootstrap: alert.js v3.3.4
* 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
- *
- * ========================================================================
- */
-
-'use strict';
-
-
-/**
- * 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))
- }
-}
-
-
-/**
- * @const
- * @type {string}
- */
-Alert['VERSION'] = '4.0.0'
-
-
-/**
- * @const
- * @type {string}
- * @private
- */
-Alert._NAME = 'alert'
-
-
-/**
- * @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
-
+ * ======================================================================== */
-/**
- * @const
- * @type {Function}
- * @private
- */
-Alert._JQUERY_NO_CONFLICT = $.fn[Alert._NAME]
++function ($) {
+ 'use strict';
-/**
- * @const
- * @enum {string}
- * @private
- */
-Alert._Event = {
- CLOSE : 'close.bs.alert',
- CLOSED : 'closed.bs.alert'
-}
+ // ALERT CLASS DEFINITION
+ // ======================
+ var dismiss = '[data-dismiss="alert"]'
+ var Alert = function (el) {
+ $(el).on('click', dismiss, this.close)
+ }
-/**
- * @const
- * @enum {string}
- * @private
- */
-Alert._ClassName = {
- ALERT : 'alert',
- FADE : 'fade',
- IN : 'in'
-}
+ Alert.VERSION = '3.3.4'
+ Alert.TRANSITION_DURATION = 150
-/**
- * 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)
+ Alert.prototype.close = function (e) {
+ var $this = $(this)
+ var selector = $this.attr('data-target')
- if (!data) {
- data = new Alert(this)
- $this.data(Alert._DATA_KEY, data)
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
}
- if (opt_config === 'close') {
- data[opt_config](this)
- }
- })
-}
+ var $parent = $(selector)
+ if (e) e.preventDefault()
-/**
- * Close the alert component
- * @param {Alert} alertInstance
- * @return {Function}
- * @private
- */
-Alert._handleDismiss = function (alertInstance) {
- return function (event) {
- if (event) {
- event.preventDefault()
+ if (!$parent.length) {
+ $parent = $this.closest('.alert')
}
- alertInstance['close'](this)
- }
-}
-
+ $parent.trigger(e = $.Event('close.bs.alert'))
-/**
- * Close the alert component
- * @param {Element} element
- */
-Alert.prototype['close'] = function (element) {
- var rootElement = this._getRootElement(element)
- var customEvent = this._triggerCloseEvent(rootElement)
+ if (e.isDefaultPrevented()) return
- if (customEvent.isDefaultPrevented()) return
+ $parent.removeClass('in')
- this._removeElement(rootElement)
-}
-
-
-/**
- * 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 (selector) {
- parent = $(selector)[0]
- }
+ function removeElement() {
+ // detach from parent, fire event then clean up data
+ $parent.detach().trigger('closed.bs.alert').remove()
+ }
- if (!parent) {
- parent = $(element).closest('.' + Alert._ClassName.ALERT)[0]
+ $.support.transition && $parent.hasClass('fade') ?
+ $parent
+ .one('bsTransitionEnd', removeElement)
+ .emulateTransitionEnd(Alert.TRANSITION_DURATION) :
+ removeElement()
}
- return parent
-}
-
-/**
- * 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 PLUGIN DEFINITION
+ // =======================
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.alert')
-/**
- * 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
+ if (!data) $this.data('bs.alert', (data = new Alert(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
}
- $(element)
- .one(Bootstrap.TRANSITION_END, this._destroyElement.bind(this, element))
- .emulateTransitionEnd(Alert._TRANSITION_DURATION)
-}
-
+ var old = $.fn.alert
-/**
- * clean up any lingering jquery data and kill element
- * @private
- */
-Alert.prototype._destroyElement = function (element) {
- $(element)
- .detach()
- .trigger(Alert._Event.CLOSED)
- .remove()
-}
+ $.fn.alert = Plugin
+ $.fn.alert.Constructor = Alert
-/**
- * ------------------------------------------------------------------------
- * jQuery Interface + noConflict implementaiton
- * ------------------------------------------------------------------------
- */
-
-/**
- * @const
- * @type {Function}
- */
-$.fn[Alert._NAME] = Alert._jQueryInterface
-
-
-/**
- * @const
- * @type {Function}
- */
-$.fn[Alert._NAME]['Constructor'] = Alert
+ // ALERT NO CONFLICT
+ // =================
+ $.fn.alert.noConflict = function () {
+ $.fn.alert = old
+ return this
+ }
-/**
- * @return {Function}
- */
-$.fn[Alert._NAME]['noConflict'] = function () {
- $.fn[Alert._NAME] = Alert._JQUERY_NO_CONFLICT
- return Alert._jQueryInterface
-}
+ // ALERT DATA-API
+ // ==============
-/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
- */
+ $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
-$(document).on('click.bs.alert.data-api', Alert._DISMISS_SELECTOR, Alert._handleDismiss(new Alert))
+}(jQuery);
diff --git a/js/button.js b/js/button.js
index 8ee2d6b084..3e2e345127 100644
--- a/js/button.js
+++ b/js/button.js
@@ -1,207 +1,120 @@
-/** =======================================================================
- * Bootstrap: button.js v4.0.0
+/* ========================================================================
+ * Bootstrap: button.js v3.3.4
* http://getbootstrap.com/javascript/#buttons
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ========================================================================
- * @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 (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)
- }
- }
+
++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.4'
+
+ 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 += '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])
+
+ if (state == 'loadingText') {
+ this.isLoading = true
+ $el.addClass(d).attr(d, d)
+ } else if (this.isLoading) {
+ this.isLoading = false
+ $el.removeClass(d).removeAttr(d)
}
+ }, this), 0)
+ }
- if (triggerChangeEvent) {
- input.checked = !$(this._element).hasClass(Button._ClassName.ACTIVE)
- $(this._element).trigger('change')
+ 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')) changed = false
+ $parent.find('.active').removeClass('active')
+ this.$element.addClass('active')
+ } else if ($input.prop('type') == 'checkbox') {
+ if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false
+ this.$element.toggleClass('active')
}
+ $input.prop('checked', this.$element.hasClass('active'))
+ if (changed) $input.trigger('change')
+ } else {
+ this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
+ this.$element.toggleClass('active')
}
- } else {
- this._element.setAttribute('aria-pressed', !$(this._element).hasClass(Button._ClassName.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
+ // ===============
- if (!$(button).hasClass(Button._ClassName.BUTTON)) {
- button = $(button).closest(Button._Selector.BUTTON)
- }
+ $(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')
+ if (!($(e.target).is('input[type="radio"]') || $(e.target).is('input[type="checkbox"]'))) 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))
+ })
- 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))
- })
+}(jQuery);
diff --git a/js/carousel.js b/js/carousel.js
index 8f33f48601..a38ef14d0b 100644
--- a/js/carousel.js
+++ b/js/carousel.js
@@ -1,576 +1,237 @@
-/** =======================================================================
- * Bootstrap: carousel.js v4.0.0
+/* ========================================================================
+ * Bootstrap: carousel.js v3.3.4
* 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
-
- /** @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)
-
- } else if (action) {
- data[action]()
-
- } else if (config.interval) {
- data['pause']()
- data['cycle']()
- }
- })
-}
+ * ======================================================================== */
-/**
- * Click handler for data api
- * @param {Event} event
- * @this {Element}
- * @private
- */
-Carousel._dataApiClickHandler = function (event) {
- var selector = Bootstrap.getSelectorFromElement(this)
++function ($) {
+ 'use strict';
- if (!selector) {
- return
- }
-
- var target = $(selector)[0]
-
- if (!target || !$(target).hasClass(Carousel._ClassName.CAROUSEL)) {
- return
- }
+ // CAROUSEL CLASS DEFINITION
+ // =========================
- var config = $.extend({}, $(target).data(), $(this).data())
+ var Carousel = function (element, options) {
+ this.$element = $(element)
+ this.$indicators = this.$element.find('.carousel-indicators')
+ this.options = options
+ this.paused = null
+ this.sliding = null
+ this.interval = null
+ this.$active = null
+ this.$items = null
- var slideIndex = this.getAttribute('data-slide-to')
- if (slideIndex) {
- config.interval = false
- }
-
- Carousel._jQueryInterface.call($(target), config)
+ this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))
- if (slideIndex) {
- $(target).data(Carousel._DATA_KEY).to(slideIndex)
+ 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))
}
- event.preventDefault()
-}
-
-
-/**
- * Advance the carousel to the next slide
- */
-Carousel.prototype['next'] = function () {
- if (!this._isSliding) {
- this._slide(Carousel._Direction.NEXT)
- }
-}
+ Carousel.VERSION = '3.3.4'
+ Carousel.TRANSITION_DURATION = 600
-/**
- * Return the carousel to the previous slide
- */
-Carousel.prototype['prev'] = function () {
- if (!this._isSliding) {
- this._slide(Carousel._Direction.PREVIOUS)
+ Carousel.DEFAULTS = {
+ interval: 5000,
+ pause: 'hover',
+ wrap: true,
+ keyboard: true
}
-}
+ 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
+ }
-/**
- * Pause the carousel cycle
- * @param {Event=} opt_event
- */
-Carousel.prototype['pause'] = function (opt_event) {
- if (!opt_event) {
- this._isPaused = true
+ e.preventDefault()
}
- if ($(this._element).find(Carousel._Selector.NEXT_PREV)[0] && Bootstrap.transition) {
- $(this._element).trigger(Bootstrap.transition.end)
- this['cycle'](true)
- }
+ Carousel.prototype.cycle = function (e) {
+ e || (this.paused = false)
- clearInterval(this._interval)
- this._interval = null
-}
+ this.interval && clearInterval(this.interval)
+ this.options.interval
+ && !this.paused
+ && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
-/**
- * Cycle to the next carousel item
- * @param {Event|boolean=} opt_event
- */
-Carousel.prototype['cycle'] = function (opt_event) {
- if (!opt_event) {
- this._isPaused = false
+ return this
}
- if (this._interval) {
- clearInterval(this._interval)
- this._interval = null
+ Carousel.prototype.getItemIndex = function (item) {
+ this.$items = item.parent().children('.item')
+ return this.$items.index(item || this.$active)
}
- if (this._config['interval'] && !this._isPaused) {
- this._interval = setInterval(this['next'].bind(this), this._config['interval'])
+ 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)
}
-}
-
-/**
- * @return {Object}
- */
-Carousel.prototype['getConfig'] = function () {
- return this._config
-}
+ Carousel.prototype.to = function (pos) {
+ var that = this
+ var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active'))
+ if (pos > (this.$items.length - 1) || pos < 0) return
-/**
- * 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]
+ if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid"
+ if (activeIndex == pos) return this.pause().cycle()
- var activeIndex = this._getItemIndex(this._activeElement)
-
- if (index > (this._items.length - 1) || index < 0) {
- return
- }
-
- if (this._isSliding) {
- $(this._element).one(Carousel._Event.SLID, function () { this.to(index) }.bind(this))
- return
+ return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos))
}
- if (activeIndex == index) {
- this['pause']()
- this['cycle']()
- return
- }
+ Carousel.prototype.pause = function (e) {
+ e || (this.paused = true)
- var direction = index > activeIndex ?
- Carousel._Direction.NEXT :
- Carousel._Direction.PREVIOUS
-
- this._slide(direction, this._items[index])
-}
+ if (this.$element.find('.next, .prev').length && $.support.transition) {
+ this.$element.trigger($.support.transition.end)
+ this.cycle(true)
+ }
+ this.interval = clearInterval(this.interval)
-/**
- * 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))
+ return this
}
- 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))
+ Carousel.prototype.next = function () {
+ if (this.sliding) return
+ return this.slide('next')
}
-}
-
-
-/**
- * Keydown handler
- * @param {Event} event
- * @private
- */
-Carousel.prototype._keydown = function (event) {
- event.preventDefault()
- if (/input|textarea/i.test(event.target.tagName)) return
-
- 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
+ Carousel.prototype.prev = function () {
+ if (this.sliding) return
+ return this.slide('prev')
}
- var delta = direction == Carousel._Direction.PREVIOUS ? -1 : 1
- var itemIndex = (activeIndex + delta) % this._items.length
-
- return itemIndex === -1 ? this._items[this._items.length - 1] : this._items[itemIndex]
-}
-
+ Carousel.prototype.slide = function (type, next) {
+ var $active = this.$element.find('.item.active')
+ var $next = next || this.getItemForDirection(type, $active)
+ var isCycling = this.interval
+ var direction = type == 'next' ? 'left' : 'right'
+ var that = this
-/**
- * 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)
+ if ($next.hasClass('active')) return (this.sliding = false)
- return slideEvent
-}
+ 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
-/**
- * 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)
+ isCycling && this.pause()
- var nextIndicator = this._indicatorsElement.children[this._getItemIndex(element)]
- if (nextIndicator) {
- $(nextIndicator).addClass(Carousel._ClassName.ACTIVE)
+ if (this.$indicators.length) {
+ this.$indicators.find('.active').removeClass('active')
+ var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)])
+ $nextIndicator && $nextIndicator.addClass('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 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)
+ }
- var directionalClassName = direction == Carousel._Direction.NEXT ?
- Carousel._ClassName.LEFT :
- Carousel._ClassName.RIGHT
+ isCycling && this.cycle()
- if (nextElement && $(nextElement).hasClass(Carousel._ClassName.ACTIVE)) {
- this._isSliding = false
- return
+ return this
}
- 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 PLUGIN DEFINITION
+ // ==========================
- this._isSliding = true
+ 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 (isCycling) {
- this['pause']()
+ 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()
+ })
}
- this._setActiveIndicatorElement(nextElement)
+ var old = $.fn.carousel
- var slidEvent = $.Event(Carousel._Event.SLID, { relatedTarget: nextElement, direction: directionalClassName })
+ $.fn.carousel = Plugin
+ $.fn.carousel.Constructor = Carousel
- if (Bootstrap.transition && $(this._element).hasClass(Carousel._ClassName.SLIDE)) {
- $(nextElement).addClass(direction)
- Bootstrap.reflow(nextElement)
+ // CAROUSEL NO CONFLICT
+ // ====================
- $(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)
-
- 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)
+ $.fn.carousel.noConflict = function () {
+ $.fn.carousel = old
+ return this
}
- if (isCycling) {
- this['cycle']()
- }
-}
+ // CAROUSEL DATA-API
+ // =================
-/**
- * ------------------------------------------------------------------------
- * jQuery Interface + noConflict implementaiton
- * ------------------------------------------------------------------------
- */
+ 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
-/**
- * @const
- * @type {Function}
- */
-$.fn[Carousel._NAME] = Carousel._jQueryInterface
-
-
-/**
- * @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
-}
+ Plugin.call($target, options)
+ if (slideIndex) {
+ $target.data('bs.carousel').to(slideIndex)
+ }
-/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
- */
+ e.preventDefault()
+ }
-$(document)
- .on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', Carousel._dataApiClickHandler)
+ $(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)
- Carousel._jQueryInterface.call($carousel, /** @type {Object} */ ($carousel.data()))
+ $(window).on('load', function () {
+ $('[data-ride="carousel"]').each(function () {
+ var $carousel = $(this)
+ Plugin.call($carousel, $carousel.data())
+ })
})
-})
+
+}(jQuery);
diff --git a/js/collapse.js b/js/collapse.js
index 156163e3f9..954513c5e1 100644
--- a/js/collapse.js
+++ b/js/collapse.js
@@ -1,455 +1,211 @@
-/** =======================================================================
- * Bootstrap: collapse.js v4.0.0
+/* ========================================================================
+ * Bootstrap: collapse.js v3.3.4
* 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';
-/**
- * Our collapse class.
- * @param {Element!} element
- * @param {Object=} opt_config
- * @constructor
- */
-var Collapse = function (element, opt_config) {
+ // COLLAPSE PUBLIC CLASS DEFINITION
+ // ================================
- /** @private {Element} */
- this._element = element
+ var Collapse = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, Collapse.DEFAULTS, options)
+ this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' +
+ '[data-toggle="collapse"][data-target="#' + element.id + '"]')
+ this.transitioning = null
- /** @private {Object} */
- this._config = $.extend({}, Collapse['Defaults'], opt_config)
+ if (this.options.parent) {
+ this.$parent = this.getParent()
+ } else {
+ this.addAriaAndCollapsedClass(this.$element, this.$trigger)
+ }
- /** @private {Element} */
- this._trigger = typeof this._config['trigger'] == 'string' ?
- $(this._config['trigger'])[0] : this._config['trigger']
+ if (this.options.toggle) this.toggle()
+ }
- /** @private {boolean} */
- this._isTransitioning = false
+ Collapse.VERSION = '3.3.4'
- /** @private {?Element} */
- this._parent = this._config['parent'] ? this._getParent() : null
+ Collapse.TRANSITION_DURATION = 350
- if (!this._config['parent']) {
- this._addAriaAndCollapsedClass(this._element, this._trigger)
+ Collapse.DEFAULTS = {
+ toggle: true
}
- if (this._config['toggle']) {
- this['toggle']()
+ Collapse.prototype.dimension = function () {
+ var hasWidth = this.$element.hasClass('width')
+ return hasWidth ? 'width' : 'height'
}
-}
-
-
-/**
- * @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 (!data && config['toggle'] && opt_config == 'show') {
- config['toggle'] = false
- }
+ Collapse.prototype.show = function () {
+ if (this.transitioning || this.$element.hasClass('in')) return
- if (!data) {
- data = new Collapse(this, config)
- $this.data(Collapse._DATA_KEY, data)
- }
+ var activesData
+ var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing')
- if (typeof opt_config == 'string') {
- data[opt_config]()
+ if (actives && actives.length) {
+ activesData = actives.data('bs.collapse')
+ if (activesData && activesData.transitioning) return
}
- })
-}
+ var startEvent = $.Event('show.bs.collapse')
+ this.$element.trigger(startEvent)
+ if (startEvent.isDefaultPrevented()) return
-/**
- * Function for getting target element from element
- * @return {Element}
- * @private
- */
-Collapse._getTargetFromElement = function (element) {
- var selector = Bootstrap.getSelectorFromElement(element)
-
- return selector ? $(selector)[0] : null
-}
-
+ if (actives && actives.length) {
+ Plugin.call(actives, 'hide')
+ activesData || actives.data('bs.collapse', null)
+ }
-/**
- * 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']()
- }
-}
+ var dimension = this.dimension()
+ this.$element
+ .removeClass('collapse')
+ .addClass('collapsing')[dimension](0)
+ .attr('aria-expanded', true)
-/**
- * Show's the collapsing element
- */
-Collapse.prototype['show'] = function () {
- if (this._isTransitioning || $(this._element).hasClass(Collapse._ClassName.IN)) {
- return
- }
+ this.$trigger
+ .removeClass('collapsed')
+ .attr('aria-expanded', true)
- var activesData, actives
+ this.transitioning = 1
- if (this._parent) {
- actives = $.makeArray($(Collapse._Selector.ACTIVES))
- if (!actives.length) {
- actives = null
+ var complete = function () {
+ this.$element
+ .removeClass('collapsing')
+ .addClass('collapse in')[dimension]('')
+ this.transitioning = 0
+ this.$element
+ .trigger('shown.bs.collapse')
}
- }
- if (actives) {
- activesData = $(actives).data(Collapse._DATA_KEY)
- if (activesData && activesData._isTransitioning) {
- return
- }
- }
+ if (!$.support.transition) return complete.call(this)
- var startEvent = $.Event(Collapse._Event.SHOW)
- $(this._element).trigger(startEvent)
- if (startEvent.isDefaultPrevented()) {
- return
- }
+ var scrollSize = $.camelCase(['scroll', dimension].join('-'))
- if (actives) {
- Collapse._jQueryInterface.call($(actives), 'hide')
- if (!activesData) {
- $(actives).data(Collapse._DATA_KEY, null)
- }
+ this.$element
+ .one('bsTransitionEnd', $.proxy(complete, this))
+ .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
}
- var dimension = this._getDimension()
+ Collapse.prototype.hide = function () {
+ if (this.transitioning || !this.$element.hasClass('in')) return
- $(this._element)
- .removeClass(Collapse._ClassName.COLLAPSE)
- .addClass(Collapse._ClassName.COLLAPSING)
+ var startEvent = $.Event('hide.bs.collapse')
+ this.$element.trigger(startEvent)
+ if (startEvent.isDefaultPrevented()) return
- this._element.style[dimension] = 0
- this._element.setAttribute('aria-expanded', true)
+ var dimension = this.dimension()
- if (this._trigger) {
- $(this._trigger).removeClass(Collapse._ClassName.COLLAPSED)
- this._trigger.setAttribute('aria-expanded', true)
- }
+ this.$element[dimension](this.$element[dimension]())[0].offsetHeight
- this['setTransitioning'](true)
+ this.$element
+ .addClass('collapsing')
+ .removeClass('collapse in')
+ .attr('aria-expanded', false)
- var complete = function () {
- $(this._element)
- .removeClass(Collapse._ClassName.COLLAPSING)
- .addClass(Collapse._ClassName.COLLAPSE)
- .addClass(Collapse._ClassName.IN)
+ this.$trigger
+ .addClass('collapsed')
+ .attr('aria-expanded', false)
- this._element.style[dimension] = ''
+ this.transitioning = 1
- this['setTransitioning'](false)
+ var complete = function () {
+ this.transitioning = 0
+ this.$element
+ .removeClass('collapsing')
+ .addClass('collapse')
+ .trigger('hidden.bs.collapse')
+ }
- $(this._element).trigger(Collapse._Event.SHOWN)
- }.bind(this)
+ if (!$.support.transition) return complete.call(this)
- if (!Bootstrap.transition) {
- complete()
- return
+ this.$element
+ [dimension](0)
+ .one('bsTransitionEnd', $.proxy(complete, this))
+ .emulateTransitionEnd(Collapse.TRANSITION_DURATION)
}
- 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.toggle = function () {
+ this[this.$element.hasClass('in') ? 'hide' : 'show']()
}
- 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.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()
}
- 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)
+ Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
+ var isOpen = $element.hasClass('in')
- this._element.style[dimension] = 0
-
- if (!Bootstrap.transition) {
- return complete()
+ $element.attr('aria-expanded', isOpen)
+ $trigger
+ .toggleClass('collapsed', !isOpen)
+ .attr('aria-expanded', isOpen)
}
- $(this._element)
- .one(Bootstrap.TRANSITION_END, complete)
- .emulateTransitionEnd(Collapse._TRANSITION_DURATION)
-}
-
-
+ function getTargetFromTrigger($trigger) {
+ var href
+ var target = $trigger.attr('data-target')
+ || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
-/**
- * @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 $(target)
}
- return parent
-}
+ // COLLAPSE PLUGIN DEFINITION
+ // ==========================
-/**
- * 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)
+ 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)
- if (trigger) {
- trigger.setAttribute('aria-expanded', isOpen)
- $(trigger).toggleClass(Collapse._ClassName.COLLAPSED, !isOpen)
- }
+ if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false
+ if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
}
-}
+ var old = $.fn.collapse
+ $.fn.collapse = Plugin
+ $.fn.collapse.Constructor = Collapse
-/**
- * ------------------------------------------------------------------------
- * jQuery Interface + noConflict implementaiton
- * ------------------------------------------------------------------------
- */
-/**
- * @const
- * @type {Function}
- */
-$.fn[Collapse._NAME] = Collapse._jQueryInterface
-
-
-/**
- * @const
- * @type {Function}
- */
-$.fn[Collapse._NAME]['Constructor'] = Collapse
+ // COLLAPSE NO CONFLICT
+ // ====================
+ $.fn.collapse.noConflict = function () {
+ $.fn.collapse = old
+ return this
+ }
-/**
- * @const
- * @type {Function}
- */
-$.fn[Collapse._NAME]['noConflict'] = function () {
- $.fn[Collapse._NAME] = Collapse._JQUERY_NO_CONFLICT
- return this
-}
+ // COLLAPSE DATA-API
+ // =================
-/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
- */
+ $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
+ var $this = $(this)
-$(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (event) {
- event.preventDefault()
+ if (!$this.attr('data-target')) e.preventDefault()
- var target = Collapse._getTargetFromElement(this)
+ var $target = getTargetFromTrigger($this)
+ var data = $target.data('bs.collapse')
+ var option = data ? 'toggle' : $this.data()
- var data = $(target).data(Collapse._DATA_KEY)
- var config = data ? 'toggle' : $.extend({}, $(this).data(), { trigger: this })
+ Plugin.call($target, option)
+ })
- Collapse._jQueryInterface.call($(target), config)
-})
+}(jQuery);
diff --git a/js/dropdown.js b/js/dropdown.js
index 4599d5ba92..8fd86a0254 100644
--- a/js/dropdown.js
+++ b/js/dropdown.js
@@ -1,322 +1,165 @@
-/** =======================================================================
- * Bootstrap: dropdown.js v4.0.0
- * http://getbootstrap.com/javascript/#dropdown
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.3.4
+ * http://getbootstrap.com/javascript/#dropdowns
* ========================================================================
* 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)))
- }
+ * ======================================================================== */
- if (typeof opt_config === 'string') {
- data[opt_config].call(this)
- }
- })
-}
++function ($) {
+ 'use strict';
-/**
- * @param {Event=} opt_event
- * @private
- */
-Dropdown._clearMenus = function (opt_event) {
- if (opt_event && opt_event.which == 3) {
- return
- }
+ // DROPDOWN CLASS DEFINITION
+ // =========================
- var backdrop = $(Dropdown._Selector.BACKDROP)[0]
- if (backdrop) {
- backdrop.parentNode.removeChild(backdrop)
+ var backdrop = '.dropdown-backdrop'
+ var toggle = '[data-toggle="dropdown"]'
+ var Dropdown = function (element) {
+ $(element).on('click.bs.dropdown', this.toggle)
}
- var toggles = /** @type {Array.<Element>} */ ($.makeArray($(Dropdown._Selector.DATA_TOGGLE)))
-
- for (var i = 0; i < toggles.length; i++) {
- var parent = Dropdown._getParentFromElement(toggles[i])
- var relatedTarget = { 'relatedTarget': toggles[i] }
+ Dropdown.VERSION = '3.3.4'
- if (!$(parent).hasClass(Dropdown._ClassName.OPEN)) {
- continue
- }
+ function getParent($this) {
+ var selector = $this.attr('data-target')
- var hideEvent = $.Event(Dropdown._Event.HIDE, relatedTarget)
- $(parent).trigger(hideEvent)
- if (hideEvent.isDefaultPrevented()) {
- continue
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
}
- toggles[i].setAttribute('aria-expanded', 'false')
+ var $parent = selector && $(selector)
- $(parent)
- .removeClass(Dropdown._ClassName.OPEN)
- .trigger(Dropdown._Event.HIDDEN, relatedTarget)
+ return $parent && $parent.length ? $parent : $this.parent()
}
-}
+ 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 }
-/**
- * @param {Element} element
- * @return {Element}
- * @private
- */
-Dropdown._getParentFromElement = function (element) {
- var selector = Bootstrap.getSelectorFromElement(element)
+ if (!$parent.hasClass('open')) return
- if (selector) {
- var parent = $(selector)[0]
- }
+ if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return
- return /** @type {Element} */ (parent || element.parentNode)
-}
+ $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
+ if (e.isDefaultPrevented()) return
-/**
- * @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
+ $this.attr('aria-expanded', 'false')
+ $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
+ })
}
- event.preventDefault()
- event.stopPropagation()
+ Dropdown.prototype.toggle = function (e) {
+ var $this = $(this)
- if (this.disabled || $(this).hasClass(Dropdown._ClassName.DISABLED)) {
- return
- }
+ if ($this.is('.disabled, :disabled')) return
- var parent = Dropdown._getParentFromElement(this)
- var isActive = $(parent).hasClass(Dropdown._ClassName.OPEN)
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
- 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
- }
+ clearMenus()
- var items = $.makeArray($(Dropdown._Selector.VISIBLE_ITEMS))
+ if (!isActive) {
+ if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+ // if mobile we use a backdrop because click events don't delegate
+ $(document.createElement('div'))
+ .addClass('dropdown-backdrop')
+ .insertAfter($(this))
+ .on('click', clearMenus)
+ }
- items = items.filter(function (item) {
- return item.offsetWidth || item.offsetHeight
- })
+ var relatedTarget = { relatedTarget: this }
+ $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
- if (!items.length) {
- return
- }
+ if (e.isDefaultPrevented()) return
- var index = items.indexOf(event.target)
+ $this
+ .trigger('focus')
+ .attr('aria-expanded', 'true')
- if (event.which == 38 && index > 0) index-- // up
- if (event.which == 40 && index < items.length - 1) index++ // down
- if (!~index) index = 0
+ $parent
+ .toggleClass('open')
+ .trigger('shown.bs.dropdown', relatedTarget)
+ }
- items[index].focus()
-}
+ return false
+ }
+ Dropdown.prototype.keydown = function (e) {
+ if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return
-/**
- * Toggles the dropdown
- * @this {Element}
- * @return {boolean|undefined}
- */
-Dropdown.prototype['toggle'] = function () {
- if (this.disabled || $(this).hasClass(Dropdown._ClassName.DISABLED)) {
- return
- }
+ var $this = $(this)
- var parent = Dropdown._getParentFromElement(this)
- var isActive = $(parent).hasClass(Dropdown._ClassName.OPEN)
+ e.preventDefault()
+ e.stopPropagation()
- Dropdown._clearMenus()
+ if ($this.is('.disabled, :disabled')) return
- if (isActive) {
- return false
- }
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
- 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 (!isActive && e.which != 27 || isActive && e.which == 27) {
+ if (e.which == 27) $parent.find(toggle).trigger('focus')
+ return $this.trigger('click')
+ }
- var relatedTarget = { 'relatedTarget': this }
- var showEvent = $.Event(Dropdown._Event.SHOW, relatedTarget)
+ var desc = ' li:not(.disabled):visible a'
+ var $items = $parent.find('[role="menu"]' + desc + ', [role="listbox"]' + desc)
- $(parent).trigger(showEvent)
+ if (!$items.length) return
- if (showEvent.isDefaultPrevented()) {
- return
- }
+ var index = $items.index(e.target)
+
+ if (e.which == 38 && index > 0) index-- // up
+ if (e.which == 40 && index < $items.length - 1) index++ // down
+ if (!~index) index = 0
- this.focus()
- this.setAttribute('aria-expanded', 'true')
+ $items.eq(index).trigger('focus')
+ }
- $(parent).toggleClass(Dropdown._ClassName.OPEN)
- $(parent).trigger(Dropdown._Event.SHOWN, relatedTarget)
+ // DROPDOWN PLUGIN DEFINITION
+ // ==========================
- return false
-}
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.dropdown')
+ if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
-/**
- * ------------------------------------------------------------------------
- * jQuery Interface + noConflict implementaiton
- * ------------------------------------------------------------------------
- */
+ var old = $.fn.dropdown
-/**
- * @const
- * @type {Function}
- */
-$.fn[Dropdown._NAME] = Dropdown._jQueryInterface
+ $.fn.dropdown = Plugin
+ $.fn.dropdown.Constructor = Dropdown
-/**
- * @const
- * @type {Function}
- */
-$.fn[Dropdown._NAME]['Constructor'] = Dropdown
+ // DROPDOWN NO CONFLICT
+ // ====================
+ $.fn.dropdown.noConflict = function () {
+ $.fn.dropdown = old
+ return this
+ }
-/**
- * @const
- * @type {Function}
- */
-$.fn[Dropdown._NAME]['noConflict'] = function () {
- $.fn[Dropdown._NAME] = Dropdown._JQUERY_NO_CONFLICT
- return this
-}
+ // APPLY TO STANDARD DROPDOWN ELEMENTS
+ // ===================================
-/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
- */
+ $(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', '.dropdown-menu', Dropdown.prototype.keydown)
-$(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)
+}(jQuery);
diff --git a/js/externs/bootstrap.js b/js/externs/bootstrap.js
deleted file mode 100644
index b4c2e36c28..0000000000
--- a/js/externs/bootstrap.js
+++ /dev/null
@@ -1,6 +0,0 @@
-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
deleted file mode 100644
index f9b80c497d..0000000000
--- a/js/externs/jQuery.js
+++ /dev/null
@@ -1,2159 +0,0 @@
-/*
- * 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/modal.js b/js/modal.js
index fb4cd03296..0426561af6 100644
--- a/js/modal.js
+++ b/js/modal.js
@@ -1,598 +1,337 @@
-/** =======================================================================
- * Bootstrap: modal.js v4.0.0
- * http://getbootstrap.com/javascript/#modal
+/* ========================================================================
+ * Bootstrap: modal.js v3.3.4
+ * http://getbootstrap.com/javascript/#modals
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ========================================================================
- * @fileoverview - Bootstrap's modal plugin. Modals are streamlined, but
- * flexible, dialog prompts with the minimum required functionality and
- * smart defaults.
- *
- * Public Methods & Properties:
- *
- * + $.modal
- * + $.modal.noConflict
- * + $.modal.Constructor
- * + $.modal.Constructor.VERSION
- * + $.modal.Constructor.Defaults
- * + $.modal.Constructor.Defaults.backdrop
- * + $.modal.Constructor.Defaults.keyboard
- * + $.modal.Constructor.Defaults.show
- * + $.modal.Constructor.prototype.toggle
- * + $.modal.Constructor.prototype.show
- * + $.modal.Constructor.prototype.hide
- *
- * ========================================================================
- */
-
-'use strict';
-
-
-/**
- * Our modal class.
- * @param {Element} element
- * @param {Object} config
- * @constructor
- */
-var Modal = function (element, config) {
-
- /** @private {Object} */
- this._config = config
-
- /** @private {Element} */
- this._element = element
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // MODAL CLASS DEFINITION
+ // ======================
+
+ var Modal = function (element, options) {
+ this.options = options
+ this.$body = $(document.body)
+ this.$element = $(element)
+ this.$dialog = this.$element.find('.modal-dialog')
+ this.$backdrop = null
+ this.isShown = null
+ this.originalBodyPad = null
+ this.scrollbarWidth = 0
+ this.ignoreBackdropClick = false
+
+ if (this.options.remote) {
+ this.$element
+ .find('.modal-content')
+ .load(this.options.remote, $.proxy(function () {
+ this.$element.trigger('loaded.bs.modal')
+ }, this))
+ }
+ }
- /** @private {Element} */
- this._backdrop = null
+ Modal.VERSION = '3.3.4'
- /** @private {boolean} */
- this._isShown = false
-
- /** @private {boolean} */
- this._isBodyOverflowing = false
-
- /** @private {number} */
- this._scrollbarWidth = 0
+ Modal.TRANSITION_DURATION = 300
+ Modal.BACKDROP_TRANSITION_DURATION = 150
-}
+ Modal.DEFAULTS = {
+ backdrop: true,
+ keyboard: true,
+ show: true
+ }
+ Modal.prototype.toggle = function (_relatedTarget) {
+ return this.isShown ? this.hide() : this.show(_relatedTarget)
+ }
-/**
- * @const
- * @type {string}
- */
-Modal['VERSION'] = '4.0.0'
-
-
-/**
- * @const
- * @type {Object}
- */
-Modal['Defaults'] = {
- 'backdrop' : true,
- 'keyboard' : true,
- 'show' : true
-}
-
-
-/**
- * @const
- * @type {string}
- * @private
- */
-Modal._NAME = 'modal'
-
-
-/**
- * @const
- * @type {string}
- * @private
- */
-Modal._DATA_KEY = 'bs.modal'
-
-
-/**
- * @const
- * @type {number}
- * @private
- */
-Modal._TRANSITION_DURATION = 300
-
-
-/**
- * @const
- * @type {number}
- * @private
- */
-Modal._BACKDROP_TRANSITION_DURATION = 150
-
-
-/**
- * @const
- * @type {Function}
- * @private
- */
-Modal._JQUERY_NO_CONFLICT = $.fn[Modal._NAME]
-
-
-/**
- * @const
- * @enum {string}
- * @private
- */
-Modal._Event = {
- HIDE : 'hide.bs.modal',
- HIDDEN : 'hidden.bs.modal',
- SHOW : 'show.bs.modal',
- SHOWN : 'shown.bs.modal'
-}
-
-
-/**
- * @const
- * @enum {string}
- * @private
- */
-Modal._ClassName = {
- BACKDROP : 'modal-backdrop',
- OPEN : 'modal-open',
- FADE : 'fade',
- IN : 'in'
-}
-
-
-/**
- * @const
- * @enum {string}
- * @private
- */
-Modal._Selector = {
- DIALOG : '.modal-dialog',
- DATA_TOGGLE : '[data-toggle="modal"]',
- DATA_DISMISS : '[data-dismiss="modal"]',
- SCROLLBAR_MEASURER : 'modal-scrollbar-measure'
-}
-
-
-
-/**
- * Provides the jQuery Interface for the alert component.
- * @param {Object|string=} opt_config
- * @param {Element=} opt_relatedTarget
- * @this {jQuery}
- * @return {jQuery}
- * @private
- */
-Modal._jQueryInterface = function Plugin(opt_config, opt_relatedTarget) {
- return this.each(function () {
- var data = $(this).data(Modal._DATA_KEY)
- var config = $.extend({}, Modal['Defaults'], $(this).data(), typeof opt_config == 'object' && opt_config)
-
- if (!data) {
- data = new Modal(this, config)
- $(this).data(Modal._DATA_KEY, data)
- }
+ Modal.prototype.show = function (_relatedTarget) {
+ var that = this
+ var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
- if (typeof opt_config == 'string') {
- data[opt_config](opt_relatedTarget)
+ this.$element.trigger(e)
- } else if (config['show']) {
- data['show'](opt_relatedTarget)
- }
- })
-}
+ if (this.isShown || e.isDefaultPrevented()) return
+ this.isShown = true
-/**
- * @param {Element} relatedTarget
- */
-Modal.prototype['toggle'] = function (relatedTarget) {
- return this._isShown ? this['hide']() : this['show'](relatedTarget)
-}
+ this.checkScrollbar()
+ this.setScrollbar()
+ this.$body.addClass('modal-open')
+ this.escape()
+ this.resize()
-/**
- * @param {Element} relatedTarget
- */
-Modal.prototype['show'] = function (relatedTarget) {
- var showEvent = $.Event(Modal._Event.SHOW, { relatedTarget: relatedTarget })
+ this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
- $(this._element).trigger(showEvent)
+ this.$dialog.on('mousedown.dismiss.bs.modal', function () {
+ that.$element.one('mouseup.dismiss.bs.modal', function (e) {
+ if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
+ })
+ })
- if (this._isShown || showEvent.isDefaultPrevented()) {
- return
- }
+ this.backdrop(function () {
+ var transition = $.support.transition && that.$element.hasClass('fade')
- this._isShown = true
+ if (!that.$element.parent().length) {
+ that.$element.appendTo(that.$body) // don't move modals dom position
+ }
- this._checkScrollbar()
- this._setScrollbar()
+ that.$element
+ .show()
+ .scrollTop(0)
- $(document.body).addClass(Modal._ClassName.OPEN)
+ that.adjustDialog()
- this._escape()
- this._resize()
+ if (transition) {
+ that.$element[0].offsetWidth // force reflow
+ }
- $(this._element).on('click.dismiss.bs.modal', Modal._Selector.DATA_DISMISS, this['hide'].bind(this))
+ that.$element.addClass('in')
- this._showBackdrop(this._showElement.bind(this, relatedTarget))
-}
+ that.enforceFocus()
+ var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
-/**
- * @param {Event} event
- */
-Modal.prototype['hide'] = function (event) {
- if (event) {
- event.preventDefault()
+ transition ?
+ that.$dialog // wait for modal to slide in
+ .one('bsTransitionEnd', function () {
+ that.$element.trigger('focus').trigger(e)
+ })
+ .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
+ that.$element.trigger('focus').trigger(e)
+ })
}
- var hideEvent = $.Event(Modal._Event.HIDE)
-
- $(this._element).trigger(hideEvent)
-
- if (!this._isShown || hideEvent.isDefaultPrevented()) {
- return
- }
+ Modal.prototype.hide = function (e) {
+ if (e) e.preventDefault()
- this._isShown = false
+ e = $.Event('hide.bs.modal')
- this._escape()
- this._resize()
+ this.$element.trigger(e)
- $(document).off('focusin.bs.modal')
+ if (!this.isShown || e.isDefaultPrevented()) return
- $(this._element).removeClass(Modal._ClassName.IN)
- this._element.setAttribute('aria-hidden', true)
+ this.isShown = false
- $(this._element).off('click.dismiss.bs.modal')
+ this.escape()
+ this.resize()
- if (Bootstrap.transition && $(this._element).hasClass(Modal._ClassName.FADE)) {
- $(this._element)
- .one(Bootstrap.TRANSITION_END, this._hideModal.bind(this))
- .emulateTransitionEnd(Modal._TRANSITION_DURATION)
- } else {
- this._hideModal()
- }
-}
+ $(document).off('focusin.bs.modal')
+ this.$element
+ .removeClass('in')
+ .off('click.dismiss.bs.modal')
+ .off('mouseup.dismiss.bs.modal')
-/**
- * @param {Element} relatedTarget
- * @private
- */
-Modal.prototype._showElement = function (relatedTarget) {
- var transition = Bootstrap.transition && $(this._element).hasClass(Modal._ClassName.FADE)
+ this.$dialog.off('mousedown.dismiss.bs.modal')
- if (!this._element.parentNode || this._element.parentNode.nodeType != Node.ELEMENT_NODE) {
- document.body.appendChild(this._element) // don't move modals dom position
+ $.support.transition && this.$element.hasClass('fade') ?
+ this.$element
+ .one('bsTransitionEnd', $.proxy(this.hideModal, this))
+ .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
+ this.hideModal()
}
- this._element.style.display = 'block'
- this._element.scrollTop = 0
-
- if (this._config['backdrop']) {
- this._adjustBackdrop()
+ Modal.prototype.enforceFocus = function () {
+ $(document)
+ .off('focusin.bs.modal') // guard against infinite focus loop
+ .on('focusin.bs.modal', $.proxy(function (e) {
+ if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
+ this.$element.trigger('focus')
+ }
+ }, this))
}
- if (transition) {
- Bootstrap.reflow(this._element)
+ Modal.prototype.escape = function () {
+ if (this.isShown && this.options.keyboard) {
+ this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
+ e.which == 27 && this.hide()
+ }, this))
+ } else if (!this.isShown) {
+ this.$element.off('keydown.dismiss.bs.modal')
+ }
}
- $(this._element).addClass(Modal._ClassName.IN)
- this._element.setAttribute('aria-hidden', false)
-
- this._enforceFocus()
-
- var shownEvent = $.Event(Modal._Event.SHOWN, { relatedTarget: relatedTarget })
-
- var transitionComplete = function () {
- this._element.focus()
- $(this._element).trigger(shownEvent)
- }.bind(this)
-
- if (transition) {
- var dialog = $(this._element).find(Modal._Selector.DIALOG)[0]
- $(dialog)
- .one(Bootstrap.TRANSITION_END, transitionComplete)
- .emulateTransitionEnd(Modal._TRANSITION_DURATION)
- } else {
- transitionComplete()
+ Modal.prototype.resize = function () {
+ if (this.isShown) {
+ $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
+ } else {
+ $(window).off('resize.bs.modal')
+ }
}
-}
-
-
-/**
- * @private
- */
-Modal.prototype._enforceFocus = function () {
- $(document)
- .off('focusin.bs.modal') // guard against infinite focus loop
- .on('focusin.bs.modal', function (e) {
- if (this._element !== e.target && !$(this._element).has(e.target).length) {
- this._element.focus()
- }
- }.bind(this))
-}
-
-
-/**
- * @private
- */
-Modal.prototype._escape = function () {
- if (this._isShown && this._config['keyboard']) {
- $(this._element).on('keydown.dismiss.bs.modal', function (event) {
- if (event.which === 27) {
- this['hide']()
- }
- }.bind(this))
-
- } else if (!this._isShown) {
- $(this._element).off('keydown.dismiss.bs.modal')
+ Modal.prototype.hideModal = function () {
+ var that = this
+ this.$element.hide()
+ this.backdrop(function () {
+ that.$body.removeClass('modal-open')
+ that.resetAdjustments()
+ that.resetScrollbar()
+ that.$element.trigger('hidden.bs.modal')
+ })
}
-}
-
-/**
- * @private
- */
-Modal.prototype._resize = function () {
- if (this._isShown) {
- $(window).on('resize.bs.modal', this._handleUpdate.bind(this))
- } else {
- $(window).off('resize.bs.modal')
+ Modal.prototype.removeBackdrop = function () {
+ this.$backdrop && this.$backdrop.remove()
+ this.$backdrop = null
}
-}
-
-
-/**
- * @private
- */
-Modal.prototype._hideModal = function () {
- this._element.style.display = 'none'
- this._showBackdrop(function () {
- $(document.body).removeClass(Modal._ClassName.OPEN)
- this._resetAdjustments()
- this._resetScrollbar()
- $(this._element).trigger(Modal._Event.HIDDEN)
- }.bind(this))
-}
-
-
-/**
- * @private
- */
-Modal.prototype._removeBackdrop = function () {
- if (this._backdrop) {
- this._backdrop.parentNode.removeChild(this._backdrop)
- this._backdrop = null
- }
-}
+ Modal.prototype.backdrop = function (callback) {
+ var that = this
+ var animate = this.$element.hasClass('fade') ? 'fade' : ''
-/**
- * @param {Function} callback
- * @private
- */
-Modal.prototype._showBackdrop = function (callback) {
- var animate = $(this._element).hasClass(Modal._ClassName.FADE) ? Modal._ClassName.FADE : ''
+ if (this.isShown && this.options.backdrop) {
+ var doAnimate = $.support.transition && animate
- if (this._isShown && this._config['backdrop']) {
- var doAnimate = Bootstrap.transition && animate
+ this.$backdrop = $(document.createElement('div'))
+ .addClass('modal-backdrop ' + animate)
+ .appendTo(this.$body)
- this._backdrop = document.createElement('div')
- this._backdrop.className = Modal._ClassName.BACKDROP
+ this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
+ if (this.ignoreBackdropClick) {
+ this.ignoreBackdropClick = false
+ return
+ }
+ if (e.target !== e.currentTarget) return
+ this.options.backdrop == 'static'
+ ? this.$element[0].focus()
+ : this.hide()
+ }, this))
- if (animate) {
- $(this._backdrop).addClass(animate)
- }
+ if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
- $(this._element).prepend(this._backdrop)
+ this.$backdrop.addClass('in')
- $(this._backdrop).on('click.dismiss.bs.modal', function (event) {
- if (event.target !== event.currentTarget) return
- this._config['backdrop'] === 'static'
- ? this._element.focus()
- : this['hide']()
- }.bind(this))
+ if (!callback) return
- if (doAnimate) {
- Bootstrap.reflow(this._backdrop)
- }
+ doAnimate ?
+ this.$backdrop
+ .one('bsTransitionEnd', callback)
+ .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
+ callback()
- $(this._backdrop).addClass(Modal._ClassName.IN)
+ } else if (!this.isShown && this.$backdrop) {
+ this.$backdrop.removeClass('in')
- if (!callback) {
- return
- }
+ var callbackRemove = function () {
+ that.removeBackdrop()
+ callback && callback()
+ }
+ $.support.transition && this.$element.hasClass('fade') ?
+ this.$backdrop
+ .one('bsTransitionEnd', callbackRemove)
+ .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
+ callbackRemove()
- if (!doAnimate) {
+ } else if (callback) {
callback()
- return
}
+ }
- $(this._backdrop)
- .one(Bootstrap.TRANSITION_END, callback)
- .emulateTransitionEnd(Modal._BACKDROP_TRANSITION_DURATION)
+ // these following methods are used to handle overflowing modals
- } else if (!this._isShown && this._backdrop) {
- $(this._backdrop).removeClass(Modal._ClassName.IN)
+ Modal.prototype.handleUpdate = function () {
+ this.adjustDialog()
+ }
- var callbackRemove = function () {
- this._removeBackdrop()
- if (callback) {
- callback()
- }
- }.bind(this)
+ Modal.prototype.adjustDialog = function () {
+ var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
- if (Bootstrap.transition && $(this._element).hasClass(Modal._ClassName.FADE)) {
- $(this._backdrop)
- .one(Bootstrap.TRANSITION_END, callbackRemove)
- .emulateTransitionEnd(Modal._BACKDROP_TRANSITION_DURATION)
- } else {
- callbackRemove()
+ this.$element.css({
+ paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
+ paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
+ })
+ }
+
+ Modal.prototype.resetAdjustments = function () {
+ this.$element.css({
+ paddingLeft: '',
+ paddingRight: ''
+ })
+ }
+
+ Modal.prototype.checkScrollbar = function () {
+ var fullWindowWidth = window.innerWidth
+ if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
+ var documentElementRect = document.documentElement.getBoundingClientRect()
+ fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)
}
+ this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth
+ this.scrollbarWidth = this.measureScrollbar()
+ }
- } else if (callback) {
- callback()
+ Modal.prototype.setScrollbar = function () {
+ var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
+ this.originalBodyPad = document.body.style.paddingRight || ''
+ if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
}
-}
-
-
-/**
- * ------------------------------------------------------------------------
- * the following methods are used to handle overflowing modals
- * todo (fat): these should probably be refactored into a
- * ------------------------------------------------------------------------
- */
-
-
-/**
- * @private
- */
-Modal.prototype._handleUpdate = function () {
- if (this._config['backdrop']) this._adjustBackdrop()
- this._adjustDialog()
-}
-
-/**
- * @private
- */
-Modal.prototype._adjustBackdrop = function () {
- this._backdrop.style.height = 0 // todo (fat): no clue why we do this
- this._backdrop.style.height = this._element.scrollHeight + 'px'
-}
-
-
-/**
- * @private
- */
-Modal.prototype._adjustDialog = function () {
- var isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight
-
- if (!this._isBodyOverflowing && isModalOverflowing) {
- this._element.style.paddingLeft = this._scrollbarWidth + 'px'
+
+ Modal.prototype.resetScrollbar = function () {
+ this.$body.css('padding-right', this.originalBodyPad)
}
- if (this._isBodyOverflowing && !isModalOverflowing) {
- this._element.style.paddingRight = this._scrollbarWidth + 'px'
+ Modal.prototype.measureScrollbar = function () { // thx walsh
+ var scrollDiv = document.createElement('div')
+ scrollDiv.className = 'modal-scrollbar-measure'
+ this.$body.append(scrollDiv)
+ var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
+ this.$body[0].removeChild(scrollDiv)
+ return scrollbarWidth
}
-}
-/**
- * @private
- */
-Modal.prototype._resetAdjustments = function () {
- this._element.style.paddingLeft = ''
- this._element.style.paddingRight = ''
-}
+ // MODAL PLUGIN DEFINITION
+ // =======================
+ function Plugin(option, _relatedTarget) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.modal')
+ var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
-/**
- * @private
- */
-Modal.prototype._checkScrollbar = function () {
- this._isBodyOverflowing = document.body.scrollHeight > document.documentElement.clientHeight
- this._scrollbarWidth = this._getScrollbarWidth()
-}
+ if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
+ if (typeof option == 'string') data[option](_relatedTarget)
+ else if (options.show) data.show(_relatedTarget)
+ })
+ }
+ var old = $.fn.modal
-/**
- * @private
- */
-Modal.prototype._setScrollbar = function () {
- var bodyPadding = parseInt(($(document.body).css('padding-right') || 0), 10)
+ $.fn.modal = Plugin
+ $.fn.modal.Constructor = Modal
- if (this._isBodyOverflowing) {
- document.body.style.paddingRight = bodyPadding + this._scrollbarWidth + 'px'
- }
-}
-
-
-/**
- * @private
- */
-Modal.prototype._resetScrollbar = function () {
- document.body.style.paddingRight = ''
-}
-
-
-/**
- * @private
- */
-Modal.prototype._getScrollbarWidth = function () { // thx walsh
- var scrollDiv = document.createElement('div')
- scrollDiv.className = Modal._Selector.SCROLLBAR_MEASURER
- document.body.appendChild(scrollDiv)
- var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
- document.body.removeChild(scrollDiv)
- return scrollbarWidth
-}
-
-
-/**
- * ------------------------------------------------------------------------
- * jQuery Interface + noConflict implementaiton
- * ------------------------------------------------------------------------
- */
-
-/**
- * @const
- * @type {Function}
- */
-$.fn[Modal._NAME] = Modal._jQueryInterface
-
-
-/**
- * @const
- * @type {Function}
- */
-$.fn[Modal._NAME]['Constructor'] = Modal
-
-
-/**
- * @const
- * @type {Function}
- */
-$.fn[Modal._NAME]['noConflict'] = function () {
- $.fn[Modal._NAME] = Modal._JQUERY_NO_CONFLICT
- return this
-}
-
-
-/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
- */
-
-$(document).on('click.bs.modal.data-api', Modal._Selector.DATA_TOGGLE, function (event) {
- var selector = Bootstrap.getSelectorFromElement(this)
-
- if (selector) {
- var target = $(selector)[0]
- }
- var config = $(target).data(Modal._DATA_KEY) ? 'toggle' : $.extend({}, $(target).data(), $(this).data())
+ // MODAL NO CONFLICT
+ // =================
- if (this.tagName == 'A') {
- event.preventDefault()
+ $.fn.modal.noConflict = function () {
+ $.fn.modal = old
+ return this
}
- var $target = $(target).one(Modal._Event.SHOW, function (showEvent) {
- if (showEvent.isDefaultPrevented()) {
- return // only register focus restorer if modal will actually get shown
- }
- $target.one(Modal._Event.HIDDEN, function () {
- if ($(this).is(':visible')) {
- this.focus()
- }
- }.bind(this))
- }.bind(this))
+ // MODAL DATA-API
+ // ==============
+
+ $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
+ var $this = $(this)
+ var href = $this.attr('href')
+ var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
+ var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
+
+ if ($this.is('a')) e.preventDefault()
+
+ $target.one('show.bs.modal', function (showEvent) {
+ if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
+ $target.one('hidden.bs.modal', function () {
+ $this.is(':visible') && $this.trigger('focus')
+ })
+ })
+ Plugin.call($target, option, this)
+ })
- Modal._jQueryInterface.call($(target), config, this)
-})
+}(jQuery);
diff --git a/js/popover.js b/js/popover.js
index 5663355516..0b0755ee27 100644
--- a/js/popover.js
+++ b/js/popover.js
@@ -1,283 +1,108 @@
-/** =======================================================================
- * Bootstrap: popover.js v4.0.0
+/* ========================================================================
+ * Bootstrap: popover.js v3.3.4
* 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
- *
- * ========================================================================
- */
-
-
-'use strict';
-
-
-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'
-}
++function ($) {
+ 'use strict';
-/**
- * @const
- * @enum {string}
- * @private
- */
-Popover._ClassName = {
- FADE : 'fade',
- IN : 'in'
-}
+ // POPOVER PUBLIC CLASS DEFINITION
+ // ===============================
+ var Popover = function (element, options) {
+ this.init('popover', element, options)
+ }
-/**
- * @const
- * @enum {string}
- * @private
- */
-Popover._Selector = {
- TITLE : '.popover-title',
- CONTENT : '.popover-content',
- ARROW : '.popover-arrow'
-}
-
-
-/**
- * @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 (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
- if (!data) {
- data = new Popover(this, config)
- $(this).data(Popover._DATA_KEY, data)
- }
+ Popover.VERSION = '3.3.4'
- if (typeof opt_config === 'string') {
- data[opt_config]()
- }
+ Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
+ placement: 'right',
+ trigger: 'click',
+ content: '',
+ template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
})
-}
-
-
-/**
- * @return {string}
- * @protected
- */
-Popover.prototype.getName = function () {
- return Popover._NAME
-}
-/**
- * @override
- */
-Popover.prototype.getDataKey = function () {
- return Popover._DATA_KEY
-}
+ // NOTE: POPOVER EXTENDS tooltip.js
+ // ================================
+ Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
-/**
- * @override
- */
-Popover.prototype.getEventObject = function () {
- return Popover._Event
-}
+ Popover.prototype.constructor = Popover
+ Popover.prototype.getDefaults = function () {
+ return Popover.DEFAULTS
+ }
-/**
- * @override
- */
-Popover.prototype.getArrowElement = function () {
- return (this.arrow = this.arrow || $(this.getTipElement()).find(Popover._Selector.ARROW)[0])
-}
+ Popover.prototype.setContent = function () {
+ var $tip = this.tip()
+ var title = this.getTitle()
+ var content = this.getContent()
+ $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)
-/**
- * @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]
+ $tip.removeClass('fade top bottom left right in')
- if (titleElement) {
- titleElement[this.config['html'] ? 'innerHTML' : 'innerText'] = title
+ // 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()
}
- // 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)
-
- $(tip)
- .removeClass(Popover._ClassName.FADE)
- .removeClass(Popover._ClassName.IN)
-
- for (var direction in Tooltip.Direction) {
- $(tip).removeClass(Popover._NAME + '-' + Tooltip.Direction[direction])
+ Popover.prototype.hasContent = function () {
+ return this.getTitle() || this.getContent()
}
-}
-
-/**
- * @override
- */
-Popover.prototype.isWithContent = function () {
- return this.getTitle() || this._getContent()
-}
+ Popover.prototype.getContent = function () {
+ var $e = this.$element
+ var o = this.options
+ return $e.attr('data-content')
+ || (typeof o.content == 'function' ?
+ o.content.call($e[0]) :
+ o.content)
+ }
-/**
- * @override
- */
-Popover.prototype.getTipElement = function () {
- return (this.tip = this.tip || $(this.config['template'])[0])
-}
+ Popover.prototype.arrow = function () {
+ return (this.$arrow = this.$arrow || this.tip().find('.arrow'))
+ }
-/**
- * @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'])
-}
+ // 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
+ if (!data && /destroy|hide/.test(option)) return
+ if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
-/**
- * ------------------------------------------------------------------------
- * jQuery Interface + noConflict implementaiton
- * ------------------------------------------------------------------------
- */
+ var old = $.fn.popover
-/**
- * @const
- * @type {Function}
- */
-$.fn[Popover._NAME] = Popover._jQueryInterface
+ $.fn.popover = Plugin
+ $.fn.popover.Constructor = Popover
-/**
- * @const
- * @type {Function}
- */
-$.fn[Popover._NAME]['Constructor'] = Popover
+ // POPOVER NO CONFLICT
+ // ===================
+ $.fn.popover.noConflict = function () {
+ $.fn.popover = old
+ return this
+ }
-/**
- * @const
- * @type {Function}
- */
-$.fn[Popover._NAME]['noConflict'] = function () {
- $.fn[Popover._NAME] = Popover._JQUERY_NO_CONFLICT
- return this
-}
+}(jQuery);
diff --git a/js/scrollspy.js b/js/scrollspy.js
index 150241b540..f860c1abad 100644
--- a/js/scrollspy.js
+++ b/js/scrollspy.js
@@ -1,346 +1,172 @@
-/** =======================================================================
- * Bootstrap: scrollspy.js v4.0.0
+/* ========================================================================
+ * Bootstrap: scrollspy.js v3.3.4
* 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';
-
-
-/**
- * Our scrollspy class.
- * @param {Element!} element
- * @param {Object=} opt_config
- * @constructor
- */
-function ScrollSpy(element, opt_config) {
-
- /** @private {Element|Window} */
- this._scrollElement = element.tagName == 'BODY' ? window : element
-
- /** @private {Object} */
- this._config = $.extend({}, ScrollSpy['Defaults'], opt_config)
-
- /** @private {string} */
- this._selector = (this._config.target || '') + ' .nav li > a'
-
- /** @private {Array} */
- this._offsets = []
-
- /** @private {Array} */
- this._targets = []
-
- /** @private {Element} */
- this._activeTarget = null
-
- /** @private {number} */
- this._scrollHeight = 0
-
- $(this._scrollElement).on('scroll.bs.scrollspy', this._process.bind(this))
-
- this['refresh']()
+ * ======================================================================== */
- this._process()
-}
++function ($) {
+ 'use strict';
-/**
- * @const
- * @type {string}
- */
-ScrollSpy['VERSION'] = '4.0.0'
+ // SCROLLSPY CLASS DEFINITION
+ // ==========================
+ function ScrollSpy(element, options) {
+ this.$body = $(document.body)
+ this.$scrollElement = $(element).is(document.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
-/**
- * @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)
- }
-
- if (typeof opt_config === 'string') {
- data[opt_config]()
- }
- })
-}
-
+ this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this))
+ this.refresh()
+ this.process()
+ }
-/**
- * Refresh the scrollspy target cache
- */
-ScrollSpy.prototype['refresh'] = function () {
- var offsetMethod = 'offset'
- var offsetBase = 0
+ ScrollSpy.VERSION = '3.3.4'
- if (this._scrollElement !== this._scrollElement.window) {
- offsetMethod = 'position'
- offsetBase = this._getScrollTop()
+ ScrollSpy.DEFAULTS = {
+ offset: 10
}
- this._offsets = []
- this._targets = []
-
- this._scrollHeight = this._getScrollHeight()
+ ScrollSpy.prototype.getScrollHeight = function () {
+ return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
+ }
- var targets = /** @type {Array.<Element>} */ ($.makeArray($(this._selector)))
+ ScrollSpy.prototype.refresh = function () {
+ var that = this
+ var offsetMethod = 'offset'
+ var offsetBase = 0
- targets
- .map(function (element, index) {
- var target
- var targetSelector = Bootstrap.getSelectorFromElement(element)
+ this.offsets = []
+ this.targets = []
+ this.scrollHeight = this.getScrollHeight()
- if (targetSelector) {
- target = $(targetSelector)[0]
- }
+ if (!$.isWindow(this.$scrollElement[0])) {
+ offsetMethod = 'position'
+ offsetBase = this.$scrollElement.scrollTop()
+ }
- 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']()
+ 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 () {
+ that.offsets.push(this[0])
+ that.targets.push(this[1])
+ })
}
- if (scrollTop >= maxScroll) {
- var target = this._targets[this._targets.length - 1]
-
- if (this._activeTarget != target) {
- this._activate(target)
+ 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()
}
- }
- if (this._activeTarget && scrollTop < this._offsets[0]) {
- this._activeTarget = null
- this._clear()
- return
- }
+ if (scrollTop >= maxScroll) {
+ return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
+ }
- 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 (activeTarget && scrollTop < offsets[0]) {
+ this.activeTarget = null
+ return this.clear()
+ }
- if (isActiveTarget) {
- this._activate(this._targets[i])
+ for (i = offsets.length; i--;) {
+ activeTarget != targets[i]
+ && scrollTop >= offsets[i]
+ && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1])
+ && this.activate(targets[i])
}
}
-}
-
-/**
- * @param {Element} target
- * @private
- */
-ScrollSpy.prototype._activate = function (target) {
- this._activeTarget = target
+ ScrollSpy.prototype.activate = function (target) {
+ this.activeTarget = target
- this._clear()
+ this.clear()
- var selector = this._selector
- + '[data-target="' + target + '"],'
- + this._selector + '[href="' + target + '"]'
+ 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)
+ var active = $(selector)
+ .parents('li')
+ .addClass('active')
- for (var i = parentListItems.length; i--;) {
- $(parentListItems[i]).addClass(ScrollSpy._ClassName.ACTIVE)
+ if (active.parent('.dropdown-menu').length) {
+ active = active
+ .closest('li.dropdown')
+ .addClass('active')
+ }
- var itemParent = parentListItems[i].parentNode
+ active.trigger('activate.bs.scrollspy')
+ }
- if (itemParent && $(itemParent).hasClass(ScrollSpy._ClassName.DROPDOWN_MENU)) {
- var closestDropdown = $(itemParent).closest(ScrollSpy._Selector.LI_DROPDOWN)[0]
- $(closestDropdown).addClass(ScrollSpy._ClassName.ACTIVE)
- }
+ ScrollSpy.prototype.clear = function () {
+ $(this.selector)
+ .parentsUntil(this.options.target, '.active')
+ .removeClass('active')
}
- $(this._scrollElement).trigger(ScrollSpy._Event.ACTIVATE, {
- relatedTarget: target
- })
-}
+ // SCROLLSPY PLUGIN DEFINITION
+ // ===========================
-/**
- * @private
- */
-ScrollSpy.prototype._clear = function () {
- var activeParents = $(this._selector).parentsUntil(this._config.target, ScrollSpy._Selector.ACTIVE)
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.scrollspy')
+ var options = typeof option == 'object' && option
- for (var i = activeParents.length; i--;) {
- $(activeParents[i]).removeClass(ScrollSpy._ClassName.ACTIVE)
+ if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
}
-}
+ var old = $.fn.scrollspy
-/**
- * ------------------------------------------------------------------------
- * jQuery Interface + noConflict implementaiton
- * ------------------------------------------------------------------------
- */
+ $.fn.scrollspy = Plugin
+ $.fn.scrollspy.Constructor = ScrollSpy
-/**
- * @const
- * @type {Function}
- */
-$.fn[ScrollSpy._NAME] = ScrollSpy._jQueryInterface
+ // SCROLLSPY NO CONFLICT
+ // =====================
-/**
- * @const
- * @type {Function}
- */
-$.fn[ScrollSpy._NAME]['Constructor'] = ScrollSpy
-
-
-/**
- * @const
- * @type {Function}
- */
-$.fn[ScrollSpy._NAME]['noConflict'] = function () {
- $.fn[ScrollSpy._NAME] = ScrollSpy._JQUERY_NO_CONFLICT
- return this
-}
+ $.fn.scrollspy.noConflict = function () {
+ $.fn.scrollspy = old
+ return this
+ }
-/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
- */
+ // SCROLLSPY DATA-API
+ // ==================
-$(window).on('load.bs.scrollspy.data-api', function () {
- var scrollSpys = /** @type {Array.<Element>} */ ($.makeArray($(ScrollSpy._Selector.DATA_SPY)))
+ $(window).on('load.bs.scrollspy.data-api', function () {
+ $('[data-spy="scroll"]').each(function () {
+ var $spy = $(this)
+ Plugin.call($spy, $spy.data())
+ })
+ })
- for (var i = scrollSpys.length; i--;) {
- var $spy = $(scrollSpys[i])
- ScrollSpy._jQueryInterface.call($spy, /** @type {Object|null} */ ($spy.data()))
- }
-})
+}(jQuery);
diff --git a/js/tab.js b/js/tab.js
index 07897294a3..416189e3d1 100644
--- a/js/tab.js
+++ b/js/tab.js
@@ -1,324 +1,155 @@
-/** =======================================================================
- * Bootstrap: tab.js v4.0.0
+/* ========================================================================
+ * Bootstrap: tab.js v3.3.4
* 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
- }
- var ulElement = $(this._element).closest(Tab._Selector.UL)[0]
- var selector = Bootstrap.getSelectorFromElement(this._element)
++function ($) {
+ 'use strict';
- if (ulElement) {
- var previous = /** @type {Array.<Element>} */ ($.makeArray($(ulElement).find(Tab._Selector.ACTIVE)))
- previous = previous[previous.length - 1]
+ // TAB CLASS DEFINITION
+ // ====================
- if (previous) {
- previous = $(previous).find('a')[0]
- }
+ var Tab = function (element) {
+ // jscs:disable requireDollarBeforejQueryAssignment
+ this.element = $(element)
+ // jscs:enable requireDollarBeforejQueryAssignment
}
- var hideEvent = $.Event(Tab._Event.HIDE, {
- relatedTarget: this._element
- })
-
- var showEvent = $.Event(Tab._Event.SHOW, {
- relatedTarget: previous
- })
+ Tab.VERSION = '3.3.4'
- if (previous) {
- $(previous).trigger(hideEvent)
- }
-
- $(this._element).trigger(showEvent)
+ Tab.TRANSITION_DURATION = 150
- if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
+ Tab.prototype.show = function () {
+ var $this = this.element
+ var $ul = $this.closest('ul:not(.dropdown-menu)')
+ var selector = $this.data('target')
- if (selector) {
- var target = $(selector)[0]
- }
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+ }
- this._activate($(this._element).closest(Tab._Selector.LI)[0], ulElement)
+ if ($this.parent('li').hasClass('active')) return
- var complete = function () {
- var hiddenEvent = $.Event(Tab._Event.HIDDEN, {
- relatedTarget: this._element
+ var $previous = $ul.find('.active:last a')
+ var hideEvent = $.Event('hide.bs.tab', {
+ relatedTarget: $this[0]
})
-
- var shownEvent = $.Event(Tab._Event.SHOWN, {
- relatedTarget: previous
+ var showEvent = $.Event('show.bs.tab', {
+ relatedTarget: $previous[0]
})
- $(previous).trigger(hiddenEvent)
- $(this._element).trigger(shownEvent)
- }.bind(this)
+ $previous.trigger(hideEvent)
+ $this.trigger(showEvent)
- 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()
- }
+ if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
- if (active) {
- $(active).removeClass(Tab._ClassName.IN)
+ var $target = $(selector)
+
+ 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]
+ })
+ })
}
-}
-
-
-/**
- * @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)
- }
- var activeToggle = $(active).find(Tab._Selector.DATA_TOGGLE)[0]
- if (activeToggle) {
- activeToggle.setAttribute('aria-expanded', false)
+ 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').length) {
+ element
+ .closest('li.dropdown')
+ .addClass('active')
+ .end()
+ .find('[data-toggle="tab"]')
+ .attr('aria-expanded', true)
+ }
+
+ callback && callback()
}
- }
- $(element).addClass(Tab._ClassName.ACTIVE)
+ $active.length && transition ?
+ $active
+ .one('bsTransitionEnd', next)
+ .emulateTransitionEnd(Tab.TRANSITION_DURATION) :
+ next()
- var elementToggle = $(element).find(Tab._Selector.DATA_TOGGLE)[0]
- if (elementToggle) {
- elementToggle.setAttribute('aria-expanded', true)
+ $active.removeClass('in')
}
- if (isTransitioning) {
- Bootstrap.reflow(element)
- $(element).addClass(Tab._ClassName.IN)
- } else {
- $(element).removeClass(Tab._ClassName.FADE)
- }
- 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)
- }
+ // TAB PLUGIN DEFINITION
+ // =====================
- elementToggle = $(element).find(Tab._Selector.DATA_TOGGLE)[0]
- if (elementToggle) {
- elementToggle.setAttribute('aria-expanded', true)
- }
- }
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.tab')
- if (opt_callback) {
- opt_callback()
+ if (!data) $this.data('bs.tab', (data = new Tab(this)))
+ if (typeof option == 'string') data[option]()
+ })
}
-}
-
-/**
- * ------------------------------------------------------------------------
- * jQuery Interface + noConflict implementaiton
- * ------------------------------------------------------------------------
- */
+ var old = $.fn.tab
-/**
- * @const
- * @type {Function}
- */
-$.fn[Tab._NAME] = Tab._jQueryInterface
+ $.fn.tab = Plugin
+ $.fn.tab.Constructor = Tab
-/**
- * @const
- * @type {Function}
- */
-$.fn[Tab._NAME]['Constructor'] = Tab
+ // TAB NO CONFLICT
+ // ===============
-
-/**
- * @const
- * @type {Function}
- */
-$.fn[Tab._NAME]['noConflict'] = function () {
- $.fn[Tab._NAME] = Tab._JQUERY_NO_CONFLICT
- return this
-}
+ $.fn.tab.noConflict = function () {
+ $.fn.tab = old
+ return this
+ }
+ // TAB DATA-API
+ // ============
-// TAB DATA-API
-// ============
+ var clickHandler = function (e) {
+ e.preventDefault()
+ Plugin.call($(this), 'show')
+ }
-var clickHandler = function (e) {
- e.preventDefault()
- Tab._jQueryInterface.call($(this), 'show')
-}
+ $(document)
+ .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
+ .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
-$(document)
- .on('click.bs.tab.data-api', Tab._Selector.DATA_TOGGLE, clickHandler)
+}(jQuery);
diff --git a/js/tests/closure.html b/js/tests/closure.html
deleted file mode 100644
index 82c65f62b8..0000000000
--- a/js/tests/closure.html
+++ /dev/null
@@ -1,83 +0,0 @@
-<!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 d1ec0a7f40..fab2ebc163 100644
--- a/js/tests/index.html
+++ b/js/tests/index.html
@@ -130,7 +130,7 @@
</script>
<!-- Plugin sources -->
- <script src="../../js/util.js"></script>
+ <script>$.support.transition = false</script>
<script src="../../js/alert.js"></script>
<script src="../../js/button.js"></script>
<script src="../../js/carousel.js"></script>
@@ -141,6 +141,7 @@
<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>
@@ -153,6 +154,7 @@
<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/affix.js b/js/tests/unit/affix.js
new file mode 100644
index 0000000000..3a6918f866
--- /dev/null
+++ b/js/tests/unit/affix.js
@@ -0,0 +1,107 @@
+$(function () {
+ 'use strict';
+
+ QUnit.module('affix plugin')
+
+ QUnit.test('should be defined on jquery object', function (assert) {
+ assert.expect(1)
+ assert.ok($(document.body).affix, 'affix method is defined')
+ })
+
+ QUnit.module('affix', {
+ beforeEach: function () {
+ // Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
+ $.fn.bootstrapAffix = $.fn.affix.noConflict()
+ },
+ afterEach: function () {
+ $.fn.affix = $.fn.bootstrapAffix
+ delete $.fn.bootstrapAffix
+ }
+ })
+
+ QUnit.test('should provide no conflict', function (assert) {
+ assert.expect(1)
+ assert.strictEqual($.fn.affix, undefined, 'affix was set back to undefined (org value)')
+ })
+
+ QUnit.test('should return jquery collection containing the element', function (assert) {
+ assert.expect(2)
+ var $el = $('<div/>')
+ var $affix = $el.bootstrapAffix()
+ assert.ok($affix instanceof $, 'returns jquery collection')
+ assert.strictEqual($affix[0], $el[0], 'collection contains element')
+ })
+
+ QUnit.test('should exit early if element is not visible', function (assert) {
+ assert.expect(1)
+ var $affix = $('<div style="display: none"/>').bootstrapAffix()
+ $affix.data('bs.affix').checkPosition()
+ assert.ok(!$affix.hasClass('affix'), 'affix class was not added')
+ })
+
+ QUnit.test('should trigger affixed event after affix', function (assert) {
+ assert.expect(2)
+ var done = assert.async()
+
+ var templateHTML = '<div id="affixTarget">'
+ + '<ul>'
+ + '<li>Please affix</li>'
+ + '<li>And unaffix</li>'
+ + '</ul>'
+ + '</div>'
+ + '<div id="affixAfter" style="height: 20000px; display: block;"/>'
+ $(templateHTML).appendTo(document.body)
+
+ $('#affixTarget').bootstrapAffix({
+ offset: $('#affixTarget ul').position()
+ })
+
+ $('#affixTarget')
+ .on('affix.bs.affix', function () {
+ assert.ok(true, 'affix event fired')
+ }).on('affixed.bs.affix', function () {
+ assert.ok(true, 'affixed event fired')
+ $('#affixTarget, #affixAfter').remove()
+ done()
+ })
+
+ setTimeout(function () {
+ window.scrollTo(0, document.body.scrollHeight)
+
+ setTimeout(function () {
+ window.scroll(0, 0)
+ }, 16) // for testing in a browser
+ }, 0)
+ })
+
+ QUnit.test('should affix-top when scrolling up to offset when parent has padding', function (assert) {
+ assert.expect(1)
+ var done = assert.async()
+
+ var templateHTML = '<div id="padding-offset" style="padding-top: 20px;">'
+ + '<div id="affixTopTarget">'
+ + '<p>Testing affix-top class is added</p>'
+ + '</div>'
+ + '<div style="height: 1000px; display: block;"/>'
+ + '</div>'
+ $(templateHTML).appendTo(document.body)
+
+ $('#affixTopTarget')
+ .bootstrapAffix({
+ offset: { top: 120, bottom: 0 }
+ })
+ .on('affixed-top.bs.affix', function () {
+ assert.ok($('#affixTopTarget').hasClass('affix-top'), 'affix-top class applied')
+ $('#padding-offset').remove()
+ done()
+ })
+
+ setTimeout(function () {
+ window.scrollTo(0, document.body.scrollHeight)
+
+ setTimeout(function () {
+ window.scroll(0, 119)
+ }, 250)
+ }, 250)
+ })
+})
diff --git a/js/tests/unit/button.js b/js/tests/unit/button.js
index 08b071d655..691796c428 100644
--- a/js/tests/unit/button.js
+++ b/js/tests/unit/button.js
@@ -1,83 +1,137 @@
$(function () {
'use strict';
- module('button plugin')
+ QUnit.module('button plugin')
- test('should be defined on jquery object', function () {
- ok($(document.body).button, 'button method is defined')
+ QUnit.test('should be defined on jquery object', function (assert) {
+ assert.expect(1)
+ assert.ok($(document.body).button, 'button method is defined')
})
- module('button', {
- setup: function () {
+ QUnit.module('button', {
+ beforeEach: function () {
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
$.fn.bootstrapButton = $.fn.button.noConflict()
},
- teardown: function () {
+ afterEach: function () {
$.fn.button = $.fn.bootstrapButton
delete $.fn.bootstrapButton
}
})
- test('should provide no conflict', function () {
- strictEqual($.fn.button, undefined, 'button was set back to undefined (org value)')
+ QUnit.test('should provide no conflict', function (assert) {
+ assert.expect(1)
+ assert.strictEqual($.fn.button, undefined, 'button was set back to undefined (org value)')
})
- test('should return jquery collection containing the element', function () {
+ QUnit.test('should return jquery collection containing the element', function (assert) {
+ assert.expect(2)
var $el = $('<div/>')
var $button = $el.bootstrapButton()
- ok($button instanceof $, 'returns jquery collection')
- strictEqual($button[0], $el[0], 'collection contains element')
+ assert.ok($button instanceof $, 'returns jquery collection')
+ assert.strictEqual($button[0], $el[0], 'collection contains element')
})
- test('should toggle active', function () {
+ QUnit.test('should return set state to loading', function (assert) {
+ assert.expect(4)
+ var $btn = $('<button class="btn" data-loading-text="fat">mdo</button>')
+ assert.strictEqual($btn.html(), 'mdo', 'btn text equals mdo')
+ $btn.bootstrapButton('loading')
+ var done = assert.async()
+ setTimeout(function () {
+ assert.strictEqual($btn.html(), 'fat', 'btn text equals fat')
+ assert.ok($btn[0].hasAttribute('disabled'), 'btn is disabled')
+ assert.ok($btn.hasClass('disabled'), 'btn has disabled class')
+ done()
+ }, 0)
+ })
+
+ QUnit.test('should return reset state', function (assert) {
+ assert.expect(7)
+ var $btn = $('<button class="btn" data-loading-text="fat">mdo</button>')
+ assert.strictEqual($btn.html(), 'mdo', 'btn text equals mdo')
+ $btn.bootstrapButton('loading')
+ var doneOne = assert.async()
+ setTimeout(function () {
+ assert.strictEqual($btn.html(), 'fat', 'btn text equals fat')
+ assert.ok($btn[0].hasAttribute('disabled'), 'btn is disabled')
+ assert.ok($btn.hasClass('disabled'), 'btn has disabled class')
+ doneOne()
+ var doneTwo = assert.async()
+ $btn.bootstrapButton('reset')
+ setTimeout(function () {
+ assert.strictEqual($btn.html(), 'mdo', 'btn text equals mdo')
+ assert.ok(!$btn[0].hasAttribute('disabled'), 'btn is not disabled')
+ assert.ok(!$btn.hasClass('disabled'), 'btn does not have disabled class')
+ doneTwo()
+ }, 0)
+ }, 0)
+ })
+
+ QUnit.test('should work with an empty string as reset state', function (assert) {
+ assert.expect(7)
+ var $btn = $('<button class="btn" data-loading-text="fat"/>')
+ assert.strictEqual($btn.html(), '', 'btn text equals ""')
+ $btn.bootstrapButton('loading')
+ var doneOne = assert.async()
+ setTimeout(function () {
+ assert.strictEqual($btn.html(), 'fat', 'btn text equals fat')
+ assert.ok($btn[0].hasAttribute('disabled'), 'btn is disabled')
+ assert.ok($btn.hasClass('disabled'), 'btn has disabled class')
+ doneOne()
+ var doneTwo = assert.async()
+ $btn.bootstrapButton('reset')
+ setTimeout(function () {
+ assert.strictEqual($btn.html(), '', 'btn text equals ""')
+ assert.ok(!$btn[0].hasAttribute('disabled'), 'btn is not disabled')
+ assert.ok(!$btn.hasClass('disabled'), 'btn does not have disabled class')
+ doneTwo()
+ }, 0)
+ }, 0)
+ })
+
+ QUnit.test('should toggle active', function (assert) {
+ assert.expect(2)
var $btn = $('<button class="btn" data-toggle="button">mdo</button>')
- ok(!$btn.hasClass('active'), 'btn does not have active class')
+ assert.ok(!$btn.hasClass('active'), 'btn does not have active class')
$btn.bootstrapButton('toggle')
- ok($btn.hasClass('active'), 'btn has class active')
+ assert.ok($btn.hasClass('active'), 'btn has class active')
})
- test('should toggle active when btn children are clicked', function () {
+ QUnit.test('should toggle active when btn children are clicked', function (assert) {
+ assert.expect(2)
var $btn = $('<button class="btn" data-toggle="button">mdo</button>')
var $inner = $('<i/>')
$btn
.append($inner)
.appendTo('#qunit-fixture')
- ok(!$btn.hasClass('active'), 'btn does not have active class')
- $inner.click()
- ok($btn.hasClass('active'), 'btn has class active')
+ assert.ok(!$btn.hasClass('active'), 'btn does not have active class')
+ $inner.trigger('click')
+ assert.ok($btn.hasClass('active'), 'btn has class active')
})
- test('should toggle aria-pressed', function () {
+ QUnit.test('should toggle aria-pressed', function (assert) {
+ assert.expect(2)
var $btn = $('<button class="btn" data-toggle="button" aria-pressed="false">redux</button>')
- equal($btn.attr('aria-pressed'), 'false', 'btn aria-pressed state is false')
+ assert.strictEqual($btn.attr('aria-pressed'), 'false', 'btn aria-pressed state is false')
$btn.bootstrapButton('toggle')
- equal($btn.attr('aria-pressed'), 'true', 'btn aria-pressed state is true')
+ assert.strictEqual($btn.attr('aria-pressed'), 'true', 'btn aria-pressed state is true')
})
- test('should toggle aria-pressed when btn children are clicked', function () {
+ QUnit.test('should toggle aria-pressed when btn children are clicked', function (assert) {
+ assert.expect(2)
var $btn = $('<button class="btn" data-toggle="button" aria-pressed="false">redux</button>')
var $inner = $('<i/>')
$btn
.append($inner)
.appendTo('#qunit-fixture')
- equal($btn.attr('aria-pressed'), 'false', 'btn aria-pressed state is false')
- $inner.click()
- equal($btn.attr('aria-pressed'), 'true', 'btn aria-pressed state is true')
+ assert.strictEqual($btn.attr('aria-pressed'), 'false', 'btn aria-pressed state is false')
+ $inner.trigger('click')
+ assert.strictEqual($btn.attr('aria-pressed'), 'true', 'btn aria-pressed state is true')
})
- test('should toggle active when btn children are clicked within btn-group', function () {
- var $btngroup = $('<div class="btn-group" data-toggle="buttons"/>')
- var $btn = $('<button class="btn">fat</button>')
- var $inner = $('<i/>')
- $btngroup
- .append($btn.append($inner))
- .appendTo('#qunit-fixture')
- ok(!$btn.hasClass('active'), 'btn does not have active class')
- $inner.click()
- ok($btn.hasClass('active'), 'btn has class active')
- })
-
- test('should check for closest matching toggle', function () {
+ QUnit.test('should check for closest matching toggle', function (assert) {
+ assert.expect(12)
var groupHTML = '<div class="btn-group" data-toggle="buttons">'
+ '<label class="btn btn-primary active">'
+ '<input type="radio" name="options" id="option1" checked="true"> Option 1'
@@ -94,21 +148,21 @@ $(function () {
var $btn1 = $group.children().eq(0)
var $btn2 = $group.children().eq(1)
- ok($btn1.hasClass('active'), 'btn1 has active class')
- ok($btn1.find('input').prop('checked'), 'btn1 is checked')
- ok(!$btn2.hasClass('active'), 'btn2 does not have active class')
- ok(!$btn2.find('input').prop('checked'), 'btn2 is not checked')
- $btn2.find('input').click()
- ok(!$btn1.hasClass('active'), 'btn1 does not have active class')
- ok(!$btn1.find('input').prop('checked'), 'btn1 is checked')
- ok($btn2.hasClass('active'), 'btn2 has active class')
- ok($btn2.find('input').prop('checked'), 'btn2 is checked')
-
- $btn2.find('input').click() // clicking an already checked radio should not un-check it
- ok(!$btn1.hasClass('active'), 'btn1 does not have active class')
- ok(!$btn1.find('input').prop('checked'), 'btn1 is checked')
- ok($btn2.hasClass('active'), 'btn2 has active class')
- ok($btn2.find('input').prop('checked'), 'btn2 is checked')
+ assert.ok($btn1.hasClass('active'), 'btn1 has active class')
+ assert.ok($btn1.find('input').prop('checked'), 'btn1 is checked')
+ assert.ok(!$btn2.hasClass('active'), 'btn2 does not have active class')
+ assert.ok(!$btn2.find('input').prop('checked'), 'btn2 is not checked')
+ $btn2.find('input').trigger('click')
+ assert.ok(!$btn1.hasClass('active'), 'btn1 does not have active class')
+ assert.ok(!$btn1.find('input').prop('checked'), 'btn1 is not checked')
+ assert.ok($btn2.hasClass('active'), 'btn2 has active class')
+ assert.ok($btn2.find('input').prop('checked'), 'btn2 is checked')
+
+ $btn2.find('input').trigger('click') // clicking an already checked radio should not un-check it
+ assert.ok(!$btn1.hasClass('active'), 'btn1 does not have active class')
+ assert.ok(!$btn1.find('input').prop('checked'), 'btn1 is not checked')
+ assert.ok($btn2.hasClass('active'), 'btn2 has active class')
+ assert.ok($btn2.find('input').prop('checked'), 'btn2 is checked')
})
})
diff --git a/js/tests/unit/carousel.js b/js/tests/unit/carousel.js
index 2ad2c67347..39d2505980 100644
--- a/js/tests/unit/carousel.js
+++ b/js/tests/unit/carousel.js
@@ -1,49 +1,54 @@
$(function () {
'use strict';
- module('carousel plugin')
+ QUnit.module('carousel plugin')
- test('should be defined on jQuery object', function () {
- ok($(document.body).carousel, 'carousel method is defined')
+ QUnit.test('should be defined on jQuery object', function (assert) {
+ assert.expect(1)
+ assert.ok($(document.body).carousel, 'carousel method is defined')
})
- module('carousel', {
- setup: function () {
+ QUnit.module('carousel', {
+ beforeEach: function () {
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
$.fn.bootstrapCarousel = $.fn.carousel.noConflict()
},
- teardown: function () {
+ afterEach: function () {
$.fn.carousel = $.fn.bootstrapCarousel
delete $.fn.bootstrapCarousel
}
})
- test('should provide no conflict', function () {
- strictEqual($.fn.carousel, undefined, 'carousel was set back to undefined (orig value)')
+ QUnit.test('should provide no conflict', function (assert) {
+ assert.expect(1)
+ assert.strictEqual($.fn.carousel, undefined, 'carousel was set back to undefined (orig value)')
})
- test('should return jquery collection containing the element', function () {
+ QUnit.test('should return jquery collection containing the element', function (assert) {
+ assert.expect(2)
var $el = $('<div/>')
var $carousel = $el.bootstrapCarousel()
- ok($carousel instanceof $, 'returns jquery collection')
- strictEqual($carousel[0], $el[0], 'collection contains element')
+ assert.ok($carousel instanceof $, 'returns jquery collection')
+ assert.strictEqual($carousel[0], $el[0], 'collection contains element')
})
- test('should not fire slid when slide is prevented', function (assert) {
+ QUnit.test('should not fire slid when slide is prevented', function (assert) {
+ assert.expect(1)
var done = assert.async()
$('<div class="carousel"/>')
.on('slide.bs.carousel', function (e) {
e.preventDefault()
- ok(true, 'slide event fired')
+ assert.ok(true, 'slide event fired')
done()
})
.on('slid.bs.carousel', function () {
- ok(false, 'slid event fired')
+ assert.ok(false, 'slid event fired')
})
.bootstrapCarousel('next')
})
- test('should reset when slide is prevented', function (assert) {
+ QUnit.test('should reset when slide is prevented', function (assert) {
+ assert.expect(6)
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide">'
+ '<ol class="carousel-indicators">'
+ '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>'
@@ -51,13 +56,13 @@ $(function () {
+ '<li data-target="#carousel-example-generic" data-slide-to="2"/>'
+ '</ol>'
+ '<div class="carousel-inner">'
- + '<div class="carousel-item active">'
+ + '<div class="item active">'
+ '<div class="carousel-caption"/>'
+ '</div>'
- + '<div class="carousel-item">'
+ + '<div class="item">'
+ '<div class="carousel-caption"/>'
+ '</div>'
- + '<div class="carousel-item">'
+ + '<div class="item">'
+ '<div class="carousel-caption"/>'
+ '</div>'
+ '</div>'
@@ -71,27 +76,28 @@ $(function () {
.one('slide.bs.carousel', function (e) {
e.preventDefault()
setTimeout(function () {
- ok($carousel.find('.carousel-item:eq(0)').is('.active'), 'first item still active')
- ok($carousel.find('.carousel-indicators li:eq(0)').is('.active'), 'first indicator still active')
+ assert.ok($carousel.find('.item:eq(0)').is('.active'), 'first item still active')
+ assert.ok($carousel.find('.carousel-indicators li:eq(0)').is('.active'), 'first indicator still active')
$carousel.bootstrapCarousel('next')
}, 0)
})
.one('slid.bs.carousel', function () {
setTimeout(function () {
- ok(!$carousel.find('.carousel-item:eq(0)').is('.active'), 'first item still active')
- ok(!$carousel.find('.carousel-indicators li:eq(0)').is('.active'), 'first indicator still active')
- ok($carousel.find('.carousel-item:eq(1)').is('.active'), 'second item active')
- ok($carousel.find('.carousel-indicators li:eq(1)').is('.active'), 'second indicator active')
+ assert.ok(!$carousel.find('.item:eq(0)').is('.active'), 'first item still active')
+ assert.ok(!$carousel.find('.carousel-indicators li:eq(0)').is('.active'), 'first indicator still active')
+ assert.ok($carousel.find('.item:eq(1)').is('.active'), 'second item active')
+ assert.ok($carousel.find('.carousel-indicators li:eq(1)').is('.active'), 'second indicator active')
done()
}, 0)
})
.bootstrapCarousel('next')
})
- test('should fire slide event with direction', function (assert) {
+ QUnit.test('should fire slide event with direction', function (assert) {
+ assert.expect(4)
var carouselHTML = '<div id="myCarousel" class="carousel slide">'
+ '<div class="carousel-inner">'
- + '<div class="carousel-item active">'
+ + '<div class="item active">'
+ '<img alt="">'
+ '<div class="carousel-caption">'
+ '<h4>First Thumbnail label</h4>'
@@ -100,7 +106,7 @@ $(function () {
+ 'ultricies vehicula ut id elit.</p>'
+ '</div>'
+ '</div>'
- + '<div class="carousel-item">'
+ + '<div class="item">'
+ '<img alt="">'
+ '<div class="carousel-caption">'
+ '<h4>Second Thumbnail label</h4>'
@@ -109,7 +115,7 @@ $(function () {
+ 'ultricies vehicula ut id elit.</p>'
+ '</div>'
+ '</div>'
- + '<div class="carousel-item">'
+ + '<div class="item">'
+ '<img alt="">'
+ '<div class="carousel-caption">'
+ '<h4>Third Thumbnail label</h4>'
@@ -128,13 +134,13 @@ $(function () {
$carousel
.one('slide.bs.carousel', function (e) {
- ok(e.direction, 'direction present on next')
- strictEqual(e.direction, 'left', 'direction is left on next')
+ assert.ok(e.direction, 'direction present on next')
+ assert.strictEqual(e.direction, 'left', 'direction is left on next')
$carousel
.one('slide.bs.carousel', function (e) {
- ok(e.direction, 'direction present on prev')
- strictEqual(e.direction, 'right', 'direction is right on prev')
+ assert.ok(e.direction, 'direction present on prev')
+ assert.strictEqual(e.direction, 'right', 'direction is right on prev')
done()
})
.bootstrapCarousel('prev')
@@ -142,10 +148,11 @@ $(function () {
.bootstrapCarousel('next')
})
- test('should fire slid event with direction', function (assert) {
+ QUnit.test('should fire slid event with direction', function (assert) {
+ assert.expect(4)
var carouselHTML = '<div id="myCarousel" class="carousel slide">'
+ '<div class="carousel-inner">'
- + '<div class="carousel-item active">'
+ + '<div class="item active">'
+ '<img alt="">'
+ '<div class="carousel-caption">'
+ '<h4>First Thumbnail label</h4>'
@@ -154,7 +161,7 @@ $(function () {
+ 'ultricies vehicula ut id elit.</p>'
+ '</div>'
+ '</div>'
- + '<div class="carousel-item">'
+ + '<div class="item">'
+ '<img alt="">'
+ '<div class="carousel-caption">'
+ '<h4>Second Thumbnail label</h4>'
@@ -163,7 +170,7 @@ $(function () {
+ 'ultricies vehicula ut id elit.</p>'
+ '</div>'
+ '</div>'
- + '<div class="carousel-item">'
+ + '<div class="item">'
+ '<img alt="">'
+ '<div class="carousel-caption">'
+ '<h4>Third Thumbnail label</h4>'
@@ -182,13 +189,13 @@ $(function () {
$carousel
.one('slid.bs.carousel', function (e) {
- ok(e.direction, 'direction present on next')
- strictEqual(e.direction, 'left', 'direction is left on next')
+ assert.ok(e.direction, 'direction present on next')
+ assert.strictEqual(e.direction, 'left', 'direction is left on next')
$carousel
.one('slid.bs.carousel', function (e) {
- ok(e.direction, 'direction present on prev')
- strictEqual(e.direction, 'right', 'direction is right on prev')
+ assert.ok(e.direction, 'direction present on prev')
+ assert.strictEqual(e.direction, 'right', 'direction is right on prev')
done()
})
.bootstrapCarousel('prev')
@@ -196,10 +203,11 @@ $(function () {
.bootstrapCarousel('next')
})
- test('should fire slide event with relatedTarget', function (assert) {
+ QUnit.test('should fire slide event with relatedTarget', function (assert) {
+ assert.expect(2)
var template = '<div id="myCarousel" class="carousel slide">'
+ '<div class="carousel-inner">'
- + '<div class="carousel-item active">'
+ + '<div class="item active">'
+ '<img alt="">'
+ '<div class="carousel-caption">'
+ '<h4>First Thumbnail label</h4>'
@@ -208,7 +216,7 @@ $(function () {
+ 'ultricies vehicula ut id elit.</p>'
+ '</div>'
+ '</div>'
- + '<div class="carousel-item">'
+ + '<div class="item">'
+ '<img alt="">'
+ '<div class="carousel-caption">'
+ '<h4>Second Thumbnail label</h4>'
@@ -217,7 +225,7 @@ $(function () {
+ 'ultricies vehicula ut id elit.</p>'
+ '</div>'
+ '</div>'
- + '<div class="carousel-item">'
+ + '<div class="item">'
+ '<img alt="">'
+ '<div class="carousel-caption">'
+ '<h4>Third Thumbnail label</h4>'
@@ -235,17 +243,18 @@ $(function () {
$(template)
.on('slide.bs.carousel', function (e) {
- ok(e.relatedTarget, 'relatedTarget present')
- ok($(e.relatedTarget).hasClass('carousel-item'), 'relatedTarget has class "carousel-item"')
+ assert.ok(e.relatedTarget, 'relatedTarget present')
+ assert.ok($(e.relatedTarget).hasClass('item'), 'relatedTarget has class "item"')
done()
})
.bootstrapCarousel('next')
})
- test('should fire slid event with relatedTarget', function (assert) {
+ QUnit.test('should fire slid event with relatedTarget', function (assert) {
+ assert.expect(2)
var template = '<div id="myCarousel" class="carousel slide">'
+ '<div class="carousel-inner">'
- + '<div class="carousel-item active">'
+ + '<div class="item active">'
+ '<img alt="">'
+ '<div class="carousel-caption">'
+ '<h4>First Thumbnail label</h4>'
@@ -254,7 +263,7 @@ $(function () {
+ 'ultricies vehicula ut id elit.</p>'
+ '</div>'
+ '</div>'
- + '<div class="carousel-item">'
+ + '<div class="item">'
+ '<img alt="">'
+ '<div class="carousel-caption">'
+ '<h4>Second Thumbnail label</h4>'
@@ -263,7 +272,7 @@ $(function () {
+ 'ultricies vehicula ut id elit.</p>'
+ '</div>'
+ '</div>'
- + '<div class="carousel-item">'
+ + '<div class="item">'
+ '<img alt="">'
+ '<div class="carousel-caption">'
+ '<h4>Third Thumbnail label</h4>'
@@ -281,17 +290,18 @@ $(function () {
$(template)
.on('slid.bs.carousel', function (e) {
- ok(e.relatedTarget, 'relatedTarget present')
- ok($(e.relatedTarget).hasClass('carousel-item'), 'relatedTarget has class "carousel-item"')
+ assert.ok(e.relatedTarget, 'relatedTarget present')
+ assert.ok($(e.relatedTarget).hasClass('item'), 'relatedTarget has class "item"')
done()
})
.bootstrapCarousel('next')
})
- test('should set interval from data attribute', function () {
+ QUnit.test('should set interval from data attribute', function (assert) {
+ assert.expect(4)
var templateHTML = '<div id="myCarousel" class="carousel slide">'
+ '<div class="carousel-inner">'
- + '<div class="carousel-item active">'
+ + '<div class="item active">'
+ '<img alt="">'
+ '<div class="carousel-caption">'
+ '<h4>First Thumbnail label</h4>'
@@ -300,7 +310,7 @@ $(function () {
+ 'ultricies vehicula ut id elit.</p>'
+ '</div>'
+ '</div>'
- + '<div class="carousel-item">'
+ + '<div class="item">'
+ '<img alt="">'
+ '<div class="carousel-caption">'
+ '<h4>Second Thumbnail label</h4>'
@@ -309,7 +319,7 @@ $(function () {
+ 'ultricies vehicula ut id elit.</p>'
+ '</div>'
+ '</div>'
- + '<div class="carousel-item">'
+ + '<div class="item">'
+ '<img alt="">'
+ '<div class="carousel-caption">'
+ '<h4>Third Thumbnail label</h4>'
@@ -322,45 +332,45 @@ $(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').getConfig().interval, 1814)
+ $('[data-slide]').first().trigger('click')
+ assert.strictEqual($carousel.data('bs.carousel').options.interval, 1814)
$carousel.remove()
$carousel.appendTo('body').attr('data-modal', 'foobar')
- $('[data-slide]').first().click()
- equal($carousel.data('bs.carousel').getConfig().interval, 1814, 'even if there is an data-modal attribute set')
+ $('[data-slide]').first().trigger('click')
+ assert.strictEqual($carousel.data('bs.carousel').options.interval, 1814, 'even if there is an data-modal attribute set')
$carousel.remove()
$carousel.appendTo('body')
- $('[data-slide]').first().click()
+ $('[data-slide]').first().trigger('click')
$carousel.attr('data-interval', 1860)
- $('[data-slide]').first().click()
- equal($carousel.data('bs.carousel').getConfig().interval, 1814, 'attributes should be read only on initialization')
+ $('[data-slide]').first().trigger('click')
+ assert.strictEqual($carousel.data('bs.carousel').options.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').getConfig().interval, false, 'data attribute has higher priority than default options')
+ assert.strictEqual($carousel.data('bs.carousel').options.interval, false, 'data attribute has higher priority than default options')
$carousel.remove()
})
- test('should skip over non-items when using item indices', function () {
+ QUnit.test('should skip over non-items when using item indices', function (assert) {
+ assert.expect(2)
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="1814">'
+ '<div class="carousel-inner">'
- + '<div class="carousel-item active">'
+ + '<div class="item active">'
+ '<img alt="">'
+ '</div>'
+ '<script type="text/x-metamorph" id="thingy"/>'
- + '<div class="carousel-item">'
+ + '<div class="item">'
+ '<img alt="">'
+ '</div>'
- + '<div class="carousel-item">'
+ + '<div class="item">'
+ '</div>'
+ '</div>'
+ '</div>'
@@ -368,24 +378,25 @@ $(function () {
$template.bootstrapCarousel()
- strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active')
+ assert.strictEqual($template.find('.item')[0], $template.find('.active')[0], 'first item active')
$template.bootstrapCarousel(1)
- strictEqual($template.find('.carousel-item')[1], $template.find('.active')[0], 'second item active')
+ assert.strictEqual($template.find('.item')[1], $template.find('.active')[0], 'second item active')
})
- test('should skip over non-items when using next/prev methods', function () {
+ QUnit.test('should skip over non-items when using next/prev methods', function (assert) {
+ assert.expect(2)
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="1814">'
+ '<div class="carousel-inner">'
- + '<div class="carousel-item active">'
+ + '<div class="item active">'
+ '<img alt="">'
+ '</div>'
+ '<script type="text/x-metamorph" id="thingy"/>'
- + '<div class="carousel-item">'
+ + '<div class="item">'
+ '<img alt="">'
+ '</div>'
- + '<div class="carousel-item">'
+ + '<div class="item">'
+ '</div>'
+ '</div>'
+ '</div>'
@@ -393,23 +404,24 @@ $(function () {
$template.bootstrapCarousel()
- strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active')
+ assert.strictEqual($template.find('.item')[0], $template.find('.active')[0], 'first item active')
$template.bootstrapCarousel('next')
- strictEqual($template.find('.carousel-item')[1], $template.find('.active')[0], 'second item active')
+ assert.strictEqual($template.find('.item')[1], $template.find('.active')[0], 'second item active')
})
- test('should go to previous item if left arrow key is pressed', function () {
+ QUnit.test('should go to previous item if left arrow key is pressed', function (assert) {
+ assert.expect(2)
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false">'
+ '<div class="carousel-inner">'
- + '<div id="first" class="carousel-item">'
+ + '<div id="first" class="item">'
+ '<img alt="">'
+ '</div>'
- + '<div id="second" class="carousel-item active">'
+ + '<div id="second" class="item active">'
+ '<img alt="">'
+ '</div>'
- + '<div id="third" class="carousel-item">'
+ + '<div id="third" class="item">'
+ '<img alt="">'
+ '</div>'
+ '</div>'
@@ -418,23 +430,24 @@ $(function () {
$template.bootstrapCarousel()
- strictEqual($template.find('.carousel-item')[1], $template.find('.active')[0], 'second item active')
+ assert.strictEqual($template.find('.item')[1], $template.find('.active')[0], 'second item active')
$template.trigger($.Event('keydown', { which: 37 }))
- strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active')
+ assert.strictEqual($template.find('.item')[0], $template.find('.active')[0], 'first item active')
})
- test('should go to next item if right arrow key is pressed', function () {
+ QUnit.test('should go to next item if right arrow key is pressed', function (assert) {
+ assert.expect(2)
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false">'
+ '<div class="carousel-inner">'
- + '<div id="first" class="carousel-item active">'
+ + '<div id="first" class="item active">'
+ '<img alt="">'
+ '</div>'
- + '<div id="second" class="carousel-item">'
+ + '<div id="second" class="item">'
+ '<img alt="">'
+ '</div>'
- + '<div id="third" class="carousel-item">'
+ + '<div id="third" class="item">'
+ '<img alt="">'
+ '</div>'
+ '</div>'
@@ -443,23 +456,24 @@ $(function () {
$template.bootstrapCarousel()
- strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active')
+ assert.strictEqual($template.find('.item')[0], $template.find('.active')[0], 'first item active')
$template.trigger($.Event('keydown', { which: 39 }))
- strictEqual($template.find('.carousel-item')[1], $template.find('.active')[0], 'second item active')
+ assert.strictEqual($template.find('.item')[1], $template.find('.active')[0], 'second item active')
})
- test('should support disabling the keyboard navigation', function () {
+ QUnit.test('should support disabling the keyboard navigation', function (assert) {
+ assert.expect(3)
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false" data-keyboard="false">'
+ '<div class="carousel-inner">'
- + '<div id="first" class="carousel-item active">'
+ + '<div id="first" class="item active">'
+ '<img alt="">'
+ '</div>'
- + '<div id="second" class="carousel-item">'
+ + '<div id="second" class="item">'
+ '<img alt="">'
+ '</div>'
- + '<div id="third" class="carousel-item">'
+ + '<div id="third" class="item">'
+ '<img alt="">'
+ '</div>'
+ '</div>'
@@ -468,29 +482,30 @@ $(function () {
$template.bootstrapCarousel()
- strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active')
+ assert.strictEqual($template.find('.item')[0], $template.find('.active')[0], 'first item active')
$template.trigger($.Event('keydown', { which: 39 }))
- strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item still active after right arrow press')
+ assert.strictEqual($template.find('.item')[0], $template.find('.active')[0], 'first item still active after right arrow press')
$template.trigger($.Event('keydown', { which: 37 }))
- strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item still active after left arrow press')
+ assert.strictEqual($template.find('.item')[0], $template.find('.active')[0], 'first item still active after left arrow press')
})
- test('should ignore keyboard events within <input>s and <textarea>s', function () {
+ QUnit.test('should ignore keyboard events within <input>s and <textarea>s', function (assert) {
+ assert.expect(7)
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false">'
+ '<div class="carousel-inner">'
- + '<div id="first" class="carousel-item active">'
+ + '<div id="first" class="item active">'
+ '<img alt="">'
+ '<input type="text" id="in-put">'
+ '<textarea id="text-area"></textarea>'
+ '</div>'
- + '<div id="second" class="carousel-item">'
+ + '<div id="second" class="item">'
+ '<img alt="">'
+ '</div>'
- + '<div id="third" class="carousel-item">'
+ + '<div id="third" class="item">'
+ '<img alt="">'
+ '</div>'
+ '</div>'
@@ -499,39 +514,40 @@ $(function () {
var $input = $template.find('#in-put')
var $textarea = $template.find('#text-area')
- strictEqual($input.length, 1, 'found <input>')
- strictEqual($textarea.length, 1, 'found <textarea>')
+ assert.strictEqual($input.length, 1, 'found <input>')
+ assert.strictEqual($textarea.length, 1, 'found <textarea>')
$template.bootstrapCarousel()
- strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active')
+ assert.strictEqual($template.find('.item')[0], $template.find('.active')[0], 'first item active')
$input.trigger($.Event('keydown', { which: 39 }))
- strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item still active after right arrow press in <input>')
+ assert.strictEqual($template.find('.item')[0], $template.find('.active')[0], 'first item still active after right arrow press in <input>')
$input.trigger($.Event('keydown', { which: 37 }))
- strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item still active after left arrow press in <input>')
+ assert.strictEqual($template.find('.item')[0], $template.find('.active')[0], 'first item still active after left arrow press in <input>')
$textarea.trigger($.Event('keydown', { which: 39 }))
- strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item still active after right arrow press in <textarea>')
+ assert.strictEqual($template.find('.item')[0], $template.find('.active')[0], 'first item still active after right arrow press in <textarea>')
$textarea.trigger($.Event('keydown', { which: 37 }))
- strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item still active after left arrow press in <textarea>')
+ assert.strictEqual($template.find('.item')[0], $template.find('.active')[0], 'first item still active after left arrow press in <textarea>')
})
- test('should only add mouseenter and mouseleave listeners when not on mobile', function () {
+ QUnit.test('should only add mouseenter and mouseleave listeners when not on mobile', function (assert) {
+ assert.expect(2)
var isMobile = 'ontouchstart' in document.documentElement
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false" data-pause="hover">'
+ '<div class="carousel-inner">'
- + '<div id="first" class="carousel-item active">'
+ + '<div id="first" class="item active">'
+ '<img alt="">'
+ '</div>'
- + '<div id="second" class="carousel-item">'
+ + '<div id="second" class="item">'
+ '<img alt="">'
+ '</div>'
- + '<div id="third" class="carousel-item">'
+ + '<div id="third" class="item">'
+ '<img alt="">'
+ '</div>'
+ '</div>'
@@ -539,11 +555,12 @@ $(function () {
var $template = $(templateHTML).bootstrapCarousel()
$.each(['mouseover', 'mouseout'], function (i, type) {
- strictEqual(type in $._data($template[0], 'events'), !isMobile, 'does' + (isMobile ? ' not' : '') + ' listen for ' + type + ' events')
+ assert.strictEqual(type in $._data($template[0], 'events'), !isMobile, 'does' + (isMobile ? ' not' : '') + ' listen for ' + type + ' events')
})
})
- test('should wrap around from end to start when wrap option is true', function (assert) {
+ QUnit.test('should wrap around from end to start when wrap option is true', function (assert) {
+ assert.expect(3)
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="true">'
+ '<ol class="carousel-indicators">'
+ '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>'
@@ -551,13 +568,13 @@ $(function () {
+ '<li data-target="#carousel-example-generic" data-slide-to="2"/>'
+ '</ol>'
+ '<div class="carousel-inner">'
- + '<div class="carousel-item active" id="one">'
+ + '<div class="item active" id="one">'
+ '<div class="carousel-caption"/>'
+ '</div>'
- + '<div class="carousel-item" id="two">'
+ + '<div class="item" id="two">'
+ '<div class="carousel-caption"/>'
+ '</div>'
- + '<div class="carousel-item" id="three">'
+ + '<div class="item" id="three">'
+ '<div class="carousel-caption"/>'
+ '</div>'
+ '</div>'
@@ -565,19 +582,19 @@ $(function () {
+ '<a class="right carousel-control" href="#carousel-example-generic" data-slide="next"/>'
+ '</div>'
var $carousel = $(carouselHTML)
- var getActiveId = function () { return $carousel.find('.carousel-item.active').attr('id') }
+ var getActiveId = function () { return $carousel.find('.item.active').attr('id') }
var done = assert.async()
$carousel
.one('slid.bs.carousel', function () {
- strictEqual(getActiveId(), 'two', 'carousel slid from 1st to 2nd slide')
+ assert.strictEqual(getActiveId(), 'two', 'carousel slid from 1st to 2nd slide')
$carousel
.one('slid.bs.carousel', function () {
- strictEqual(getActiveId(), 'three', 'carousel slid from 2nd to 3rd slide')
+ assert.strictEqual(getActiveId(), 'three', 'carousel slid from 2nd to 3rd slide')
$carousel
.one('slid.bs.carousel', function () {
- strictEqual(getActiveId(), 'one', 'carousel wrapped around and slid from 3rd to 1st slide')
+ assert.strictEqual(getActiveId(), 'one', 'carousel wrapped around and slid from 3rd to 1st slide')
done()
})
.bootstrapCarousel('next')
@@ -587,7 +604,8 @@ $(function () {
.bootstrapCarousel('next')
})
- test('should wrap around from start to end when wrap option is true', function (assert) {
+ QUnit.test('should wrap around from start to end when wrap option is true', function (assert) {
+ assert.expect(1)
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="true">'
+ '<ol class="carousel-indicators">'
+ '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>'
@@ -595,13 +613,13 @@ $(function () {
+ '<li data-target="#carousel-example-generic" data-slide-to="2"/>'
+ '</ol>'
+ '<div class="carousel-inner">'
- + '<div class="carousel-item active" id="one">'
+ + '<div class="item active" id="one">'
+ '<div class="carousel-caption"/>'
+ '</div>'
- + '<div class="carousel-item" id="two">'
+ + '<div class="item" id="two">'
+ '<div class="carousel-caption"/>'
+ '</div>'
- + '<div class="carousel-item" id="three">'
+ + '<div class="item" id="three">'
+ '<div class="carousel-caption"/>'
+ '</div>'
+ '</div>'
@@ -614,13 +632,14 @@ $(function () {
$carousel
.on('slid.bs.carousel', function () {
- strictEqual($carousel.find('.carousel-item.active').attr('id'), 'three', 'carousel wrapped around and slid from 1st to 3rd slide')
+ assert.strictEqual($carousel.find('.item.active').attr('id'), 'three', 'carousel wrapped around and slid from 1st to 3rd slide')
done()
})
.bootstrapCarousel('prev')
})
- test('should stay at the end when the next method is called and wrap is false', function (assert) {
+ QUnit.test('should stay at the end when the next method is called and wrap is false', function (assert) {
+ assert.expect(3)
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="false">'
+ '<ol class="carousel-indicators">'
+ '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>'
@@ -628,13 +647,13 @@ $(function () {
+ '<li data-target="#carousel-example-generic" data-slide-to="2"/>'
+ '</ol>'
+ '<div class="carousel-inner">'
- + '<div class="carousel-item active" id="one">'
+ + '<div class="item active" id="one">'
+ '<div class="carousel-caption"/>'
+ '</div>'
- + '<div class="carousel-item" id="two">'
+ + '<div class="item" id="two">'
+ '<div class="carousel-caption"/>'
+ '</div>'
- + '<div class="carousel-item" id="three">'
+ + '<div class="item" id="three">'
+ '<div class="carousel-caption"/>'
+ '</div>'
+ '</div>'
@@ -642,22 +661,22 @@ $(function () {
+ '<a class="right carousel-control" href="#carousel-example-generic" data-slide="next"/>'
+ '</div>'
var $carousel = $(carouselHTML)
- var getActiveId = function () { return $carousel.find('.carousel-item.active').attr('id') }
+ var getActiveId = function () { return $carousel.find('.item.active').attr('id') }
var done = assert.async()
$carousel
.one('slid.bs.carousel', function () {
- strictEqual(getActiveId(), 'two', 'carousel slid from 1st to 2nd slide')
+ assert.strictEqual(getActiveId(), 'two', 'carousel slid from 1st to 2nd slide')
$carousel
.one('slid.bs.carousel', function () {
- strictEqual(getActiveId(), 'three', 'carousel slid from 2nd to 3rd slide')
+ assert.strictEqual(getActiveId(), 'three', 'carousel slid from 2nd to 3rd slide')
$carousel
.one('slid.bs.carousel', function () {
- ok(false, 'carousel slid when it should not have slid')
+ assert.ok(false, 'carousel slid when it should not have slid')
})
.bootstrapCarousel('next')
- strictEqual(getActiveId(), 'three', 'carousel did not wrap around and stayed on 3rd slide')
+ assert.strictEqual(getActiveId(), 'three', 'carousel did not wrap around and stayed on 3rd slide')
done()
})
.bootstrapCarousel('next')
@@ -665,7 +684,8 @@ $(function () {
.bootstrapCarousel('next')
})
- test('should stay at the start when the prev method is called and wrap is false', function () {
+ QUnit.test('should stay at the start when the prev method is called and wrap is false', function (assert) {
+ assert.expect(1)
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="false">'
+ '<ol class="carousel-indicators">'
+ '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>'
@@ -673,13 +693,13 @@ $(function () {
+ '<li data-target="#carousel-example-generic" data-slide-to="2"/>'
+ '</ol>'
+ '<div class="carousel-inner">'
- + '<div class="carousel-item active" id="one">'
+ + '<div class="item active" id="one">'
+ '<div class="carousel-caption"/>'
+ '</div>'
- + '<div class="carousel-item" id="two">'
+ + '<div class="item" id="two">'
+ '<div class="carousel-caption"/>'
+ '</div>'
- + '<div class="carousel-item" id="three">'
+ + '<div class="item" id="three">'
+ '<div class="carousel-caption"/>'
+ '</div>'
+ '</div>'
@@ -690,9 +710,9 @@ $(function () {
$carousel
.on('slid.bs.carousel', function () {
- ok(false, 'carousel slid when it should not have slid')
+ assert.ok(false, 'carousel slid when it should not have slid')
})
.bootstrapCarousel('prev')
- strictEqual($carousel.find('.carousel-item.active').attr('id'), 'one', 'carousel did not wrap around and stayed on 1st slide')
+ assert.strictEqual($carousel.find('.item.active').attr('id'), 'one', 'carousel did not wrap around and stayed on 1st slide')
})
})
diff --git a/js/tests/unit/collapse.js b/js/tests/unit/collapse.js
index 0f1b7b119f..0efa65400d 100644
--- a/js/tests/unit/collapse.js
+++ b/js/tests/unit/collapse.js
@@ -400,7 +400,7 @@ $(function () {
$body2
.toggleClass('in collapsing')
- .data('bs.collapse').setTransitioning(true)
+ .data('bs.collapse').transitioning = 1
$target1.trigger('click')
diff --git a/js/tests/unit/modal.js b/js/tests/unit/modal.js
index 7018b865f3..217e4d7f76 100644
--- a/js/tests/unit/modal.js
+++ b/js/tests/unit/modal.js
@@ -1,194 +1,209 @@
$(function () {
'use strict';
- module('modal plugin')
+ QUnit.module('modal plugin')
- test('should be defined on jquery object', function () {
- ok($(document.body).modal, 'modal method is defined')
+ QUnit.test('should be defined on jquery object', function (assert) {
+ assert.expect(1)
+ assert.ok($(document.body).modal, 'modal method is defined')
})
- module('modal', {
- setup: function () {
+ QUnit.module('modal', {
+ beforeEach: function () {
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
$.fn.bootstrapModal = $.fn.modal.noConflict()
},
- teardown: function () {
+ afterEach: function () {
$.fn.modal = $.fn.bootstrapModal
delete $.fn.bootstrapModal
}
})
- test('should provide no conflict', function () {
- strictEqual($.fn.modal, undefined, 'modal was set back to undefined (orig value)')
+ QUnit.test('should provide no conflict', function (assert) {
+ assert.expect(1)
+ assert.strictEqual($.fn.modal, undefined, 'modal was set back to undefined (orig value)')
})
- test('should return jquery collection containing the element', function () {
+ QUnit.test('should return jquery collection containing the element', function (assert) {
+ assert.expect(2)
var $el = $('<div id="modal-test"/>')
var $modal = $el.bootstrapModal()
- ok($modal instanceof $, 'returns jquery collection')
- strictEqual($modal[0], $el[0], 'collection contains element')
+ assert.ok($modal instanceof $, 'returns jquery collection')
+ assert.strictEqual($modal[0], $el[0], 'collection contains element')
})
- test('should expose defaults var for settings', function () {
- ok($.fn.bootstrapModal.Constructor.Defaults, 'default object exposed')
+ QUnit.test('should expose defaults var for settings', function (assert) {
+ assert.expect(1)
+ assert.ok($.fn.bootstrapModal.Constructor.DEFAULTS, 'default object exposed')
})
- test('should insert into dom when show method is called', function (assert) {
+ QUnit.test('should insert into dom when show method is called', function (assert) {
+ assert.expect(1)
var done = assert.async()
$('<div id="modal-test"/>')
.on('shown.bs.modal', function () {
- notEqual($('#modal-test').length, 0, 'modal inserted into dom')
+ assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
done()
})
.bootstrapModal('show')
})
- test('should fire show event', function (assert) {
+ QUnit.test('should fire show event', function (assert) {
+ assert.expect(1)
var done = assert.async()
$('<div id="modal-test"/>')
.on('show.bs.modal', function () {
- ok(true, 'show event fired')
+ assert.ok(true, 'show event fired')
done()
})
.bootstrapModal('show')
})
- test('should not fire shown when show was prevented', function (assert) {
+ QUnit.test('should not fire shown when show was prevented', function (assert) {
+ assert.expect(1)
var done = assert.async()
$('<div id="modal-test"/>')
.on('show.bs.modal', function (e) {
e.preventDefault()
- ok(true, 'show event fired')
+ assert.ok(true, 'show event fired')
done()
})
.on('shown.bs.modal', function () {
- ok(false, 'shown event fired')
+ assert.ok(false, 'shown event fired')
})
.bootstrapModal('show')
})
- test('should hide modal when hide is called', function (assert) {
+ QUnit.test('should hide modal when hide is called', function (assert) {
+ assert.expect(3)
var done = assert.async()
$('<div id="modal-test"/>')
.on('shown.bs.modal', function () {
- ok($('#modal-test').is(':visible'), 'modal visible')
- notEqual($('#modal-test').length, 0, 'modal inserted into dom')
+ assert.ok($('#modal-test').is(':visible'), 'modal visible')
+ assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
$(this).bootstrapModal('hide')
})
.on('hidden.bs.modal', function () {
- ok(!$('#modal-test').is(':visible'), 'modal hidden')
+ assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
done()
})
.bootstrapModal('show')
})
- test('should toggle when toggle is called', function (assert) {
+ QUnit.test('should toggle when toggle is called', function (assert) {
+ assert.expect(3)
var done = assert.async()
$('<div id="modal-test"/>')
.on('shown.bs.modal', function () {
- ok($('#modal-test').is(':visible'), 'modal visible')
- notEqual($('#modal-test').length, 0, 'modal inserted into dom')
+ assert.ok($('#modal-test').is(':visible'), 'modal visible')
+ assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
$(this).bootstrapModal('toggle')
})
.on('hidden.bs.modal', function () {
- ok(!$('#modal-test').is(':visible'), 'modal hidden')
+ assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
done()
})
.bootstrapModal('toggle')
})
- test('should remove from dom when click [data-dismiss="modal"]', function (assert) {
+ QUnit.test('should remove from dom when click [data-dismiss="modal"]', function (assert) {
+ assert.expect(3)
var done = assert.async()
$('<div id="modal-test"><span class="close" data-dismiss="modal"/></div>')
.on('shown.bs.modal', function () {
- ok($('#modal-test').is(':visible'), 'modal visible')
- notEqual($('#modal-test').length, 0, 'modal inserted into dom')
- $(this).find('.close').click()
+ assert.ok($('#modal-test').is(':visible'), 'modal visible')
+ assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
+ $(this).find('.close').trigger('click')
})
.on('hidden.bs.modal', function () {
- ok(!$('#modal-test').is(':visible'), 'modal hidden')
+ assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
done()
})
.bootstrapModal('toggle')
})
- test('should allow modal close with "backdrop:false"', function (assert) {
+ QUnit.test('should allow modal close with "backdrop:false"', function (assert) {
+ assert.expect(2)
var done = assert.async()
$('<div id="modal-test" data-backdrop="false"/>')
.on('shown.bs.modal', function () {
- ok($('#modal-test').is(':visible'), 'modal visible')
+ assert.ok($('#modal-test').is(':visible'), 'modal visible')
$(this).bootstrapModal('hide')
})
.on('hidden.bs.modal', function () {
- ok(!$('#modal-test').is(':visible'), 'modal hidden')
+ assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
done()
})
.bootstrapModal('show')
})
- test('should close modal when clicking outside of modal-content', function (assert) {
+ QUnit.test('should close modal when clicking outside of modal-content', function (assert) {
+ assert.expect(3)
var done = assert.async()
$('<div id="modal-test"><div class="contents"/></div>')
.on('shown.bs.modal', function () {
- notEqual($('#modal-test').length, 0, 'modal insterted into dom')
- $('.contents').click()
- ok($('#modal-test').is(':visible'), 'modal visible')
- $('#modal-test .modal-backdrop').click()
+ assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
+ $('.contents').trigger('click')
+ assert.ok($('#modal-test').is(':visible'), 'modal visible')
+ $('#modal-test').trigger('click')
})
.on('hidden.bs.modal', function () {
- ok(!$('#modal-test').is(':visible'), 'modal hidden')
+ assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
done()
})
.bootstrapModal('show')
})
- test('should close modal when escape key is pressed via keydown', function (assert) {
+ QUnit.test('should close modal when escape key is pressed via keydown', function (assert) {
+ assert.expect(3)
var done = assert.async()
- var div = $('<div id="modal-test"/>')
- div
+ var $div = $('<div id="modal-test"/>')
+ $div
.on('shown.bs.modal', function () {
- ok($('#modal-test').length, 'modal insterted into dom')
- ok($('#modal-test').is(':visible'), 'modal visible')
- div.trigger($.Event('keydown', { which: 27 }))
+ assert.ok($('#modal-test').length, 'modal insterted into dom')
+ assert.ok($('#modal-test').is(':visible'), 'modal visible')
+ $div.trigger($.Event('keydown', { which: 27 }))
setTimeout(function () {
- ok(!$('#modal-test').is(':visible'), 'modal hidden')
- div.remove()
+ assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
+ $div.remove()
done()
}, 0)
})
.bootstrapModal('show')
})
- test('should not close modal when escape key is pressed via keyup', function (assert) {
+ QUnit.test('should not close modal when escape key is pressed via keyup', function (assert) {
+ assert.expect(3)
var done = assert.async()
- var div = $('<div id="modal-test"/>')
- div
+ var $div = $('<div id="modal-test"/>')
+ $div
.on('shown.bs.modal', function () {
- ok($('#modal-test').length, 'modal insterted into dom')
- ok($('#modal-test').is(':visible'), 'modal visible')
- div.trigger($.Event('keyup', { which: 27 }))
+ assert.ok($('#modal-test').length, 'modal inserted into dom')
+ assert.ok($('#modal-test').is(':visible'), 'modal visible')
+ $div.trigger($.Event('keyup', { which: 27 }))
setTimeout(function () {
- ok($('#modal-test').is(':visible'), 'modal still visible')
- div.remove()
+ assert.ok($div.is(':visible'), 'modal still visible')
+ $div.remove()
done()
}, 0)
})
.bootstrapModal('show')
})
- test('should trigger hide event once when clicking outside of modal-content', function (assert) {
+ QUnit.test('should trigger hide event once when clicking outside of modal-content', function (assert) {
+ assert.expect(1)
var done = assert.async()
var triggered
@@ -196,32 +211,33 @@ $(function () {
$('<div id="modal-test"><div class="contents"/></div>')
.on('shown.bs.modal', function () {
triggered = 0
- $('#modal-test .modal-backdrop').click()
+ $('#modal-test').trigger('click')
})
.on('hide.bs.modal', function () {
triggered += 1
- strictEqual(triggered, 1, 'modal hide triggered once')
+ assert.strictEqual(triggered, 1, 'modal hide triggered once')
done()
})
.bootstrapModal('show')
})
- test('should close reopened modal with [data-dismiss="modal"] click', function (assert) {
+ QUnit.test('should close reopened modal with [data-dismiss="modal"] click', function (assert) {
+ assert.expect(2)
var done = assert.async()
$('<div id="modal-test"><div class="contents"><div id="close" data-dismiss="modal"/></div></div>')
.one('shown.bs.modal', function () {
- $('#close').click()
+ $('#close').trigger('click')
})
.one('hidden.bs.modal', function () {
// after one open-close cycle
- ok(!$('#modal-test').is(':visible'), 'modal hidden')
+ assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
$(this)
.one('shown.bs.modal', function () {
- $('#close').click()
+ $('#close').trigger('click')
})
.one('hidden.bs.modal', function () {
- ok(!$('#modal-test').is(':visible'), 'modal hidden')
+ assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
done()
})
.bootstrapModal('show')
@@ -229,7 +245,8 @@ $(function () {
.bootstrapModal('show')
})
- test('should restore focus to toggling element when modal is hidden after having been opened via data-api', function (assert) {
+ QUnit.test('should restore focus to toggling element when modal is hidden after having been opened via data-api', function (assert) {
+ assert.expect(1)
var done = assert.async()
var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture')
@@ -237,19 +254,20 @@ $(function () {
$('<div id="modal-test"><div class="contents"><div id="close" data-dismiss="modal"/></div></div>')
.on('hidden.bs.modal', function () {
setTimeout(function () {
- ok($(document.activeElement).is($toggleBtn), 'toggling element is once again focused')
+ assert.ok($(document.activeElement).is($toggleBtn), 'toggling element is once again focused')
done()
}, 0)
})
.on('shown.bs.modal', function () {
- $('#close').click()
+ $('#close').trigger('click')
})
.appendTo('#qunit-fixture')
- $toggleBtn.click()
+ $toggleBtn.trigger('click')
})
- test('should not restore focus to toggling element if the associated show event gets prevented', function (assert) {
+ QUnit.test('should not restore focus to toggling element if the associated show event gets prevented', function (assert) {
+ assert.expect(1)
var done = assert.async()
var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture')
var $otherBtn = $('<button id="other-btn"/>').appendTo('#qunit-fixture')
@@ -257,22 +275,103 @@ $(function () {
$('<div id="modal-test"><div class="contents"><div id="close" data-dismiss="modal"/></div>')
.one('show.bs.modal', function (e) {
e.preventDefault()
- $otherBtn.focus()
+ $otherBtn.trigger('focus')
setTimeout($.proxy(function () {
$(this).bootstrapModal('show')
}, this), 0)
})
.on('hidden.bs.modal', function () {
setTimeout(function () {
- ok($(document.activeElement).is($otherBtn), 'focus returned to toggling element')
+ assert.ok($(document.activeElement).is($otherBtn), 'focus returned to toggling element')
done()
}, 0)
})
.on('shown.bs.modal', function () {
- $('#close').click()
+ $('#close').trigger('click')
})
.appendTo('#qunit-fixture')
- $toggleBtn.click()
+ $toggleBtn.trigger('click')
+ })
+
+ QUnit.test('should restore inline body padding after closing', function (assert) {
+ assert.expect(2)
+ var done = assert.async()
+ var originalBodyPad = 0
+ var $body = $(document.body)
+
+ $body.css('padding-right', originalBodyPad)
+
+ $('<div id="modal-test"/>')
+ .on('hidden.bs.modal', function () {
+ var currentBodyPad = parseInt($body.css('padding-right'), 10)
+ assert.notStrictEqual($body.attr('style'), '', 'body has non-empty style attribute')
+ assert.strictEqual(currentBodyPad, originalBodyPad, 'original body padding was not changed')
+ $body.removeAttr('style')
+ done()
+ })
+ .on('shown.bs.modal', function () {
+ $(this).bootstrapModal('hide')
+ })
+ .bootstrapModal('show')
+ })
+
+ QUnit.test('should ignore values set via CSS when trying to restore body padding after closing', function (assert) {
+ assert.expect(1)
+ var done = assert.async()
+ var $body = $(document.body)
+ var $style = $('<style>body { padding-right: 42px; }</style>').appendTo('head')
+
+ $('<div id="modal-test"/>')
+ .on('hidden.bs.modal', function () {
+ assert.ok(!$body.attr('style'), 'body does not have inline padding set')
+ $style.remove()
+ done()
+ })
+ .on('shown.bs.modal', function () {
+ $(this).bootstrapModal('hide')
+ })
+ .bootstrapModal('show')
+ })
+
+ QUnit.test('should ignore other inline styles when trying to restore body padding after closing', function (assert) {
+ assert.expect(2)
+ var done = assert.async()
+ var $body = $(document.body)
+ var $style = $('<style>body { padding-right: 42px; }</style>').appendTo('head')
+
+ $body.css('color', 'red')
+
+ $('<div id="modal-test"/>')
+ .on('hidden.bs.modal', function () {
+ assert.strictEqual($body[0].style.paddingRight, '', 'body does not have inline padding set')
+ assert.strictEqual($body[0].style.color, 'red', 'body still has other inline styles set')
+ $body.removeAttr('style')
+ $style.remove()
+ done()
+ })
+ .on('shown.bs.modal', function () {
+ $(this).bootstrapModal('hide')
+ })
+ .bootstrapModal('show')
+ })
+
+ QUnit.test('should properly restore non-pixel inline body padding after closing', function (assert) {
+ assert.expect(1)
+ var done = assert.async()
+ var $body = $(document.body)
+
+ $body.css('padding-right', '5%')
+
+ $('<div id="modal-test"/>')
+ .on('hidden.bs.modal', function () {
+ assert.strictEqual($body[0].style.paddingRight, '5%', 'body does not have inline padding set')
+ $body.removeAttr('style')
+ done()
+ })
+ .on('shown.bs.modal', function () {
+ $(this).bootstrapModal('hide')
+ })
+ .bootstrapModal('show')
})
})
diff --git a/js/tests/unit/phantom.js b/js/tests/unit/phantom.js
index f6f0ac00fd..3ed3b3ea37 100644
--- a/js/tests/unit/phantom.js
+++ b/js/tests/unit/phantom.js
@@ -52,9 +52,9 @@
QUnit.moduleDone(function (obj) {
if (obj.failed === 0) {
- console.log('\r\u2714 All tests passed in "' + obj.name + '" module')
+ console.log('\r\u221A All tests passed in "' + obj.name + '" module')
} else {
- console.log('\u2716 ' + obj.failed + ' tests failed in "' + obj.name + '" module')
+ console.log('\u00D7 ' + obj.failed + ' tests failed in "' + obj.name + '" module')
}
sendMessage('qunit.moduleDone', obj.name, obj.failed, obj.passed, obj.total)
})
diff --git a/js/tests/unit/popover.js b/js/tests/unit/popover.js
index 68525540a5..a25df3a58d 100644
--- a/js/tests/unit/popover.js
+++ b/js/tests/unit/popover.js
@@ -152,7 +152,7 @@ $(function () {
.bootstrapPopover({
title: 'Test',
content: 'Test',
- template: '<div class="popover foobar"><div class="popover-arrow"></div><div class="inner"><h3 class="title"/><div class="content"><p/></div></div></div>'
+ template: '<div class="popover foobar"><div class="arrow"></div><div class="inner"><h3 class="title"/><div class="content"><p/></div></div></div>'
})
$popover.bootstrapPopover('show')
@@ -166,8 +166,7 @@ $(function () {
QUnit.test('should destroy popover', function (assert) {
assert.expect(7)
- var $popover = $('<div>Popover trigger</div>')
- .appendTo('#qunit-fixture')
+ var $popover = $('<div/>')
.bootstrapPopover({
trigger: 'hover'
})
@@ -241,17 +240,6 @@ $(function () {
.bootstrapPopover('show')
})
- QUnit.test('should throw an error when trying to show a popover on a hidden element', function (assert) {
- assert.expect(1)
- var $target = $('<a href="#" title="Another popover" data-content="Body" style="display: none;">I am hidden</a>').appendTo('#qunit-fixture')
-
- assert.throws(function () {
- $target.bootstrapPopover('show')
- }, new Error('Can\'t show a tooltip/popover on a hidden element'))
-
- $target.remove()
- })
-
QUnit.test('should throw an error when initializing popover on the document object without specifying a delegation selector', function (assert) {
assert.expect(1)
assert.throws(function () {
@@ -271,4 +259,32 @@ $(function () {
assert.strictEqual($popover.data('bs.popover'), undefined, 'should not initialize the popover')
})
+ QUnit.test('should throw an error when template contains multiple top-level elements', function (assert) {
+ assert.expect(1)
+ assert.throws(function () {
+ $('<span data-toggle="popover" data-title="some title" data-content="some content">some text</span>')
+ .appendTo('#qunit-fixture')
+ .bootstrapPopover({ template: '<div>Foo</div><div>Bar</div>' })
+ .bootstrapPopover('show')
+ }, new Error('popover `template` option must consist of exactly 1 top-level element!'))
+ })
+
+ QUnit.test('should fire inserted event', function (assert) {
+ assert.expect(2)
+ var done = assert.async()
+
+ $('<a href="#">@Johann-S</a>')
+ .appendTo('#qunit-fixture')
+ .on('inserted.bs.popover', function () {
+ assert.notEqual($('.popover').length, 0, 'popover was inserted')
+ assert.ok(true, 'inserted event fired')
+ done()
+ })
+ .bootstrapPopover({
+ title: 'Test',
+ content: 'Test'
+ })
+ .bootstrapPopover('show')
+ })
+
})
diff --git a/js/tests/unit/tooltip.js b/js/tests/unit/tooltip.js
index e1205b4c0f..27ce6208e7 100644
--- a/js/tests/unit/tooltip.js
+++ b/js/tests/unit/tooltip.js
@@ -1,284 +1,319 @@
$(function () {
'use strict';
- module('tooltip plugin')
+ QUnit.module('tooltip plugin')
- test('should be defined on jquery object', function () {
- ok($(document.body).tooltip, 'tooltip method is defined')
+ QUnit.test('should be defined on jquery object', function (assert) {
+ assert.expect(1)
+ assert.ok($(document.body).tooltip, 'tooltip method is defined')
})
- module('tooltip', {
- setup: function () {
+ QUnit.module('tooltip', {
+ beforeEach: function () {
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
$.fn.bootstrapTooltip = $.fn.tooltip.noConflict()
},
- teardown: function () {
+ afterEach: function () {
$.fn.tooltip = $.fn.bootstrapTooltip
delete $.fn.bootstrapTooltip
}
})
- test('should provide no conflict', function () {
- strictEqual($.fn.tooltip, undefined, 'tooltip was set back to undefined (org value)')
+ QUnit.test('should provide no conflict', function (assert) {
+ assert.expect(1)
+ assert.strictEqual($.fn.tooltip, undefined, 'tooltip was set back to undefined (org value)')
})
- test('should return jquery collection containing the element', function () {
+ QUnit.test('should return jquery collection containing the element', function (assert) {
+ assert.expect(2)
var $el = $('<div/>')
var $tooltip = $el.bootstrapTooltip()
- ok($tooltip instanceof $, 'returns jquery collection')
- strictEqual($tooltip[0], $el[0], 'collection contains element')
+ assert.ok($tooltip instanceof $, 'returns jquery collection')
+ assert.strictEqual($tooltip[0], $el[0], 'collection contains element')
})
- test('should expose default settings', function () {
- ok($.fn.bootstrapTooltip.Constructor.Defaults, 'defaults is defined')
+ QUnit.test('should expose default settings', function (assert) {
+ assert.expect(1)
+ assert.ok($.fn.bootstrapTooltip.Constructor.DEFAULTS, 'defaults is defined')
})
- test('should empty title attribute', function () {
+ QUnit.test('should empty title attribute', function (assert) {
+ assert.expect(1)
var $trigger = $('<a href="#" rel="tooltip" title="Another tooltip"/>').bootstrapTooltip()
- strictEqual($trigger.attr('title'), '', 'title attribute was emptied')
+ assert.strictEqual($trigger.attr('title'), '', 'title attribute was emptied')
})
- test('should add data attribute for referencing original title', function () {
+ QUnit.test('should add data attribute for referencing original title', function (assert) {
+ assert.expect(1)
var $trigger = $('<a href="#" rel="tooltip" title="Another tooltip"/>').bootstrapTooltip()
- strictEqual($trigger.attr('data-original-title'), 'Another tooltip', 'original title preserved in data attribute')
+ assert.strictEqual($trigger.attr('data-original-title'), 'Another tooltip', 'original title preserved in data attribute')
})
- test('should add aria-describedby to the trigger on show', function () {
- var $trigger = $('<a href="#" rel="tooltip" title="Another tooltip">Tooltip trigger</a>')
+ QUnit.test('should add aria-describedby to the trigger on show', function (assert) {
+ assert.expect(3)
+ var $trigger = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
.bootstrapTooltip()
.appendTo('#qunit-fixture')
.bootstrapTooltip('show')
var id = $('.tooltip').attr('id')
- strictEqual($('#' + id).length, 1, 'has a unique id')
- strictEqual($('.tooltip').attr('aria-describedby'), $trigger.attr('id'), 'tooltip id and aria-describedby on trigger match')
- ok($trigger[0].hasAttribute('aria-describedby'), 'trigger has aria-describedby')
+ assert.strictEqual($('#' + id).length, 1, 'has a unique id')
+ assert.strictEqual($('.tooltip').attr('aria-describedby'), $trigger.attr('id'), 'tooltip id and aria-describedby on trigger match')
+ assert.ok($trigger[0].hasAttribute('aria-describedby'), 'trigger has aria-describedby')
})
- test('should remove aria-describedby from trigger on hide', function () {
- var $trigger = $('<a href="#" rel="tooltip" title="Another tooltip">Tooltip trigger</a>')
+ QUnit.test('should remove aria-describedby from trigger on hide', function (assert) {
+ assert.expect(2)
+ var $trigger = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
.bootstrapTooltip()
.appendTo('#qunit-fixture')
$trigger.bootstrapTooltip('show')
- ok($trigger[0].hasAttribute('aria-describedby'), 'trigger has aria-describedby')
+ assert.ok($trigger[0].hasAttribute('aria-describedby'), 'trigger has aria-describedby')
$trigger.bootstrapTooltip('hide')
- ok(!$trigger[0].hasAttribute('aria-describedby'), 'trigger does not have aria-describedby')
+ assert.ok(!$trigger[0].hasAttribute('aria-describedby'), 'trigger does not have aria-describedby')
})
- test('should assign a unique id tooltip element', function () {
- $('<a href="#" rel="tooltip" title="Another tooltip">Tooltip trigger</a>')
+ QUnit.test('should assign a unique id tooltip element', function (assert) {
+ assert.expect(2)
+ $('<a href="#" rel="tooltip" title="Another tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip('show')
var id = $('.tooltip').attr('id')
- strictEqual($('#' + id).length, 1, 'tooltip has unique id')
- strictEqual(id.indexOf('tooltip'), 0, 'tooltip id has prefix')
+ assert.strictEqual($('#' + id).length, 1, 'tooltip has unique id')
+ assert.strictEqual(id.indexOf('tooltip'), 0, 'tooltip id has prefix')
})
- test('should place tooltips relative to placement option', function () {
- var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip">Tooltip trigger</a>')
+ QUnit.test('should place tooltips relative to placement option', function (assert) {
+ assert.expect(2)
+ var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({ placement: 'bottom' })
$tooltip.bootstrapTooltip('show')
- ok($('.tooltip').is('.fade.tooltip-bottom.in'), 'has correct classes applied')
+ assert.ok($('.tooltip').is('.fade.bottom.in'), 'has correct classes applied')
$tooltip.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'tooltip removed')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed')
})
- test('should allow html entities', function () {
- var $tooltip = $('<a href="#" rel="tooltip" title="&lt;b&gt;@fat&lt;/b&gt;">Tooltip trigger</a>')
+ QUnit.test('should allow html entities', function (assert) {
+ assert.expect(2)
+ var $tooltip = $('<a href="#" rel="tooltip" title="&lt;b&gt;@fat&lt;/b&gt;"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({ html: true })
$tooltip.bootstrapTooltip('show')
- notEqual($('.tooltip b').length, 0, 'b tag was inserted')
+ assert.notEqual($('.tooltip b').length, 0, 'b tag was inserted')
$tooltip.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'tooltip removed')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed')
})
- test('should respect custom classes', function () {
- var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip">Tooltip trigger</a>')
+ QUnit.test('should respect custom classes', function (assert) {
+ assert.expect(2)
+ var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({ template: '<div class="tooltip some-class"><div class="tooltip-arrow"/><div class="tooltip-inner"/></div>' })
$tooltip.bootstrapTooltip('show')
- ok($('.tooltip').hasClass('some-class'), 'custom class is present')
+ assert.ok($('.tooltip').hasClass('some-class'), 'custom class is present')
$tooltip.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'tooltip removed')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed')
})
- test('should fire show event', function (assert) {
+ QUnit.test('should fire show event', function (assert) {
+ assert.expect(1)
var done = assert.async()
- $('<div title="tooltip title">Tooltip trigger</div>')
- .appendTo('#qunit-fixture')
+ $('<div title="tooltip title"/>')
.on('show.bs.tooltip', function () {
- ok(true, 'show event fired')
+ assert.ok(true, 'show event fired')
done()
})
.bootstrapTooltip('show')
})
- test('should fire shown event', function (assert) {
+ QUnit.test('should fire inserted event', function (assert) {
+ assert.expect(2)
var done = assert.async()
- $('<div title="tooltip title">Tooltip trigger</div>')
+ $('<div title="tooltip title"/>')
.appendTo('#qunit-fixture')
- .on('shown.bs.tooltip', function () {
- ok(true, 'shown was called')
+ .on('inserted.bs.tooltip', function () {
+ assert.notEqual($('.tooltip').length, 0, 'tooltip was inserted')
+ assert.ok(true, 'inserted event fired')
done()
})
.bootstrapTooltip('show')
})
- test('should not fire shown event when show was prevented', function (assert) {
+ QUnit.test('should fire shown event', function (assert) {
+ assert.expect(1)
var done = assert.async()
- $('<div title="tooltip title">Tooltip trigger</div>')
+ $('<div title="tooltip title"></div>')
.appendTo('#qunit-fixture')
+ .on('shown.bs.tooltip', function () {
+ assert.ok(true, 'shown was called')
+ done()
+ })
+ .bootstrapTooltip('show')
+ })
+
+ QUnit.test('should not fire shown event when show was prevented', function (assert) {
+ assert.expect(1)
+ var done = assert.async()
+
+ $('<div title="tooltip title"/>')
.on('show.bs.tooltip', function (e) {
e.preventDefault()
- ok(true, 'show event fired')
+ assert.ok(true, 'show event fired')
done()
})
.on('shown.bs.tooltip', function () {
- ok(false, 'shown event fired')
+ assert.ok(false, 'shown event fired')
})
.bootstrapTooltip('show')
})
- test('should fire hide event', function (assert) {
+ QUnit.test('should fire hide event', function (assert) {
+ assert.expect(1)
var done = assert.async()
- $('<div title="tooltip title">Tooltip trigger</div>')
+ $('<div title="tooltip title"/>')
.appendTo('#qunit-fixture')
.on('shown.bs.tooltip', function () {
$(this).bootstrapTooltip('hide')
})
.on('hide.bs.tooltip', function () {
- ok(true, 'hide event fired')
+ assert.ok(true, 'hide event fired')
done()
})
.bootstrapTooltip('show')
})
- test('should fire hidden event', function (assert) {
+ QUnit.test('should fire hidden event', function (assert) {
+ assert.expect(1)
var done = assert.async()
- $('<div title="tooltip title">Tooltip trigger</div>')
+ $('<div title="tooltip title"/>')
.appendTo('#qunit-fixture')
.on('shown.bs.tooltip', function () {
$(this).bootstrapTooltip('hide')
})
.on('hidden.bs.tooltip', function () {
- ok(true, 'hidden event fired')
+ assert.ok(true, 'hidden event fired')
done()
})
.bootstrapTooltip('show')
})
- test('should not fire hidden event when hide was prevented', function (assert) {
+ QUnit.test('should not fire hidden event when hide was prevented', function (assert) {
+ assert.expect(1)
var done = assert.async()
- $('<div title="tooltip title">Tooltip trigger</div>')
+ $('<div title="tooltip title"/>')
.appendTo('#qunit-fixture')
.on('shown.bs.tooltip', function () {
$(this).bootstrapTooltip('hide')
})
.on('hide.bs.tooltip', function (e) {
e.preventDefault()
- ok(true, 'hide event fired')
+ assert.ok(true, 'hide event fired')
done()
})
.on('hidden.bs.tooltip', function () {
- ok(false, 'hidden event fired')
+ assert.ok(false, 'hidden event fired')
})
.bootstrapTooltip('show')
})
- test('should destroy tooltip', function () {
- var $tooltip = $('<div>Tooltip trigger</div>')
- .appendTo('#qunit-fixture')
+ QUnit.test('should destroy tooltip', function (assert) {
+ assert.expect(7)
+ var $tooltip = $('<div/>')
.bootstrapTooltip()
.on('click.foo', function () {})
- ok($tooltip.data('bs.tooltip'), 'tooltip has data')
- ok($._data($tooltip[0], 'events').mouseover && $._data($tooltip[0], 'events').mouseout, 'tooltip has hover events')
- equal($._data($tooltip[0], 'events').click[0].namespace, 'foo', 'tooltip has extra click.foo event')
+ assert.ok($tooltip.data('bs.tooltip'), 'tooltip has data')
+ assert.ok($._data($tooltip[0], 'events').mouseover && $._data($tooltip[0], 'events').mouseout, 'tooltip has hover events')
+ assert.strictEqual($._data($tooltip[0], 'events').click[0].namespace, 'foo', 'tooltip has extra click.foo event')
$tooltip.bootstrapTooltip('show')
$tooltip.bootstrapTooltip('destroy')
- ok(!$tooltip.hasClass('in'), 'tooltip is hidden')
- ok(!$._data($tooltip[0], 'bs.tooltip'), 'tooltip does not have data')
- equal($._data($tooltip[0], 'events').click[0].namespace, 'foo', 'tooltip still has click.foo')
- ok(!$._data($tooltip[0], 'events').mouseover && !$._data($tooltip[0], 'events').mouseout, 'tooltip does not have hover events')
+ assert.ok(!$tooltip.hasClass('in'), 'tooltip is hidden')
+ assert.ok(!$._data($tooltip[0], 'bs.tooltip'), 'tooltip does not have data')
+ assert.strictEqual($._data($tooltip[0], 'events').click[0].namespace, 'foo', 'tooltip still has click.foo')
+ assert.ok(!$._data($tooltip[0], 'events').mouseover && !$._data($tooltip[0], 'events').mouseout, 'tooltip does not have hover events')
})
- test('should show tooltip with delegate selector on click', function () {
- var $div = $('<div><a href="#" rel="tooltip" title="Another tooltip">Tooltip trigger</a></div>')
+ QUnit.test('should show tooltip with delegate selector on click', function (assert) {
+ assert.expect(2)
+ var $div = $('<div><a href="#" rel="tooltip" title="Another tooltip"/></div>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({
selector: 'a[rel="tooltip"]',
trigger: 'click'
})
- $div.find('a').click()
- ok($('.tooltip').is('.fade.in'), 'tooltip is faded in')
+ $div.find('a').trigger('click')
+ assert.ok($('.tooltip').is('.fade.in'), 'tooltip is faded in')
- $div.find('a').click()
- equal($('.tooltip').length, 0, 'tooltip was removed from dom')
+ $div.find('a').trigger('click')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip was removed from dom')
})
- test('should show tooltip when toggle is called', function () {
- $('<a href="#" rel="tooltip" title="tooltip on toggle">Tooltip trigger</a>')
+ QUnit.test('should show tooltip when toggle is called', function (assert) {
+ assert.expect(1)
+ $('<a href="#" rel="tooltip" title="tooltip on toggle"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({ trigger: 'manual' })
.bootstrapTooltip('toggle')
- ok($('.tooltip').is('.fade.in'), 'tooltip is faded in')
+ assert.ok($('.tooltip').is('.fade.in'), 'tooltip is faded in')
})
- test('should hide previously shown tooltip when toggle is called on tooltip', function () {
+ QUnit.test('should hide previously shown tooltip when toggle is called on tooltip', function (assert) {
+ assert.expect(1)
$('<a href="#" rel="tooltip" title="tooltip on toggle">@ResentedHook</a>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({ trigger: 'manual' })
.bootstrapTooltip('show')
$('.tooltip').bootstrapTooltip('toggle')
- ok($('.tooltip').not('.fade.in'), 'tooltip was faded out')
+ assert.ok($('.tooltip').not('.fade.in'), 'tooltip was faded out')
})
- test('should place tooltips inside body when container is body', function () {
- var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip">Tooltip trigger</a>')
+ QUnit.test('should place tooltips inside body when container is body', function (assert) {
+ assert.expect(3)
+ var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({ container: 'body' })
.bootstrapTooltip('show')
- notEqual($('body > .tooltip').length, 0, 'tooltip is direct descendant of body')
- equal($('#qunit-fixture > .tooltip').length, 0, 'tooltip is not in parent')
+ assert.notEqual($('body > .tooltip').length, 0, 'tooltip is direct descendant of body')
+ assert.strictEqual($('#qunit-fixture > .tooltip').length, 0, 'tooltip is not in parent')
$tooltip.bootstrapTooltip('hide')
- equal($('body > .tooltip').length, 0, 'tooltip was removed from dom')
+ assert.strictEqual($('body > .tooltip').length, 0, 'tooltip was removed from dom')
})
- test('should add position class before positioning so that position-specific styles are taken into account', function () {
+ QUnit.test('should add position class before positioning so that position-specific styles are taken into account', function (assert) {
+ assert.expect(1)
var styles = '<style>'
- + '.tooltip.tooltip-right { white-space: nowrap; }'
- + '.tooltip.tooltip-right .tooltip-inner { max-width: none; }'
+ + '.tooltip.right { white-space: nowrap; }'
+ + '.tooltip.right .tooltip-inner { max-width: none; }'
+ '</style>'
var $styles = $(styles).appendTo('head')
var $container = $('<div/>').appendTo('#qunit-fixture')
- var $target = $('<a href="#" rel="tooltip" title="very very very very very very very very long tooltip in one line">m</a>')
+ var $target = $('<a href="#" rel="tooltip" title="very very very very very very very very long tooltip in one line"/>')
.appendTo($container)
.bootstrapTooltip({
placement: 'right',
@@ -291,101 +326,102 @@ $(function () {
var top = Math.round($target.offset().top + ($target[0].offsetHeight / 2) - ($tooltip[0].offsetHeight / 2))
var top2 = Math.round($tooltip.offset().top)
var topDiff = top - top2
- ok(topDiff <= 1 && topDiff >= -1)
+ assert.ok(topDiff <= 1 && topDiff >= -1)
$target.bootstrapTooltip('hide')
$container.remove()
$styles.remove()
})
- test('should use title attribute for tooltip text', function () {
- var $tooltip = $('<a href="#" rel="tooltip" title="Simple tooltip">Tooltip trigger</a>')
+ QUnit.test('should use title attribute for tooltip text', function (assert) {
+ assert.expect(2)
+ var $tooltip = $('<a href="#" rel="tooltip" title="Simple tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip()
$tooltip.bootstrapTooltip('show')
- equal($('.tooltip').children('.tooltip-inner').text(), 'Simple tooltip', 'title from title attribute is set')
+ assert.strictEqual($('.tooltip').children('.tooltip-inner').text(), 'Simple tooltip', 'title from title attribute is set')
$tooltip.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'tooltip removed from dom')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
})
- test('should prefer title attribute over title option', function () {
- var $tooltip = $('<a href="#" rel="tooltip" title="Simple tooltip">Tooltip trigger</a>')
+ QUnit.test('should prefer title attribute over title option', function (assert) {
+ assert.expect(2)
+ var $tooltip = $('<a href="#" rel="tooltip" title="Simple tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({
title: 'This is a tooltip with some content'
})
$tooltip.bootstrapTooltip('show')
- equal($('.tooltip').children('.tooltip-inner').text(), 'Simple tooltip', 'title is set from title attribute while preferred over title option')
+ assert.strictEqual($('.tooltip').children('.tooltip-inner').text(), 'Simple tooltip', 'title is set from title attribute while preferred over title option')
$tooltip.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'tooltip removed from dom')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
})
- test('should use title option', function () {
- var $tooltip = $('<a href="#" rel="tooltip">Tooltip trigger</a>')
+ QUnit.test('should use title option', function (assert) {
+ assert.expect(2)
+ var $tooltip = $('<a href="#" rel="tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({
title: 'This is a tooltip with some content'
})
$tooltip.bootstrapTooltip('show')
- equal($('.tooltip').children('.tooltip-inner').text(), 'This is a tooltip with some content', 'title from title option is set')
+ assert.strictEqual($('.tooltip').children('.tooltip-inner').text(), 'This is a tooltip with some content', 'title from title option is set')
$tooltip.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'tooltip removed from dom')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
})
- test('should be placed dynamically with the dynamic placement option', function () {
- var $style = $('<style> a[rel="tooltip"] { display: inline-block; position: absolute; } </style>')
+ QUnit.test('should be placed dynamically to viewport with the dynamic placement option', function (assert) {
+ assert.expect(6)
+ var $style = $('<style> div[rel="tooltip"] { position: absolute; } #qunit-fixture { top: inherit; left: inherit } </style>').appendTo('head')
var $container = $('<div/>')
.css({
- position: 'absolute',
- overflow: 'hidden',
- width: 600,
- height: 400,
- top: 0,
- left: 0
+ position: 'relative',
+ height: '100%'
})
- .appendTo(document.body)
+ .appendTo('#qunit-fixture')
var $topTooltip = $('<div style="left: 0; top: 0;" rel="tooltip" title="Top tooltip">Top Dynamic Tooltip</div>')
.appendTo($container)
- .bootstrapTooltip({ placement: 'auto' })
+ .bootstrapTooltip({ placement: 'auto', viewport: '#qunit-fixture' })
$topTooltip.bootstrapTooltip('show')
- ok($('.tooltip').is('.tooltip-bottom'), 'top positioned tooltip is dynamically positioned to bottom')
+ assert.ok($('.tooltip').is('.bottom'), 'top positioned tooltip is dynamically positioned to bottom')
$topTooltip.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'top positioned tooltip removed from dom')
+ assert.strictEqual($('.tooltip').length, 0, 'top positioned tooltip removed from dom')
var $rightTooltip = $('<div style="right: 0;" rel="tooltip" title="Right tooltip">Right Dynamic Tooltip</div>')
.appendTo($container)
- .bootstrapTooltip({ placement: 'right auto' })
+ .bootstrapTooltip({ placement: 'right auto', viewport: '#qunit-fixture' })
$rightTooltip.bootstrapTooltip('show')
- ok($('.tooltip').is('.tooltip-left'), 'right positioned tooltip is dynamically positioned left')
+ assert.ok($('.tooltip').is('.left'), 'right positioned tooltip is dynamically positioned left')
$rightTooltip.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'right positioned tooltip removed from dom')
+ assert.strictEqual($('.tooltip').length, 0, 'right positioned tooltip removed from dom')
var $leftTooltip = $('<div style="left: 0;" rel="tooltip" title="Left tooltip">Left Dynamic Tooltip</div>')
.appendTo($container)
- .bootstrapTooltip({ placement: 'auto left' })
+ .bootstrapTooltip({ placement: 'auto left', viewport: '#qunit-fixture' })
$leftTooltip.bootstrapTooltip('show')
- ok($('.tooltip').is('.tooltip-right'), 'left positioned tooltip is dynamically positioned right')
+ assert.ok($('.tooltip').is('.right'), 'left positioned tooltip is dynamically positioned right')
$leftTooltip.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'left positioned tooltip removed from dom')
+ assert.strictEqual($('.tooltip').length, 0, 'left positioned tooltip removed from dom')
$container.remove()
$style.remove()
})
- test('should position tip on top if viewport has enough space and placement is "auto top"', function () {
+ QUnit.test('should position tip on top if viewport has enough space and placement is "auto top"', function (assert) {
+ assert.expect(2)
var styles = '<style>'
+ 'body { padding-top: 100px; }'
+ '#section { height: 300px; border: 1px solid red; padding-top: 50px }'
@@ -402,15 +438,41 @@ $(function () {
})
$target.bootstrapTooltip('show')
- ok($('.tooltip').is('.tooltip-top'), 'top positioned tooltip is dynamically positioned to top')
+ assert.ok($('.tooltip').is('.top'), 'top positioned tooltip is dynamically positioned to top')
$target.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'tooltip removed from dom')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
$styles.remove()
})
- test('should position tip on bottom if the tip\'s dimension exceeds the viewport area and placement is "auto top"', function () {
+ QUnit.test('should position tip on top if viewport has enough space and is not parent', function (assert) {
+ assert.expect(2)
+ var styles = '<style>'
+ + '#section { height: 300px; border: 1px solid red; margin-top: 100px; }'
+ + 'div[rel="tooltip"] { width: 150px; border: 1px solid blue; }'
+ + '</style>'
+ var $styles = $(styles).appendTo('head')
+
+ var $container = $('<div id="section"/>').appendTo('#qunit-fixture')
+ var $target = $('<div rel="tooltip" title="tip"/>')
+ .appendTo($container)
+ .bootstrapTooltip({
+ placement: 'auto top',
+ viewport: '#qunit-fixture'
+ })
+
+ $target.bootstrapTooltip('show')
+ assert.ok($('.tooltip').is('.top'), 'top positioned tooltip is dynamically positioned to top')
+
+ $target.bootstrapTooltip('hide')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
+
+ $styles.remove()
+ })
+
+ QUnit.test('should position tip on bottom if the tip\'s dimension exceeds the viewport area and placement is "auto top"', function (assert) {
+ assert.expect(2)
var styles = '<style>'
+ 'body { padding-top: 100px; }'
+ '#section { height: 300px; border: 1px solid red; }'
@@ -427,15 +489,16 @@ $(function () {
})
$target.bootstrapTooltip('show')
- ok($('.tooltip').is('.tooltip-bottom'), 'top positioned tooltip is dynamically positioned to bottom')
+ assert.ok($('.tooltip').is('.bottom'), 'top positioned tooltip is dynamically positioned to bottom')
$target.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'tooltip removed from dom')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
$styles.remove()
})
- test('should display the tip on top whenever scrollable viewport has enough room if the given placement is "auto top"', function () {
+ QUnit.test('should display the tip on top whenever scrollable viewport has enough room if the given placement is "auto top"', function (assert) {
+ assert.expect(2)
var styles = '<style>'
+ '#scrollable-div { height: 200px; overflow: auto; }'
+ '.tooltip-item { margin: 200px 0 400px; width: 150px; }'
@@ -453,15 +516,16 @@ $(function () {
$('#scrollable-div').scrollTop(100)
$target.bootstrapTooltip('show')
- ok($('.tooltip').is('.fade.tooltip-top.in'), 'has correct classes applied')
+ assert.ok($('.tooltip').is('.fade.top.in'), 'has correct classes applied')
$target.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'tooltip removed from dom')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
$styles.remove()
})
- test('should display the tip on bottom whenever scrollable viewport doesn\'t have enough room if the given placement is "auto top"', function () {
+ QUnit.test('should display the tip on bottom whenever scrollable viewport doesn\'t have enough room if the given placement is "auto top"', function (assert) {
+ assert.expect(2)
var styles = '<style>'
+ '#scrollable-div { height: 200px; overflow: auto; }'
+ '.tooltip-item { padding: 200px 0 400px; width: 150px; }'
@@ -479,15 +543,16 @@ $(function () {
$('#scrollable-div').scrollTop(200)
$target.bootstrapTooltip('show')
- ok($('.tooltip').is('.fade.tooltip-bottom.in'), 'has correct classes applied')
+ assert.ok($('.tooltip').is('.fade.bottom.in'), 'has correct classes applied')
$target.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'tooltip removed from dom')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
$styles.remove()
})
- test('should display the tip on bottom whenever scrollable viewport has enough room if the given placement is "auto bottom"', function () {
+ QUnit.test('should display the tip on bottom whenever scrollable viewport has enough room if the given placement is "auto bottom"', function (assert) {
+ assert.expect(2)
var styles = '<style>'
+ '#scrollable-div { height: 200px; overflow: auto; }'
+ '.spacer { height: 400px; }'
@@ -509,15 +574,16 @@ $(function () {
$('#scrollable-div').scrollTop(200)
$target.bootstrapTooltip('show')
- ok($('.tooltip').is('.fade.tooltip-bottom.in'), 'has correct classes applied')
+ assert.ok($('.tooltip').is('.fade.bottom.in'), 'has correct classes applied')
$target.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'tooltip removed from dom')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
$styles.remove()
})
- test('should display the tip on top whenever scrollable viewport doesn\'t have enough room if the given placement is "auto bottom"', function () {
+ QUnit.test('should display the tip on top whenever scrollable viewport doesn\'t have enough room if the given placement is "auto bottom"', function (assert) {
+ assert.expect(2)
var styles = '<style>'
+ '#scrollable-div { height: 200px; overflow: auto; }'
+ '.tooltip-item { margin-top: 400px; width: 150px; }'
@@ -535,15 +601,16 @@ $(function () {
$('#scrollable-div').scrollTop(400)
$target.bootstrapTooltip('show')
- ok($('.tooltip').is('.fade.tooltip-top.in'), 'has correct classes applied')
+ assert.ok($('.tooltip').is('.fade.top.in'), 'has correct classes applied')
$target.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'tooltip removed from dom')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
$styles.remove()
})
- test('should adjust the tip\'s top position when up against the top of the viewport', function () {
+ QUnit.test('should adjust the tip\'s top position when up against the top of the viewport', function (assert) {
+ assert.expect(2)
var styles = '<style>'
+ '.tooltip .tooltip-inner { width: 200px; height: 200px; max-width: none; }'
+ 'a[rel="tooltip"] { position: fixed; }'
@@ -551,7 +618,7 @@ $(function () {
var $styles = $(styles).appendTo('head')
var $container = $('<div/>').appendTo('#qunit-fixture')
- var $target = $('<a href="#" rel="tooltip" title="tip" style="top: 0px; left: 0px;">m</a>')
+ var $target = $('<a href="#" rel="tooltip" title="tip" style="top: 0px; left: 0px;"/>')
.appendTo($container)
.bootstrapTooltip({
placement: 'right',
@@ -562,15 +629,16 @@ $(function () {
})
$target.bootstrapTooltip('show')
- equal(Math.round($container.find('.tooltip').offset().top), 12)
+ assert.strictEqual(Math.round($container.find('.tooltip').offset().top), 12)
$target.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'tooltip removed from dom')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
$styles.remove()
})
- test('should adjust the tip\'s top position when up against the bottom of the viewport', function () {
+ QUnit.test('should adjust the tip\'s top position when up against the bottom of the viewport', function (assert) {
+ assert.expect(2)
var styles = '<style>'
+ '.tooltip .tooltip-inner { width: 200px; height: 200px; max-width: none; }'
+ 'a[rel="tooltip"] { position: fixed; }'
@@ -578,7 +646,7 @@ $(function () {
var $styles = $(styles).appendTo('head')
var $container = $('<div/>').appendTo('#qunit-fixture')
- var $target = $('<a href="#" rel="tooltip" title="tip" style="bottom: 0px; left: 0px;">m</a>')
+ var $target = $('<a href="#" rel="tooltip" title="tip" style="bottom: 0px; left: 0px;"/>')
.appendTo($container)
.bootstrapTooltip({
placement: 'right',
@@ -590,16 +658,17 @@ $(function () {
$target.bootstrapTooltip('show')
var $tooltip = $container.find('.tooltip')
- strictEqual(Math.round($tooltip.offset().top), Math.round($(window).height() - 12 - $tooltip[0].offsetHeight))
+ assert.strictEqual(Math.round($tooltip.offset().top), Math.round($(window).height() - 12 - $tooltip[0].offsetHeight))
$target.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'tooltip removed from dom')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
$container.remove()
$styles.remove()
})
- test('should adjust the tip\'s left position when up against the left of the viewport', function () {
+ QUnit.test('should adjust the tip\'s left position when up against the left of the viewport', function (assert) {
+ assert.expect(2)
var styles = '<style>'
+ '.tooltip .tooltip-inner { width: 200px; height: 200px; max-width: none; }'
+ 'a[rel="tooltip"] { position: fixed; }'
@@ -607,7 +676,7 @@ $(function () {
var $styles = $(styles).appendTo('head')
var $container = $('<div/>').appendTo('#qunit-fixture')
- var $target = $('<a href="#" rel="tooltip" title="tip" style="top: 0px; left: 0px;">m</a>')
+ var $target = $('<a href="#" rel="tooltip" title="tip" style="top: 0px; left: 0px;"/>')
.appendTo($container)
.bootstrapTooltip({
placement: 'bottom',
@@ -618,16 +687,17 @@ $(function () {
})
$target.bootstrapTooltip('show')
- strictEqual(Math.round($container.find('.tooltip').offset().left), 12)
+ assert.strictEqual(Math.round($container.find('.tooltip').offset().left), 12)
$target.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'tooltip removed from dom')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
$container.remove()
$styles.remove()
})
- test('should adjust the tip\'s left position when up against the right of the viewport', function () {
+ QUnit.test('should adjust the tip\'s left position when up against the right of the viewport', function (assert) {
+ assert.expect(2)
var styles = '<style>'
+ '.tooltip .tooltip-inner { width: 200px; height: 200px; max-width: none; }'
+ 'a[rel="tooltip"] { position: fixed; }'
@@ -635,7 +705,7 @@ $(function () {
var $styles = $(styles).appendTo('head')
var $container = $('<div/>').appendTo('body')
- var $target = $('<a href="#" rel="tooltip" title="tip" style="top: 0px; right: 0px;">m</a>')
+ var $target = $('<a href="#" rel="tooltip" title="tip" style="top: 0px; right: 0px;"/>')
.appendTo($container)
.bootstrapTooltip({
placement: 'bottom',
@@ -647,16 +717,17 @@ $(function () {
$target.bootstrapTooltip('show')
var $tooltip = $container.find('.tooltip')
- strictEqual(Math.round($tooltip.offset().left), Math.round($(window).width() - 12 - $tooltip[0].offsetWidth))
+ assert.strictEqual(Math.round($tooltip.offset().left), Math.round($(window).width() - 12 - $tooltip[0].offsetWidth))
$target.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'tooltip removed from dom')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
$container.remove()
$styles.remove()
})
- test('should adjust the tip when up against the right of an arbitrary viewport', function () {
+ QUnit.test('should adjust the tip when up against the right of an arbitrary viewport', function (assert) {
+ assert.expect(2)
var styles = '<style>'
+ '.tooltip, .tooltip .tooltip-inner { width: 200px; height: 200px; max-width: none; }'
+ '.container-viewport { position: absolute; top: 50px; left: 60px; width: 300px; height: 300px; }'
@@ -665,7 +736,7 @@ $(function () {
var $styles = $(styles).appendTo('head')
var $container = $('<div class="container-viewport"/>').appendTo(document.body)
- var $target = $('<a href="#" rel="tooltip" title="tip" style="top: 50px; left: 350px;">m</a>')
+ var $target = $('<a href="#" rel="tooltip" title="tip" style="top: 50px; left: 350px;"/>')
.appendTo($container)
.bootstrapTooltip({
placement: 'bottom',
@@ -674,18 +745,78 @@ $(function () {
$target.bootstrapTooltip('show')
var $tooltip = $container.find('.tooltip')
- strictEqual(Math.round($tooltip.offset().left), Math.round(60 + $container.width() - $tooltip[0].offsetWidth))
+ assert.strictEqual(Math.round($tooltip.offset().left), Math.round(60 + $container.width() - $tooltip[0].offsetWidth))
+
+ $target.bootstrapTooltip('hide')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
+
+ $container.remove()
+ $styles.remove()
+ })
+
+ QUnit.test('should get viewport element from function', function (assert) {
+ assert.expect(3)
+ var styles = '<style>'
+ + '.tooltip, .tooltip .tooltip-inner { width: 200px; height: 200px; max-width: none; }'
+ + '.container-viewport { position: absolute; top: 50px; left: 60px; width: 300px; height: 300px; }'
+ + 'a[rel="tooltip"] { position: fixed; }'
+ + '</style>'
+ var $styles = $(styles).appendTo('head')
+
+ var $container = $('<div class="container-viewport"/>').appendTo(document.body)
+ var $target = $('<a href="#" rel="tooltip" title="tip" style="top: 50px; left: 350px;"/>').appendTo($container)
+ $target
+ .bootstrapTooltip({
+ placement: 'bottom',
+ viewport: function ($element) {
+ assert.strictEqual($element[0], $target[0], 'viewport function was passed target as argument')
+ return ($element.closest('.container-viewport'))
+ }
+ })
+
+ $target.bootstrapTooltip('show')
+ var $tooltip = $container.find('.tooltip')
+ assert.strictEqual(Math.round($tooltip.offset().left), Math.round(60 + $container.width() - $tooltip[0].offsetWidth))
+
+ $target.bootstrapTooltip('hide')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
+
+ $container.remove()
+ $styles.remove()
+ })
+
+ QUnit.test('should not misplace the tip when the right edge offset is greater or equal than the viewport width', function (assert) {
+ assert.expect(2)
+ var styles = '<style>'
+ + '.tooltip, .tooltip *, .tooltip *:before, .tooltip *:after { box-sizing: border-box; }'
+ + '.container-viewport, .container-viewport *, .container-viewport *:before, .container-viewport *:after { box-sizing: border-box; }'
+ + '.tooltip, .tooltip .tooltip-inner { width: 50px; height: 50px; max-width: none; background: red; }'
+ + '.container-viewport { padding: 100px; margin-left: 100px; width: 100px; }'
+ + '</style>'
+ var $styles = $(styles).appendTo('head')
+
+ var $container = $('<div class="container-viewport"/>').appendTo(document.body)
+ var $target = $('<a href="#" rel="tooltip" title="tip">foobar</a>')
+ .appendTo($container)
+ .bootstrapTooltip({
+ viewport: '.container-viewport'
+ })
+
+ $target.bootstrapTooltip('show')
+ var $tooltip = $container.find('.tooltip')
+ assert.strictEqual(Math.round($tooltip.offset().left), Math.round($target.position().left + $target.width() / 2 - $tooltip[0].offsetWidth / 2))
$target.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'tooltip removed from dom')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
$container.remove()
$styles.remove()
})
- test('should not error when trying to show an auto-placed tooltip that has been removed from the dom', function () {
+ QUnit.test('should not error when trying to show an auto-placed tooltip that has been removed from the dom', function (assert) {
+ assert.expect(1)
var passed = true
- var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip">Tooltip trigger</a>')
+ var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
.appendTo('#qunit-fixture')
.one('show.bs.tooltip', function () {
$(this).remove()
@@ -699,10 +830,11 @@ $(function () {
console.log(err)
}
- ok(passed, '.tooltip(\'show\') should not throw an error if element no longer is in dom')
+ assert.ok(passed, '.tooltip(\'show\') should not throw an error if element no longer is in dom')
})
- test('should place tooltip on top of element', function (assert) {
+ QUnit.test('should place tooltip on top of element', function (assert) {
+ assert.expect(1)
var done = assert.async()
var containerHTML = '<div>'
@@ -734,12 +866,13 @@ $(function () {
var $tooltip = $container.find('.tooltip')
setTimeout(function () {
- ok(Math.round($tooltip.offset().top + $tooltip.outerHeight()) <= Math.round($trigger.offset().top))
+ assert.ok(Math.round($tooltip.offset().top + $tooltip.outerHeight()) <= Math.round($trigger.offset().top))
done()
}, 0)
})
- test('should place tooltip inside viewport', function (assert) {
+ QUnit.test('should place tooltip inside viewport', function (assert) {
+ assert.expect(1)
var done = assert.async()
var $container = $('<div/>')
@@ -765,68 +898,71 @@ $(function () {
.bootstrapTooltip('show')
setTimeout(function () {
- ok($('.tooltip').offset().left >= 0)
+ assert.ok($('.tooltip').offset().left >= 0)
done()
}, 0)
})
- test('should show tooltip if leave event hasn\'t occurred before delay expires', function (assert) {
+ QUnit.test('should show tooltip if leave event hasn\'t occurred before delay expires', function (assert) {
+ assert.expect(2)
var done = assert.async()
- var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip">Tooltip trigger</a>')
+ var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({ delay: 150 })
setTimeout(function () {
- ok(!$('.tooltip').is('.fade.in'), '100ms: tooltip is not faded in')
+ assert.ok(!$('.tooltip').is('.fade.in'), '100ms: tooltip is not faded in')
}, 100)
setTimeout(function () {
- ok($('.tooltip').is('.fade.in'), '200ms: tooltip is faded in')
+ assert.ok($('.tooltip').is('.fade.in'), '200ms: tooltip is faded in')
done()
}, 200)
$tooltip.trigger('mouseenter')
})
- test('should not show tooltip if leave event occurs before delay expires', function (assert) {
+ QUnit.test('should not show tooltip if leave event occurs before delay expires', function (assert) {
+ assert.expect(2)
var done = assert.async()
- var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip">Tooltip trigger</a>')
+ var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({ delay: 150 })
setTimeout(function () {
- ok(!$('.tooltip').is('.fade.in'), '100ms: tooltip not faded in')
+ assert.ok(!$('.tooltip').is('.fade.in'), '100ms: tooltip not faded in')
$tooltip.trigger('mouseout')
}, 100)
setTimeout(function () {
- ok(!$('.tooltip').is('.fade.in'), '200ms: tooltip not faded in')
+ assert.ok(!$('.tooltip').is('.fade.in'), '200ms: tooltip not faded in')
done()
}, 200)
$tooltip.trigger('mouseenter')
})
- test('should not hide tooltip if leave event occurs and enter event occurs within the hide delay', function (assert) {
+ QUnit.test('should not hide tooltip if leave event occurs and enter event occurs within the hide delay', function (assert) {
+ assert.expect(3)
var done = assert.async()
- var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip">Tooltip trigger</a>')
+ var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({ delay: { show: 0, hide: 150 }})
setTimeout(function () {
- ok($('.tooltip').is('.fade.in'), '1ms: tooltip faded in')
+ assert.ok($('.tooltip').is('.fade.in'), '1ms: tooltip faded in')
$tooltip.trigger('mouseout')
setTimeout(function () {
- ok($('.tooltip').is('.fade.in'), '100ms: tooltip still faded in')
+ assert.ok($('.tooltip').is('.fade.in'), '100ms: tooltip still faded in')
$tooltip.trigger('mouseenter')
}, 100)
setTimeout(function () {
- ok($('.tooltip').is('.fade.in'), '200ms: tooltip still faded in')
+ assert.ok($('.tooltip').is('.fade.in'), '200ms: tooltip still faded in')
done()
}, 200)
}, 0)
@@ -834,65 +970,68 @@ $(function () {
$tooltip.trigger('mouseenter')
})
- test('should not show tooltip if leave event occurs before delay expires', function (assert) {
+ QUnit.test('should not show tooltip if leave event occurs before delay expires', function (assert) {
+ assert.expect(2)
var done = assert.async()
- var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip">Tooltip trigger</a>')
+ var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({ delay: 150 })
setTimeout(function () {
- ok(!$('.tooltip').is('.fade.in'), '100ms: tooltip not faded in')
+ assert.ok(!$('.tooltip').is('.fade.in'), '100ms: tooltip not faded in')
$tooltip.trigger('mouseout')
}, 100)
setTimeout(function () {
- ok(!$('.tooltip').is('.fade.in'), '200ms: tooltip not faded in')
+ assert.ok(!$('.tooltip').is('.fade.in'), '200ms: tooltip not faded in')
done()
}, 200)
$tooltip.trigger('mouseenter')
})
- test('should not show tooltip if leave event occurs before delay expires, even if hide delay is 0', function (assert) {
+ QUnit.test('should not show tooltip if leave event occurs before delay expires, even if hide delay is 0', function (assert) {
+ assert.expect(2)
var done = assert.async()
- var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip">Tooltip trigger</a>')
+ var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({ delay: { show: 150, hide: 0 }})
setTimeout(function () {
- ok(!$('.tooltip').is('.fade.in'), '100ms: tooltip not faded in')
+ assert.ok(!$('.tooltip').is('.fade.in'), '100ms: tooltip not faded in')
$tooltip.trigger('mouseout')
}, 100)
setTimeout(function () {
- ok(!$('.tooltip').is('.fade.in'), '250ms: tooltip not faded in')
+ assert.ok(!$('.tooltip').is('.fade.in'), '250ms: tooltip not faded in')
done()
}, 250)
$tooltip.trigger('mouseenter')
})
- test('should wait 200ms before hiding the tooltip', function (assert) {
+ QUnit.test('should wait 200ms before hiding the tooltip', function (assert) {
+ assert.expect(3)
var done = assert.async()
- var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip">Tooltip trigger</a>')
+ var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({ delay: { show: 0, hide: 150 }})
setTimeout(function () {
- ok($('.tooltip').is('.fade.in'), '1ms: tooltip faded in')
+ assert.ok($tooltip.data('bs.tooltip').$tip.is('.fade.in'), '1ms: tooltip faded in')
$tooltip.trigger('mouseout')
setTimeout(function () {
- ok($('.tooltip').is('.fade.in'), '100ms: tooltip still faded in')
+ assert.ok($tooltip.data('bs.tooltip').$tip.is('.fade.in'), '100ms: tooltip still faded in')
}, 100)
setTimeout(function () {
- ok(!$('.tooltip').is('.in'), '200ms: tooltip removed')
- start()
+ assert.ok(!$tooltip.data('bs.tooltip').$tip.is('.in'), '200ms: tooltip removed')
+ done()
}, 200)
}, 0)
@@ -900,12 +1039,13 @@ $(function () {
$tooltip.trigger('mouseenter')
})
- test('should correctly position tooltips on SVG elements', function (assert) {
+ QUnit.test('should correctly position tooltips on SVG elements', function (assert) {
if (!window.SVGElement) {
// Skip IE8 since it doesn't support SVG
- expect(0)
+ assert.expect(0)
return
}
+ assert.expect(2)
var done = assert.async()
@@ -928,9 +1068,9 @@ $(function () {
.on('shown.bs.tooltip', function () {
var offset = $('.tooltip').offset()
$styles.remove()
- ok(Math.abs(offset.left - 88) <= 1, 'tooltip has correct horizontal location')
+ assert.ok(Math.abs(offset.left - 88) <= 1, 'tooltip has correct horizontal location')
$circle.bootstrapTooltip('hide')
- equal($('.tooltip').length, 0, 'tooltip removed from dom')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
done()
})
.bootstrapTooltip({ container: 'body', placement: 'top', trigger: 'manual' })
@@ -938,7 +1078,8 @@ $(function () {
$circle.bootstrapTooltip('show')
})
- test('should correctly determine auto placement based on container rather than parent', function (assert) {
+ QUnit.test('should correctly determine auto placement based on container rather than parent', function (assert) {
+ assert.expect(2)
var done = assert.async()
var styles = '<style>'
@@ -961,13 +1102,13 @@ $(function () {
var $tip = $('.tooltip-inner')
var tipXrightEdge = $tip.offset().left + $tip.width()
var triggerXleftEdge = $trigger.offset().left
- ok(tipXrightEdge < triggerXleftEdge, 'tooltip with auto left placement, when near the right edge of the viewport, gets left placement')
+ assert.ok(tipXrightEdge < triggerXleftEdge, 'tooltip with auto left placement, when near the right edge of the viewport, gets left placement')
$trigger.bootstrapTooltip('hide')
})
.on('hidden.bs.tooltip', function () {
$styles.remove()
$(this).remove()
- equal($('.tooltip').length, 0, 'tooltip removed from dom')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
done()
})
.bootstrapTooltip({
@@ -979,9 +1120,10 @@ $(function () {
$trigger.bootstrapTooltip('show')
})
- test('should not reload the tooltip on subsequent mouseenter events', function () {
+ QUnit.test('should not reload the tooltip on subsequent mouseenter events', function (assert) {
+ assert.expect(1)
var titleHtml = function () {
- var uid = 'fatTooltip'
+ var uid = $.fn.bootstrapTooltip.Constructor.prototype.getUID('tooltip')
return '<p id="tt-content">' + uid + '</p><p>' + uid + '</p><p>' + uid + '</p>'
}
@@ -1002,12 +1144,13 @@ $(function () {
var currentUid = $('#tt-content').text()
$('#tt-content').trigger('mouseenter')
- equal(currentUid, $('#tt-content').text())
+ assert.strictEqual(currentUid, $('#tt-content').text())
})
- test('should not reload the tooltip if the mouse leaves and re-enters before hiding', function () {
+ QUnit.test('should not reload the tooltip if the mouse leaves and re-enters before hiding', function (assert) {
+ assert.expect(4)
var titleHtml = function () {
- var uid = 'fatTooltip'
+ var uid = $.fn.bootstrapTooltip.Constructor.prototype.getUID('tooltip')
return '<p id="tt-content">' + uid + '</p><p>' + uid + '</p><p>' + uid + '</p>'
}
@@ -1030,19 +1173,18 @@ $(function () {
var currentUid = $('#tt-content').text()
$('#tt-outer').trigger('mouseleave')
- equal(currentUid, $('#tt-content').text())
-
- debugger
+ assert.strictEqual(currentUid, $('#tt-content').text())
- ok(obj.getHoverState() == 'out', 'the tooltip hoverState should be set to "out"')
+ assert.ok(obj.hoverState == 'out', 'the tooltip hoverState should be set to "out"')
$('#tt-content').trigger('mouseenter')
- ok(obj.getHoverState() == 'in', 'the tooltip hoverState should be set to "in"')
+ assert.ok(obj.hoverState == 'in', 'the tooltip hoverState should be set to "in"')
- equal(currentUid, $('#tt-content').text())
+ assert.strictEqual(currentUid, $('#tt-content').text())
})
- test('should position arrow correctly when tooltip is moved to not appear offscreen', function (assert) {
+ QUnit.test('should position arrow correctly when tooltip is moved to not appear offscreen', function (assert) {
+ assert.expect(2)
var done = assert.async()
var styles = '<style>'
@@ -1056,14 +1198,14 @@ $(function () {
$('<a href="#" title="tooltip title" style="position: absolute; bottom: 0; right: 0;">Foobar</a>')
.appendTo('body')
.on('shown.bs.tooltip', function () {
- var arrowStyles = $('.tooltip').find('.tooltip-arrow').attr('style')
- ok(/left/i.test(arrowStyles) && !/top/i.test(arrowStyles), 'arrow positioned correctly')
+ var arrowStyles = $(this).data('bs.tooltip').$tip.find('.tooltip-arrow').attr('style')
+ assert.ok(/left/i.test(arrowStyles) && !/top/i.test(arrowStyles), 'arrow positioned correctly')
$(this).bootstrapTooltip('hide')
})
.on('hidden.bs.tooltip', function () {
$styles.remove()
$(this).remove()
- equal($('.tooltip').length, 0, 'tooltip removed from dom')
+ assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
done()
})
.bootstrapTooltip({
@@ -1074,12 +1216,13 @@ $(function () {
.bootstrapTooltip('show')
})
- test('should correctly position tooltips on transformed elements', function (assert) {
+ QUnit.test('should correctly position tooltips on transformed elements', function (assert) {
var styleProps = document.documentElement.style
if (!('transform' in styleProps) && !('webkitTransform' in styleProps) && !('msTransform' in styleProps)) {
- expect(0)
+ assert.expect(0)
return
}
+ assert.expect(2)
var done = assert.async()
@@ -1092,14 +1235,14 @@ $(function () {
+ '</style>'
var $styles = $(styles).appendTo('head')
- var $element = $('<div id="target" title="1"></div>').appendTo('#qunit-fixture')
+ var $element = $('<div id="target" title="1"/>').appendTo('#qunit-fixture')
$element
.on('shown.bs.tooltip', function () {
var offset = $('.tooltip').offset()
$styles.remove()
- ok(Math.abs(offset.left - 88) <= 1, 'tooltip has correct horizontal location')
- ok(Math.abs(offset.top - 126) <= 1, 'tooltip has correct vertical location')
+ assert.ok(Math.abs(offset.left - 88) <= 1, 'tooltip has correct horizontal location')
+ assert.ok(Math.abs(offset.top - 126) <= 1, 'tooltip has correct vertical location')
$element.bootstrapTooltip('hide')
done()
})
@@ -1112,15 +1255,71 @@ $(function () {
$element.bootstrapTooltip('show')
})
- QUnit.test('should throw an error when trying to show a tooltip on a hidden element', function (assert) {
+ QUnit.test('should throw an error when initializing tooltip on the document object without specifying a delegation selector', function (assert) {
+ assert.expect(1)
+ assert.throws(function () {
+ $(document).bootstrapTooltip({ title: 'What am I on?' })
+ }, new Error('`selector` option must be specified when initializing tooltip on the window.document object!'))
+ })
+
+ QUnit.test('should do nothing when an attempt is made to hide an uninitialized tooltip', function (assert) {
assert.expect(1)
- var $target = $('<a href="#" rel="tooltip" title="Another tooltip" style="display: none;">I am hidden</a>').appendTo('#qunit-fixture')
+ var $tooltip = $('<span data-toggle="tooltip" title="some tip">some text</span>')
+ .appendTo('#qunit-fixture')
+ .on('hidden.bs.tooltip shown.bs.tooltip', function () {
+ assert.ok(false, 'should not fire any tooltip events')
+ })
+ .bootstrapTooltip('hide')
+ assert.strictEqual($tooltip.data('bs.tooltip'), undefined, 'should not initialize the tooltip')
+ })
+
+ QUnit.test('should throw an error when template contains multiple top-level elements', function (assert) {
+ assert.expect(1)
assert.throws(function () {
- $target.bootstrapTooltip('show')
- }, new Error('Can\'t show a tooltip/popover on a hidden element'))
+ $('<a href="#" data-toggle="tooltip" title="Another tooltip"></a>')
+ .appendTo('#qunit-fixture')
+ .bootstrapTooltip({ template: '<div>Foo</div><div>Bar</div>' })
+ .bootstrapTooltip('show')
+ }, new Error('tooltip `template` option must consist of exactly 1 top-level element!'))
+ })
+
+ QUnit.test('should not remove tooltip if multiple triggers are set and one is still active', function (assert) {
+ assert.expect(41)
+ var $el = $('<button>Trigger</button>')
+ .appendTo('#qunit-fixture')
+ .bootstrapTooltip({ trigger: 'click hover focus', animation: false })
+ var tooltip = $el.data('bs.tooltip')
+ var $tooltip = tooltip.tip()
+
+ function showingTooltip() { return $tooltip.hasClass('in') || tooltip.hoverState == 'in' }
+
+ var tests = [
+ ['mouseenter', 'mouseleave'],
- $target.remove()
+ ['focusin', 'focusout'],
+
+ ['click', 'click'],
+
+ ['mouseenter', 'focusin', 'focusout', 'mouseleave'],
+ ['mouseenter', 'focusin', 'mouseleave', 'focusout'],
+
+ ['focusin', 'mouseenter', 'mouseleave', 'focusout'],
+ ['focusin', 'mouseenter', 'focusout', 'mouseleave'],
+
+ ['click', 'focusin', 'mouseenter', 'focusout', 'mouseleave', 'click'],
+ ['mouseenter', 'click', 'focusin', 'focusout', 'mouseleave', 'click'],
+ ['mouseenter', 'focusin', 'click', 'click', 'mouseleave', 'focusout']
+ ]
+
+ assert.ok(!showingTooltip())
+
+ $.each(tests, function (idx, triggers) {
+ for (var i = 0, len = triggers.length; i < len; i++) {
+ $el.trigger(triggers[i]);
+ assert.equal(i < (len - 1), showingTooltip())
+ }
+ })
})
})
diff --git a/js/tests/visual/affix-with-sticky-footer.html b/js/tests/visual/affix-with-sticky-footer.html
new file mode 100644
index 0000000000..5c413067ad
--- /dev/null
+++ b/js/tests/visual/affix-with-sticky-footer.html
@@ -0,0 +1,317 @@
+<!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>
+ html {
+ position: relative;
+ min-height: 100%;
+ }
+ body {
+ /* Margin bottom by footer height */
+ margin-bottom: 200px;
+ }
+ .footer {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ /* Set the fixed height of the footer here */
+ height: 200px;
+ background-color: #f5f5f5;
+ }
+ /* 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>
+
+ <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+ <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
+ <!--[if lt IE 9]>
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+ <![endif]-->
+</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>
+
+<footer class="footer js-footer">
+ <div class="container">
+ <p class="text-muted">Place sticky footer content here.</p>
+ </div>
+</footer>
+
+<!-- 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/affix.html b/js/tests/visual/affix.html
new file mode 100644
index 0000000000..4d772dc738
--- /dev/null
+++ b/js/tests/visual/affix.html
@@ -0,0 +1,306 @@
+<!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>
+
+ <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+ <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
+ <!--[if lt IE 9]>
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+ <![endif]-->
+</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 978f9ac209..0070315a24 100644
--- a/js/tests/visual/alert.html
+++ b/js/tests/visual/alert.html
@@ -6,6 +6,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Alert</title>
<link rel="stylesheet" href="../../../dist/css/bootstrap.min.css">
+
+ <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+ <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
+ <!--[if lt IE 9]>
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+ <![endif]-->
</head>
<body>
@@ -34,7 +41,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../util.js"></script>
+<script src="../../transition.js"></script>
<script src="../../alert.js"></script>
</body>
diff --git a/js/tests/visual/button.html b/js/tests/visual/button.html
index c3be921415..d92a083296 100644
--- a/js/tests/visual/button.html
+++ b/js/tests/visual/button.html
@@ -6,6 +6,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Button</title>
<link rel="stylesheet" href="../../../dist/css/bootstrap.min.css">
+
+ <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+ <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
+ <!--[if lt IE 9]>
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+ <![endif]-->
</head>
<body>
@@ -15,6 +22,10 @@
<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">
@@ -45,7 +56,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../util.js"></script>
+<script src="../../transition.js"></script>
<script src="../../button.js"></script>
<!-- JavaScript Test -->
diff --git a/js/tests/visual/carousel.html b/js/tests/visual/carousel.html
index 45424a5d15..8dede0921b 100644
--- a/js/tests/visual/carousel.html
+++ b/js/tests/visual/carousel.html
@@ -6,6 +6,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Carousel</title>
<link rel="stylesheet" href="../../../dist/css/bootstrap.min.css">
+
+ <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+ <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
+ <!--[if lt IE 9]>
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+ <![endif]-->
</head>
<body>
@@ -22,13 +29,13 @@
<li data-target="#carousel-example-generic" data-slide-to="2" class=""></li>
</ol>
<div class="carousel-inner">
- <div class="carousel-item active">
+ <div class="item active">
<img alt="First slide" src="http://37.media.tumblr.com/tumblr_m8tay0JcfG1qa42jro1_1280.jpg">
</div>
- <div class="carousel-item">
+ <div class="item">
<img alt="Second slide" src="http://37.media.tumblr.com/tumblr_m8tazfiVYJ1qa42jro1_1280.jpg">
</div>
- <div class="carousel-item">
+ <div class="item">
<img alt="Third slide" src="http://38.media.tumblr.com/tumblr_m8tb2rVsD31qa42jro1_1280.jpg">
</div>
</div>
@@ -44,7 +51,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../util.js"></script>
+<script src="../../transition.js"></script>
<script src="../../carousel.js"></script>
</body>
diff --git a/js/tests/visual/collapse.html b/js/tests/visual/collapse.html
index 42ac0aaa2b..3b8a42e3d7 100644
--- a/js/tests/visual/collapse.html
+++ b/js/tests/visual/collapse.html
@@ -6,6 +6,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Collapse</title>
<link rel="stylesheet" href="../../../dist/css/bootstrap.min.css">
+
+ <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+ <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
+ <!--[if lt IE 9]>
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+ <![endif]-->
</head>
<body>
@@ -64,7 +71,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../util.js"></script>
+<script src="../../transition.js"></script>
<script src="../../collapse.js"></script>
</body>
diff --git a/js/tests/visual/dropdown.html b/js/tests/visual/dropdown.html
index 6cf18df8ee..02090b6cc9 100644
--- a/js/tests/visual/dropdown.html
+++ b/js/tests/visual/dropdown.html
@@ -6,6 +6,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Dropdown</title>
<link rel="stylesheet" href="../../../dist/css/bootstrap.min.css">
+
+ <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+ <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
+ <!--[if lt IE 9]>
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+ <![endif]-->
</head>
<body>
@@ -93,7 +100,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../util.js"></script>
+<script src="../../transition.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 0d6e50ea2c..4342f0ce44 100644
--- a/js/tests/visual/modal.html
+++ b/js/tests/visual/modal.html
@@ -6,16 +6,55 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Modal</title>
<link rel="stylesheet" href="../../../dist/css/bootstrap.min.css">
+
+ <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+ <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
+ <!--[if lt IE 9]>
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+ <![endif]-->
+
+ <style>
+ #tall {
+ height: 1500px;
+ width: 100px;
+ background-color: black;
+ color: white;
+ }
+ </style>
</head>
<body>
+<nav class="navbar navbar-default navbar-static-top">
+ <div class="container-fluid">
+ <div class="navbar-header">
+ <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ <a class="navbar-brand" href="#">navbar-static-top</a>
+ </div>
+ <div id="navbar" class="navbar-collapse collapse">
+ <ul class="nav navbar-nav">
+ <li><a href="#about">About</a></li>
+ <li><a href="#contact">Contact</a></li>
+ </ul>
+ <ul class="nav navbar-nav navbar-right">
+ <li><a href="#">This should not jump to the left when the modal is shown.</a></li>
+ </ul>
+ </div><!--/.nav-collapse -->
+ </div>
+</nav>
+
<div class="container">
<div class="page-header">
<h1>Modal <small>Bootstrap Visual Test</small></h1>
</div>
- <div id="myModal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
+ <div id="myModal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog">
<div class="modal-content">
@@ -28,10 +67,43 @@
<p>Duis mollis, est non commodo luctus, nisi erat porttitor ligula.</p>
<h4>Popover in a modal</h4>
- <p>This <a href="#" role="button" class="btn btn-default js-popover" title="" data-content="And here's some amazing content. It's very engaging. right?" data-original-title="A Title">button</a> should trigger a popover on click.</p>
+ <p>This <a href="#" role="button" class="btn btn-default js-popover" title="A Title" data-content="And here's some amazing content. It's very engaging. right?" data-placement="left">button</a> should trigger a popover on click.</p>
<h4>Tooltips in a modal</h4>
- <p><a href="#" class="js-tooltip" title="" data-original-title="Tooltip">This link</a> and <a href="#" class="js-tooltip" title="" data-original-title="Tooltip">that link</a> should have tooltips on hover.</p>
+ <p><a href="#" class="js-tooltip" title="Tooltip">This link</a> and <a href="#" class="js-tooltip" title="Tooltip">that link</a> should have tooltips on hover.</p>
+
+ <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
+ <div class="panel panel-default">
+ <div class="panel-heading" role="tab" id="headingOne">
+ <h4 class="panel-title">
+ <a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
+ Collapsible Group Item #1
+ </a>
+ </h4>
+ </div>
+ <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne">
+ <div class="panel-body">
+ Lorem ipsum
+ </div>
+ </div>
+ </div>
+ <div class="panel panel-default">
+ <div class="panel-heading" role="tab" id="headingTwo">
+ <h4 class="panel-title">
+ <a class="collapsed" role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
+ Collapsible Group Item #2
+ </a>
+ </h4>
+ </div>
+ <div id="collapseTwo" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingTwo">
+ <div class="panel-body">
+ Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
+ Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
+ Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
+ </div>
+ </div>
+ </div>
+ </div>
<hr>
@@ -59,20 +131,30 @@
Launch demo modal
</button>
+ <button id="tall-toggle" class="btn btn-default">Toggle tall &lt;body&gt; content</button>
+ <br><br>
+ <div id="tall" style="display: none;">
+ Tall body content to force the page to have a scrollbar.
+ </div>
+
</div>
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../util.js"></script>
+<script src="../../transition.js"></script>
<script src="../../modal.js"></script>
<script src="../../tooltip.js"></script>
<script src="../../popover.js"></script>
+<script src="../../collapse.js"></script>
<!-- JavaScript Test -->
<script>
$(function () {
$('.js-popover').popover()
$('.js-tooltip').tooltip()
+ $('#tall-toggle').click(function () {
+ $('#tall').toggle()
+ })
})
</script>
diff --git a/js/tests/visual/popover.html b/js/tests/visual/popover.html
index 7446a67412..0e84f5ceee 100644
--- a/js/tests/visual/popover.html
+++ b/js/tests/visual/popover.html
@@ -6,6 +6,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Popover</title>
<link rel="stylesheet" href="../../../dist/css/bootstrap.min.css">
+
+ <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+ <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
+ <!--[if lt IE 9]>
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+ <![endif]-->
</head>
<body>
@@ -32,7 +39,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../util.js"></script>
+<script src="../../transition.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 13bc4383cc..5952b7e593 100644
--- a/js/tests/visual/scrollspy.html
+++ b/js/tests/visual/scrollspy.html
@@ -10,6 +10,13 @@
<style>
body { padding-top: 70px; }
</style>
+
+ <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+ <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
+ <!--[if lt IE 9]>
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+ <![endif]-->
</head>
<body data-spy="scroll" data-target=".navbar" data-offset="70">
@@ -93,7 +100,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../util.js"></script>
+<script src="../../transition.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 2480ce08ca..a3fed1e9fa 100644
--- a/js/tests/visual/tab.html
+++ b/js/tests/visual/tab.html
@@ -16,6 +16,13 @@
margin-bottom: 15px;
}
</style>
+
+ <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+ <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
+ <!--[if lt IE 9]>
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+ <![endif]-->
</head>
<body>
@@ -157,7 +164,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../util.js"></script>
+<script src="../../transition.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 c61b088d0e..8d10871c9b 100644
--- a/js/tests/visual/tooltip.html
+++ b/js/tests/visual/tooltip.html
@@ -6,6 +6,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Tooltip</title>
<link rel="stylesheet" href="../../../dist/css/bootstrap.min.css">
+
+ <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+ <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
+ <!--[if lt IE 9]>
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+ <![endif]-->
</head>
<body>
@@ -29,7 +36,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../util.js"></script>
+<script src="../../transition.js"></script>
<script src="../../tooltip.js"></script>
<!-- JavaScript Test -->
diff --git a/js/tooltip.js b/js/tooltip.js
index c787c6820f..0779f139d6 100644
--- a/js/tooltip.js
+++ b/js/tooltip.js
@@ -1,876 +1,514 @@
-/** =======================================================================
- * Bootstrap: tooltip.js v4.0.0
+/* ========================================================================
+ * Bootstrap: tooltip.js v3.3.4
* 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';
+ * ======================================================================== */
-/**
- * Our tooltip class.
- * @param {Element!} element
- * @param {Object=} opt_config
- * @constructor
- */
-var Tooltip = function (element, opt_config) {
++function ($) {
+ 'use strict';
- /** @private {boolean} */
- this._isEnabled = true
+ // TOOLTIP PUBLIC CLASS DEFINITION
+ // ===============================
- /** @private {number} */
- this._timeout = 0
+ var Tooltip = function (element, options) {
+ this.type = null
+ this.options = null
+ this.enabled = null
+ this.timeout = null
+ this.hoverState = null
+ this.$element = null
+ this.inState = null
- /** @private {string} */
- this._hoverState = ''
-
- /** @protected {Element} */
- this.element = element
+ this.init('tooltip', element, options)
+ }
- /** @protected {Object} */
- this.config = this._getConfig(opt_config)
+ Tooltip.VERSION = '3.3.4'
+
+ 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
+ }
+ }
- /** @protected {Element} */
- this.tip = null
+ 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 && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport))
+ this.inState = { click: false, hover: false, focus: false }
- /** @protected {Element} */
- this.arrow = null
+ if (this.$element[0] instanceof document.constructor && !this.options.selector) {
+ throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!')
+ }
- if (this.config['viewport']) {
+ var triggers = this.options.trigger.split(' ')
- /** @private {Element} */
- this._viewport = $(this.config['viewport']['selector'] || this.config['viewport'])[0]
+ for (var i = triggers.length; i--;) {
+ var trigger = triggers[i]
- }
+ 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'
- 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
+ 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 (!data) {
- data = new Tooltip(this, config)
- $(this).data(Tooltip._DATA_KEY, data)
- }
-
- 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)
- }
+ this.options.selector ?
+ (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
+ this.fixTitle()
}
- $(context.getTipElement()).hasClass(Tooltip._ClassName.IN) ?
- context._leave(null, context) :
- context._enter(null, context)
-}
-
-
-/**
- * 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 () {
- // jQuery's :hidden gives false positives for SVG elements
- // See https://github.com/jquery/jquery/pull/939
- // Since this hiddenness check is just a nicety anyway, simply assume SVGs are always visible.
- var isHidden = $(this.element).is(':hidden') && !(window.SVGElement && this.element instanceof window.SVGElement)
- if (isHidden) {
- throw new Error('Can\'t show a tooltip/popover on a hidden element')
+ Tooltip.prototype.getDefaults = function () {
+ return Tooltip.DEFAULTS
}
- var showEvent = $.Event(this.getEventObject().SHOW)
-
- if (this.isWithContent() && this._isEnabled) {
- $(this.element).trigger(showEvent)
+ Tooltip.prototype.getOptions = function (options) {
+ options = $.extend({}, this.getDefaults(), this.$element.data(), options)
- var isInTheDom = $.contains(this.element.ownerDocument.documentElement, this.element)
-
- if (showEvent.isDefaultPrevented() || !isInTheDom) {
- return
+ if (options.delay && typeof options.delay == 'number') {
+ options.delay = {
+ show: options.delay,
+ hide: options.delay
+ }
}
- var tip = this.getTipElement()
- var tipId = Bootstrap.getUID(this.getName())
-
- tip.setAttribute('id', tipId)
- this.element.setAttribute('aria-describedby', tipId)
+ return options
+ }
- this.setContent()
+ Tooltip.prototype.getDelegateOptions = function () {
+ var options = {}
+ var defaults = this.getDefaults()
- if (this.config['animation']) {
- $(tip).addClass(Tooltip._ClassName.FADE)
- }
+ this._options && $.each(this._options, function (key, value) {
+ if (defaults[key] != value) options[key] = value
+ })
- var placement = typeof this.config['placement'] == 'function' ?
- this.config['placement'].call(this, tip, this.element) :
- this.config['placement']
+ return options
+ }
- var autoToken = /\s?auto?\s?/i
- var isWithAutoPlacement = autoToken.test(placement)
+ Tooltip.prototype.enter = function (obj) {
+ var self = obj instanceof this.constructor ?
+ obj : $(obj.currentTarget).data('bs.' + this.type)
- if (isWithAutoPlacement) {
- placement = placement.replace(autoToken, '') || Tooltip.Direction.TOP
+ if (!self) {
+ self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
+ $(obj.currentTarget).data('bs.' + this.type, self)
}
- if (tip.parentNode && tip.parentNode.nodeType == Node.ELEMENT_NODE) {
- tip.parentNode.removeChild(tip)
+ if (obj instanceof $.Event) {
+ self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true
}
- tip.style.top = 0
- tip.style.left = 0
- tip.style.display = 'block'
-
- $(tip).addClass(Tooltip._NAME + '-' + placement)
-
- $(tip).data(this.getDataKey(), this)
-
- if (this.config['container']) {
- $(this.config['container'])[0].appendChild(tip)
- } else {
- this.element.parentNode.insertBefore(tip, this.element.nextSibling)
+ if (self.tip().hasClass('in') || self.hoverState == 'in') {
+ self.hoverState = 'in'
+ return
}
- 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)
-
- this._applyCalculatedPlacement(calculatedOffset, calculatedPlacement)
+ clearTimeout(self.timeout)
- var complete = function () {
- var prevHoverState = this.hoverState
- $(this.element).trigger(this.getEventObject().SHOWN)
- this.hoverState = null
+ self.hoverState = 'in'
- if (prevHoverState == 'out') this._leave(null, this)
- }.bind(this)
+ if (!self.options.delay || !self.options.delay.show) return self.show()
- Bootstrap.transition && $(this._tip).hasClass(Tooltip._ClassName.FADE) ?
- $(this._tip)
- .one(Bootstrap.TRANSITION_END, complete)
- .emulateTransitionEnd(Tooltip._TRANSITION_DURATION) :
- complete()
+ self.timeout = setTimeout(function () {
+ if (self.hoverState == 'in') self.show()
+ }, self.options.delay.show)
}
-}
-
-/**
- * Hide the tooltip breh
- */
-Tooltip.prototype['hide'] = function (callback) {
- var tip = this.getTipElement()
- var hideEvent = $.Event(this.getEventObject().HIDE)
-
- var complete = function () {
- if (this._hoverState != Tooltip._HoverState.IN) {
- tip.parentNode.removeChild(tip)
+ Tooltip.prototype.isInStateTrue = function () {
+ for (var key in this.inState) {
+ if (this.inState[key]) return true
}
- this.element.removeAttribute('aria-describedby')
- $(this.element).trigger(this.getEventObject().HIDDEN)
-
- if (callback) {
- callback()
- }
- }.bind(this)
-
- $(this.element).trigger(hideEvent)
-
- if (hideEvent.isDefaultPrevented()) return
-
- $(tip).removeClass(Tooltip._ClassName.IN)
-
- if (Bootstrap.transition && $(this._tip).hasClass(Tooltip._ClassName.FADE)) {
- $(tip)
- .one(Bootstrap.TRANSITION_END, complete)
- .emulateTransitionEnd(Tooltip._TRANSITION_DURATION)
- } else {
- complete()
+ return false
}
- this._hoverState = ''
-}
-
+ Tooltip.prototype.leave = function (obj) {
+ var self = obj instanceof this.constructor ?
+ obj : $(obj.currentTarget).data('bs.' + this.type)
-/**
- * @return {string}
- */
-Tooltip.prototype['getHoverState'] = function (callback) {
- return this._hoverState
-}
+ if (!self) {
+ self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
+ $(obj.currentTarget).data('bs.' + this.type, self)
+ }
+ if (obj instanceof $.Event) {
+ self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false
+ }
-/**
- * @return {string}
- * @protected
- */
-Tooltip.prototype.getName = function () {
- return Tooltip._NAME
-}
+ if (self.isInStateTrue()) return
+ clearTimeout(self.timeout)
-/**
- * @return {string}
- * @protected
- */
-Tooltip.prototype.getDataKey = function () {
- return Tooltip._DATA_KEY
-}
+ self.hoverState = 'out'
+ if (!self.options.delay || !self.options.delay.hide) return self.hide()
-/**
- * @return {Object}
- * @protected
- */
-Tooltip.prototype.getEventObject = function () {
- return Tooltip._Event
-}
+ self.timeout = setTimeout(function () {
+ if (self.hoverState == 'out') self.hide()
+ }, self.options.delay.hide)
+ }
+ Tooltip.prototype.show = function () {
+ var e = $.Event('show.bs.' + this.type)
-/**
- * @return {string}
- * @protected
- */
-Tooltip.prototype.getTitle = function () {
- var title = this.element.getAttribute('data-original-title')
+ if (this.hasContent() && this.enabled) {
+ this.$element.trigger(e)
- if (!title) {
- title = typeof this.config['title'] === 'function' ?
- this.config['title'].call(this.element) :
- this.config['title']
- }
+ var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
+ if (e.isDefaultPrevented() || !inDom) return
+ var that = this
- return /** @type {string} */ (title)
-}
+ var $tip = this.tip()
+ var tipId = this.getUID(this.type)
-/**
- * @return {Element}
- * @protected
- */
-Tooltip.prototype.getTipElement = function () {
- return (this._tip = this._tip || $(this.config['template'])[0])
-}
+ this.setContent()
+ $tip.attr('id', tipId)
+ this.$element.attr('aria-describedby', tipId)
+ if (this.options.animation) $tip.addClass('fade')
-/**
- * @return {Element}
- * @protected
- */
-Tooltip.prototype.getArrowElement = function () {
- return (this.arrow = this.arrow || $(this.getTipElement()).find(Tooltip._Selector.TOOLTIP_ARROW)[0])
-}
+ var placement = typeof this.options.placement == 'function' ?
+ this.options.placement.call(this, $tip[0], this.$element[0]) :
+ this.options.placement
+ var autoToken = /\s?auto?\s?/i
+ var autoPlace = autoToken.test(placement)
+ if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
-/**
- * @return {boolean}
- * @protected
- */
-Tooltip.prototype.isWithContent = function () {
- return !!this.getTitle()
-}
+ $tip
+ .detach()
+ .css({ top: 0, left: 0, display: 'block' })
+ .addClass(placement)
+ .data('bs.' + this.type, this)
+ this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
+ this.$element.trigger('inserted.bs.' + this.type)
-/**
- * @protected
- */
-Tooltip.prototype.setContent = function () {
- var tip = this.getTipElement()
- var title = this.getTitle()
+ var pos = this.getPosition()
+ var actualWidth = $tip[0].offsetWidth
+ var actualHeight = $tip[0].offsetHeight
- $(tip).find(Tooltip._Selector.TOOLTIP_INNER)[0][this.config['html'] ? 'innerHTML' : 'innerText'] = title
+ if (autoPlace) {
+ var orgPlacement = placement
+ var viewportDim = this.getPosition(this.$viewport)
- $(tip)
- .removeClass(Tooltip._ClassName.FADE)
- .removeClass(Tooltip._ClassName.IN)
+ placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' :
+ placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' :
+ placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' :
+ placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' :
+ placement
- for (var direction in Tooltip.Direction) {
- $(tip).removeClass(Tooltip._NAME + '-' + direction)
- }
-}
+ $tip
+ .removeClass(orgPlacement)
+ .addClass(placement)
+ }
+ var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
-/**
- * @private
- */
-Tooltip.prototype._setListeners = function () {
- var triggers = this.config['trigger'].split(' ')
+ this.applyPlacement(calculatedOffset, placement)
- triggers.forEach(function (trigger) {
- if (trigger == 'click') {
- $(this.element).on('click.bs.tooltip', this.config['selector'], this['toggle'].bind(this))
+ var complete = function () {
+ var prevHoverState = that.hoverState
+ that.$element.trigger('shown.bs.' + that.type)
+ that.hoverState = null
- } else if (trigger != 'manual') {
- var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
- var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
+ if (prevHoverState == 'out') that.leave(that)
+ }
- $(this.element)
- .on(eventIn + '.bs.tooltip', this.config['selector'], this._enter.bind(this))
- .on(eventOut + '.bs.tooltip', this.config['selector'], this._leave.bind(this))
+ $.support.transition && this.$tip.hasClass('fade') ?
+ $tip
+ .one('bsTransitionEnd', complete)
+ .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
+ complete()
}
- }.bind(this))
-
- if (this.config['selector']) {
- this.config = $.extend({}, this.config, { 'trigger': 'manual', 'selector': '' })
- } else {
- this._fixTitle()
}
-}
-
-
-/**
- * @param {Object=} opt_config
- * @return {Object}
- * @private
- */
-Tooltip.prototype._getConfig = function (opt_config) {
- var config = $.extend({}, this.constructor['Defaults'], $(this.element).data(), opt_config)
- if (config['delay'] && typeof config['delay'] == 'number') {
- config['delay'] = {
- 'show': config['delay'],
- 'hide': config['delay']
+ 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 ie 8/9
+ if (isNaN(marginTop)) marginTop = 0
+ if (isNaN(marginLeft)) marginLeft = 0
+
+ offset.top += marginTop
+ 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 config
-}
+ var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
+ if (delta.left) offset.left += delta.left
+ else offset.top += delta.top
-/**
- * @return {Object}
- * @private
- */
-Tooltip.prototype._getDelegateConfig = function () {
- var config = {}
- var defaults = this.constructor['Defaults']
+ var isVertical = /top|bottom/.test(placement)
+ var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
+ var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
- if (this.config) {
- for (var key in this.config) {
- var value = this.config[key]
- if (defaults[key] != value) config[key] = value
- }
+ $tip.offset(offset)
+ this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
}
- 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)
+ Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) {
+ this.arrow()
+ .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
+ .css(isVertical ? 'top' : 'left', '')
}
- 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.setContent = function () {
+ var $tip = this.tip()
+ var title = this.getTitle()
+
+ $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
+ $tip.removeClass('fade in top bottom left right')
}
- var viewportPadding = this.config['viewport'] && this.config['viewport']['padding'] || 0
- var viewportDimensions = this._getPosition(this._viewport)
+ 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()
+ }
- if (placement === Tooltip.Direction.RIGHT || placement === Tooltip.Direction.LEFT) {
- var topEdgeOffset = position.top - viewportPadding - viewportDimensions.scroll
- var bottomEdgeOffset = position.top + viewportPadding - viewportDimensions.scroll + actualHeight
+ this.$element.trigger(e)
- if (topEdgeOffset < viewportDimensions.top) { // top overflow
- delta.top = viewportDimensions.top - topEdgeOffset
+ if (e.isDefaultPrevented()) return
- } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
- delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
- }
+ $tip.removeClass('in')
- } else {
- var leftEdgeOffset = position.left - viewportPadding
- var rightEdgeOffset = position.left + viewportPadding + actualWidth
+ $.support.transition && $tip.hasClass('fade') ?
+ $tip
+ .one('bsTransitionEnd', complete)
+ .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
+ complete()
- if (leftEdgeOffset < viewportDimensions.left) { // left overflow
- delta.left = viewportDimensions.left - leftEdgeOffset
+ this.hoverState = null
- } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
- delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
- }
+ return this
}
- 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.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', '')
+ }
}
- offset.top = offset.top + marginTop
- offset.left = offset.left + marginLeft
+ Tooltip.prototype.hasContent = function () {
+ return this.getTitle()
+ }
- // $.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)
+ Tooltip.prototype.getPosition = function ($element) {
+ $element = $element || this.$element
- $(tip).addClass(Tooltip._ClassName.IN)
+ var el = $element[0]
+ var isBody = el.tagName == 'BODY'
- // check to see if placing tip in new offset caused the tip to resize itself
- var actualWidth = tip.offsetWidth
- var actualHeight = tip.offsetHeight
+ 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 })
+ }
+ 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
- if (placement == Tooltip.Direction.TOP && actualHeight != height) {
- offset.top = offset.top + height - actualHeight
+ return $.extend({}, elRect, scroll, outerDims, elOffset)
}
- var delta = this._getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
+ 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 }
- if (delta.left) {
- offset.left += delta.left
- } else {
- offset.top += delta.top
}
- 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()
+ 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.right) { // right overflow
+ delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
+ }
+ }
- arrow.style[isHorizontal ? 'left' : 'top'] = 50 * (1 - delta / dimension) + '%'
- arrow.style[isHorizontal ? 'top' : 'left'] = ''
-}
+ return delta
+ }
+ Tooltip.prototype.getTitle = function () {
+ var title
+ var $e = this.$element
+ var o = this.options
+ title = $e.attr('data-original-title')
+ || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
-/**
- * @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', '')
+ return title
}
-}
-
-
-/**
- * @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.getUID = function (prefix) {
+ do prefix += ~~(Math.random() * 1000000)
+ while (document.getElementById(prefix))
+ return prefix
}
- if (!context) {
- context = new this.constructor(opt_event.currentTarget, this._getDelegateConfig())
- $(opt_event.currentTarget).data(dataKey, context)
+ Tooltip.prototype.tip = function () {
+ if (!this.$tip) {
+ this.$tip = $(this.options.template)
+ if (this.$tip.length != 1) {
+ throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!')
+ }
+ }
+ return this.$tip
}
- clearTimeout(context._timeout)
+ Tooltip.prototype.arrow = function () {
+ return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
+ }
- context._hoverState = Tooltip._HoverState.IN
+ Tooltip.prototype.enable = function () {
+ this.enabled = true
+ }
- if (!context.config['delay'] || !context.config['delay']['show']) {
- context['show']()
- return
+ Tooltip.prototype.disable = function () {
+ this.enabled = false
}
- 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.prototype.toggleEnabled = function () {
+ this.enabled = !this.enabled
}
- clearTimeout(context._timeout)
+ 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)
+ }
+ }
- context._hoverState = Tooltip._HoverState.OUT
+ if (e) {
+ self.inState.click = !self.inState.click
+ if (self.isInStateTrue()) self.enter(self)
+ else self.leave(self)
+ } else {
+ self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
+ }
+ }
- if (!context.config['delay'] || !context.config['delay']['hide']) {
- context['hide']()
- return
+ Tooltip.prototype.destroy = function () {
+ var that = this
+ clearTimeout(this.timeout)
+ this.hide(function () {
+ that.$element.off('.' + that.type).removeData('bs.' + that.type)
+ if (that.$tip) {
+ that.$tip.detach()
+ }
+ that.$tip = null
+ that.$arrow = null
+ that.$viewport = null
+ })
}
- context._timeout = setTimeout(function () {
- if (context._hoverState == Tooltip._HoverState.OUT) {
- context['hide']()
- }
- }, context.config['delay']['hide'])
-}
+ // TOOLTIP PLUGIN DEFINITION
+ // =========================
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.tooltip')
+ var options = typeof option == 'object' && option
-/**
- * ------------------------------------------------------------------------
- * jQuery Interface + noConflict implementaiton
- * ------------------------------------------------------------------------
- */
+ if (!data && /destroy|hide/.test(option)) return
+ if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
-/**
- * @const
- * @type {Function}
- */
-$.fn[Tooltip._NAME] = Tooltip._jQueryInterface
+ var old = $.fn.tooltip
+ $.fn.tooltip = Plugin
+ $.fn.tooltip.Constructor = Tooltip
-/**
- * @const
- * @type {Function}
- */
-$.fn[Tooltip._NAME]['Constructor'] = Tooltip
+ // TOOLTIP NO CONFLICT
+ // ===================
+
+ $.fn.tooltip.noConflict = function () {
+ $.fn.tooltip = old
+ return this
+ }
-/**
- * @const
- * @type {Function}
- */
-$.fn[Tooltip._NAME]['noConflict'] = function () {
- $.fn[Tooltip._NAME] = Tooltip._JQUERY_NO_CONFLICT
- return this
-}
+}(jQuery);
diff --git a/js/util.js b/js/util.js
deleted file mode 100644
index 294a5a9601..0000000000
--- a/js/util.js
+++ /dev/null
@@ -1,165 +0,0 @@
-/** =======================================================================
- * 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()
-})
diff --git a/package.json b/package.json
index b2800e6863..107325a254 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,6 @@
"grunt": "~0.4.5",
"grunt-autoprefixer": "~2.2.0",
"grunt-banner": "~0.3.1",
- "grunt-closure-tools": "^0.9.7",
"grunt-contrib-clean": "~0.6.0",
"grunt-contrib-compress": "~0.13.0",
"grunt-contrib-concat": "~0.5.1",
@@ -54,7 +53,7 @@
"grunt-exec": "~0.4.6",
"grunt-html": "~4.0.1",
"grunt-jekyll": "~0.4.2",
- "grunt-jscs": "~1.5.0",
+ "grunt-jscs": "~1.8.0",
"grunt-postcss": "^0.3.0",
"grunt-sass": "^1.0.0",
"grunt-saucelabs": "~8.6.0",
@@ -64,7 +63,6 @@
"markdown-it": "^4.0.1",
"mq4-hover-shim": "^0.1.0",
"npm-shrinkwrap": "^200.1.0",
- "superstartup-closure-compiler": "^0.1.6",
"time-grunt": "^1.1.0"
},
"engines": {