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.js68
-rw-r--r--dist/js/bootstrap.js5449
-rw-r--r--dist/js/bootstrap.min.js74
-rw-r--r--dist/js/bootstrap.min.js.gzbin0 -> 10652 bytes
-rw-r--r--dist/js/npm.js8
-rw-r--r--js/alert.js280
-rw-r--r--js/button.js275
-rw-r--r--js/carousel.js669
-rw-r--r--js/collapse.js526
-rw-r--r--js/dropdown.js375
-rw-r--r--js/externs/bootstrap.js6
-rw-r--r--js/externs/jQuery.js2159
-rw-r--r--js/hover.js4
-rw-r--r--js/modal.js752
-rw-r--r--js/popover.js320
-rw-r--r--js/scrollspy.js427
-rw-r--r--js/tab.js395
-rw-r--r--js/tests/closure.html83
-rw-r--r--js/tests/index.html4
-rw-r--r--js/tests/unit/button.js55
-rw-r--r--js/tests/unit/carousel.js9
-rw-r--r--js/tests/unit/collapse.js2
-rw-r--r--js/tests/unit/modal.js2
-rw-r--r--js/tests/unit/tooltip.js22
-rw-r--r--js/tests/visual/affix.html299
-rw-r--r--js/tests/visual/alert.html2
-rw-r--r--js/tests/visual/button.html6
-rw-r--r--js/tests/visual/carousel.html2
-rw-r--r--js/tests/visual/collapse.html2
-rw-r--r--js/tests/visual/dropdown.html2
-rw-r--r--js/tests/visual/modal.html2
-rw-r--r--js/tests/visual/popover.html2
-rw-r--r--js/tests/visual/scrollspy.html2
-rw-r--r--js/tests/visual/tab.html2
-rw-r--r--js/tests/visual/tooltip.html2
-rw-r--r--js/tooltip.js1086
-rw-r--r--js/util.js165
-rw-r--r--package.json5
38 files changed, 10086 insertions, 3457 deletions
diff --git a/Gruntfile.js b/Gruntfile.js
index fb9ec82020..38d6d20dfb 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -108,32 +108,78 @@ module.exports = function (grunt) {
},
bootstrap: {
src: [
- 'js/hover.js',
- mq4HoverShim.featureDetector.umdGlobal,
- 'js/transition.js',
+ '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/scrollspy.js',
- 'js/tab.js',
- 'js/affix.js'
+ 'js/tab.js'
],
dest: 'dist/js/<%= pkg.name %>.js'
}
},
- uglify: {
+ closureCompiler: {
+
options: {
- preserveComments: 'some'
+ compilerFile: require('superstartup-closure-compiler').getPath(),
+ checkModified: false,
+
+ compilerOpts: {
+ // 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'
+ },
+
+ 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'
},
- core: {
- src: '<%= concat.bootstrap.dest %>',
+
+ 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'
+ ],
dest: 'dist/js/<%= pkg.name %>.min.js'
+ }
+
+ },
+
+ uglify: {
+ options: {
+ preserveComments: 'some'
},
docsJs: {
src: configBridge.paths.docsJs,
@@ -388,7 +434,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', 'uglify:core', 'commonjs']);
+ grunt.registerTask('dist-js', ['concat', 'closureCompiler', 'commonjs']);
grunt.registerTask('test-scss', ['scsslint:scss']);
diff --git a/dist/js/bootstrap.js b/dist/js/bootstrap.js
index 584fe3ba20..5d3f63040d 100644
--- a/dist/js/bootstrap.js
+++ b/dist/js/bootstrap.js
@@ -15,2291 +15,4566 @@ if (typeof jQuery === 'undefined') {
}
}(jQuery);
-/* ========================================================================
- * Bootstrap: transition.js v3.3.2
- * http://getbootstrap.com/javascript/#transitions
+/** =======================================================================
+ * 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';
-+function ($) {
- 'use strict';
- // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
- // ============================================================
+/**
+ * @type {Object}
+ */
+var Bootstrap = {}
- function transitionEnd() {
- var el = document.createElement('bootstrap')
- var transEndEventNames = {
- WebkitTransition : 'webkitTransitionEnd',
- MozTransition : 'transitionend',
- OTransition : 'oTransitionEnd otransitionend',
- transition : 'transitionend'
- }
+/**
+ * @const
+ * @type {string}
+ */
+Bootstrap.TRANSITION_END = 'bsTransitionEnd'
- for (var name in transEndEventNames) {
- if (el.style[name] !== undefined) {
- return { end: transEndEventNames[name] }
- }
- }
- return false // explicit for ie8 ( ._.)
- }
+/**
+ * @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')
- // 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
+ if (!selector) {
+ selector = element.getAttribute('href') || ''
+ selector = /^#[a-z]/i.test(selector) ? selector : null
}
- $(function () {
- $.support.transition = transitionEnd()
+ return selector
+}
+
+
+/**
+ * @param {string} prefix
+ * @return {string}
+ */
+Bootstrap.getUID = function (prefix) {
+ do prefix += ~~(Math.random() * 1000000)
+ while (document.getElementById(prefix))
+ return prefix
+}
- if (!$.support.transition) return
- $.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)
+/**
+ * @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
})
-}(jQuery);
+ 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
+ }
-/* ========================================================================
- * Bootstrap: alert.js v3.3.2
+ $.event.special[Bootstrap.TRANSITION_END] = Bootstrap.getSpecialTransitionEndEvent()
+})
+
+$(document).on('mq4hsChange', function (e) {
+ $(document.documentElement).toggleClass('bs-true-hover', e.trueHover)
+})
+
+/*!
+ * mq4-hover-shim v0.1.0
+ * https://github.com/twbs/mq4-hover-shim
+ * Copyright (c) 2014-2015 Christopher Rebert
+ * Licensed under the MIT License (https://github.com/twbs/mq4-hover-shim/blob/master/LICENSE).
+ */
+
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.mq4HoverShim=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+"use strict";
+
+var _interopRequireWildcard = function (obj) { return obj && obj.__esModule ? obj : { "default": obj }; };
+
+
+
+
+/**
+* Does this UA's primary pointer support true hovering
+* OR does the UA at least not try to quirkily emulate hovering,
+* such that :hover CSS styles are appropriate?
+* Essentially tries to shim the `@media (hover: hover)` CSS media query feature.
+* @public
+* @returns {boolean}
+* @since 0.0.1
+*/
+exports.supportsTrueHover = supportsTrueHover;
+/*eslint-env browser */
+/*eslint no-var:2*/
+/* jshint browser: true, esnext: true */
+/* jshint -W080 */
+/**
+* @module mq4HoverShim
+* @requires jquery
+*/
+var $ = (function () {
+ try {
+ var jQuery = _interopRequireWildcard(require("jquery"));
+
+ return jQuery;
+ } catch (importErr) {
+ var globaljQuery = window.$ || window.jQuery || window.Zepto;
+ if (!globaljQuery) {
+ throw new Error("mq4HoverShim needs jQuery (or similar)");
+ }
+ return globaljQuery;
+ }
+})();
+
+/** @type {boolean|undefined} */
+var canTrulyHover = undefined;
+
+/**
+* @private
+* @fires mq4HoverShim#mq4hsChange
+*/
+function triggerEvent() {
+ $(document).trigger($.Event("mq4hsChange", { bubbles: false, trueHover: canTrulyHover }));
+}
+
+// IIFE so we can use `return`s to avoid deeply-nested if-s
+(function () {
+ if (!window.matchMedia) {
+ // Opera Mini, IE<=9, Android<=2.3, ancient, or obscure; per http://caniuse.com/#feat=matchmedia
+
+ // Opera Mini, Android, and IE Mobile don't support true hovering, so they're what we'll check for.
+ // Other browsers are either:
+ // (a) obscure
+ // (b) touch-based but old enough not to attempt to emulate hovering
+ // (c) old desktop browsers that do support true hovering
+
+ // Explanation of this UA regex:
+ // IE Mobile <9 seems to always have "Windows CE", "Windows Phone", or "IEMobile" in its UA string.
+ // IE Mobile 9 in desktop view doesn't include "IEMobile" or "Windows Phone" in the UA string,
+ // but it instead includes "XBLWP7" and/or "ZuneWP7".
+ canTrulyHover = !/Opera Mini|Android|IEMobile|Windows (Phone|CE)|(XBL|Zune)WP7/.test(navigator.userAgent);
+
+ // Since there won't be any event handlers to fire our events, do the one-and-only firing of it here and now.
+ triggerEvent();
+ return;
+ }
+
+ // CSSWG Media Queries Level 4 draft
+ // http://drafts.csswg.org/mediaqueries/#hover
+ var HOVER_NONE = "(hover: none),(-moz-hover: none),(-ms-hover: none),(-webkit-hover: none)";
+ var HOVER_ON_DEMAND = "(hover: on-demand),(-moz-hover: on-demand),(-ms-hover: on-demand),(-webkit-hover: on-demand)";
+ var HOVER_HOVER = "(hover: hover),(-moz-hover: hover),(-ms-hover: hover),(-webkit-hover: hover)";
+ if (window.matchMedia("" + HOVER_NONE + "," + HOVER_ON_DEMAND + "," + HOVER_HOVER).matches) {
+ // Browser understands the `hover` media feature
+ var hoverCallback = function (mql) {
+ var doesMatch = mql.matches;
+ if (doesMatch !== canTrulyHover) {
+ canTrulyHover = doesMatch;
+ triggerEvent();
+ }
+ };
+ var atHoverQuery = window.matchMedia(HOVER_HOVER);
+ atHoverQuery.addListener(hoverCallback);
+ hoverCallback(atHoverQuery);
+ return;
+ }
+
+ // Check for touch support instead.
+ // Touch generally implies that hovering is merely emulated,
+ // which doesn't count as true hovering support for our purposes
+ // due to the quirkiness of the emulation (e.g. :hover being sticky).
+
+ // W3C Pointer Events PR, 16 December 2014
+ // http://www.w3.org/TR/2014/PR-pointerevents-20141216/
+ // Prefixed in IE10, per http://caniuse.com/#feat=pointer
+ if (window.PointerEvent || window.MSPointerEvent) {
+ // Browser supports Pointer Events
+
+ // Browser supports touch if it has touch points
+ /* jshint -W018 */
+ canTrulyHover = !((window.navigator.maxTouchPoints || window.navigator.msMaxTouchPoints) > 0);
+ /* jshint +W018 */
+ triggerEvent();
+ return;
+ }
+
+ // Mozilla's -moz-touch-enabled
+ // https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Media_queries#-moz-touch-enabled
+ var touchEnabledQuery = window.matchMedia("(touch-enabled),(-moz-touch-enabled),(-ms-touch-enabled),(-webkit-touch-enabled)");
+ if (touchEnabledQuery.matches) {
+ canTrulyHover = false;
+ triggerEvent();
+ return;
+ }
+
+ // W3C Touch Events REC, 10 October 2013
+ // http://www.w3.org/TR/2013/REC-touch-events-20131010/
+ if ("ontouchstart" in window) {
+ canTrulyHover = false;
+ triggerEvent();
+ return;
+ }
+
+ // UA's pointer is non-touch and thus likely either supports true hovering or at least does not try to emulate it.
+ canTrulyHover = true;
+ triggerEvent();
+})();function supportsTrueHover() {
+ return canTrulyHover;
+}
+exports.__esModule = true;
+},{"jquery":undefined}]},{},[1])(1)
+});
+/** =======================================================================
+ * Bootstrap: alert.js v4.0.0
* http://getbootstrap.com/javascript/#alerts
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
+ * ========================================================================
+ * @fileoverview - Bootstrap's generic alert component. Add dismiss
+ * functionality to all alert messages with this plugin.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.alert
+ * + $.alert.noConflict
+ * + $.alert.Constructor
+ * + $.alert.Constructor.VERSION
+ * + $.alert.Constructor.prototype.close
+ *
+ * ========================================================================
+ */
-+function ($) {
- 'use strict';
+'use strict';
- // ALERT CLASS DEFINITION
- // ======================
- var dismiss = '[data-dismiss="alert"]'
- var Alert = function (el) {
- $(el).on('click', dismiss, this.close)
+/**
+ * Our Alert class.
+ * @param {Element=} opt_element
+ * @constructor
+ */
+var Alert = function (opt_element) {
+ if (opt_element) {
+ $(opt_element).on('click', Alert._DISMISS_SELECTOR, Alert._handleDismiss(this))
}
+}
- Alert.VERSION = '3.3.2'
- Alert.TRANSITION_DURATION = 150
+/**
+ * @const
+ * @type {string}
+ */
+Alert['VERSION'] = '4.0.0'
- Alert.prototype.close = function (e) {
- var $this = $(this)
- var selector = $this.attr('data-target')
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
- }
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Alert._NAME = 'alert'
- var $parent = $(selector)
- if (e) e.preventDefault()
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Alert._DATA_KEY = 'bs.alert'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Alert._DISMISS_SELECTOR = '[data-dismiss="alert"]'
- if (!$parent.length) {
- $parent = $this.closest('.alert')
- }
- $parent.trigger(e = $.Event('close.bs.alert'))
+/**
+ * @const
+ * @type {number}
+ * @private
+ */
+Alert._TRANSITION_DURATION = 150
+
+
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+Alert._JQUERY_NO_CONFLICT = $.fn[Alert._NAME]
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Alert._Event = {
+ CLOSE : 'close.bs.alert',
+ CLOSED : 'closed.bs.alert'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Alert._ClassName = {
+ ALERT : 'alert',
+ FADE : 'fade',
+ IN : 'in'
+}
+
+
+/**
+ * Provides the jQuery Interface for the alert component.
+ * @param {string=} opt_config
+ * @this {jQuery}
+ * @return {jQuery}
+ * @private
+ */
+Alert._jQueryInterface = function (opt_config) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data(Alert._DATA_KEY)
+
+ if (!data) {
+ data = new Alert(this)
+ $this.data(Alert._DATA_KEY, data)
+ }
- if (e.isDefaultPrevented()) return
+ if (opt_config === 'close') {
+ data[opt_config](this)
+ }
+ })
+}
- $parent.removeClass('in')
- function removeElement() {
- // detach from parent, fire event then clean up data
- $parent.detach().trigger('closed.bs.alert').remove()
+/**
+ * Close the alert component
+ * @param {Alert} alertInstance
+ * @return {Function}
+ * @private
+ */
+Alert._handleDismiss = function (alertInstance) {
+ return function (event) {
+ if (event) {
+ event.preventDefault()
}
- $.support.transition && $parent.hasClass('fade') ?
- $parent
- .one('bsTransitionEnd', removeElement)
- .emulateTransitionEnd(Alert.TRANSITION_DURATION) :
- removeElement()
+ alertInstance['close'](this)
}
+}
- // ALERT PLUGIN DEFINITION
- // =======================
+/**
+ * Close the alert component
+ * @param {Element} element
+ */
+Alert.prototype['close'] = function (element) {
+ var rootElement = this._getRootElement(element)
+ var customEvent = this._triggerCloseEvent(rootElement)
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.alert')
+ if (customEvent.isDefaultPrevented()) return
- if (!data) $this.data('bs.alert', (data = new Alert(this)))
- if (typeof option == 'string') data[option].call($this)
- })
+ 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]
}
- var old = $.fn.alert
+ if (!parent) {
+ parent = $(element).closest('.' + Alert._ClassName.ALERT)[0]
+ }
- $.fn.alert = Plugin
- $.fn.alert.Constructor = Alert
+ return parent
+}
- // ALERT NO CONFLICT
- // =================
+/**
+ * Trigger close event on element
+ * @return {$.Event}
+ * @private
+ */
+Alert.prototype._triggerCloseEvent = function (element) {
+ var closeEvent = $.Event(Alert._Event.CLOSE)
+ $(element).trigger(closeEvent)
+ return closeEvent
+}
+
+
+/**
+ * Trigger closed event and remove element from dom
+ * @private
+ */
+Alert.prototype._removeElement = function (element) {
+ $(element).removeClass(Alert._ClassName.IN)
- $.fn.alert.noConflict = function () {
- $.fn.alert = old
- return this
+ if (!Bootstrap.transition || !$(element).hasClass(Alert._ClassName.FADE)) {
+ this._destroyElement(element)
+ return
}
+ $(element)
+ .one(Bootstrap.TRANSITION_END, this._destroyElement.bind(this, element))
+ .emulateTransitionEnd(Alert._TRANSITION_DURATION)
+}
- // ALERT DATA-API
- // ==============
- $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
+/**
+ * clean up any lingering jquery data and kill element
+ * @private
+ */
+Alert.prototype._destroyElement = function (element) {
+ $(element)
+ .detach()
+ .trigger(Alert._Event.CLOSED)
+ .remove()
+}
-}(jQuery);
-/* ========================================================================
- * Bootstrap: button.js v3.3.2
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Alert._NAME] = Alert._jQueryInterface
+
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Alert._NAME]['Constructor'] = Alert
+
+
+/**
+ * @return {Function}
+ */
+$.fn[Alert._NAME]['noConflict'] = function () {
+ $.fn[Alert._NAME] = Alert._JQUERY_NO_CONFLICT
+ return Alert._jQueryInterface
+}
+
+
+/**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+$(document).on('click.bs.alert.data-api', Alert._DISMISS_SELECTOR, Alert._handleDismiss(new Alert))
+
+/** =======================================================================
+ * Bootstrap: button.js v4.0.0
* http://getbootstrap.com/javascript/#buttons
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @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';
-+function ($) {
- 'use strict';
- // BUTTON PUBLIC CLASS DEFINITION
- // ==============================
+/**
+ * Our Button class.
+ * @param {Element!} element
+ * @constructor
+ */
+var Button = function (element) {
- var Button = function (element, options) {
- this.$element = $(element)
- this.options = $.extend({}, Button.DEFAULTS, options)
- this.isLoading = false
- }
+ /** @private {Element} */
+ this._element = element
- Button.VERSION = '3.3.2'
+}
- Button.DEFAULTS = {
- loadingText: 'loading...'
- }
- Button.prototype.setState = function (state) {
- var d = 'disabled'
- var $el = this.$element
- var val = $el.is('input') ? 'val' : 'html'
- var data = $el.data()
+/**
+ * @const
+ * @type {string}
+ */
+Button['VERSION'] = '4.0.0'
- state = state + 'Text'
- if (data.resetText == null) $el.data('resetText', $el[val]())
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Button._NAME = 'button'
- // 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)
- }
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Button._DATA_KEY = 'bs.button'
- Button.prototype.toggle = function () {
- var changed = true
- var $parent = this.$element.closest('[data-toggle="buttons"]')
- if ($parent.length) {
- var $input = this.$element.find('input')
- if ($input.prop('type') == 'radio') {
- if ($input.prop('checked') && this.$element.hasClass('active')) changed = false
- else $parent.find('.active').removeClass('active')
- }
- if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change')
- } else {
- this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
+/**
+ * @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 (changed) this.$element.toggleClass('active')
+ 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)
+ }
+ }
+ }
+
+ if (triggerChangeEvent) {
+ input.checked = !$(this._element).hasClass(Button._ClassName.ACTIVE)
+ $(this._element).trigger('change')
+ }
+ }
+ } else {
+ this._element.setAttribute('aria-pressed', !$(this._element).hasClass(Button._ClassName.ACTIVE))
}
+ if (triggerChangeEvent) {
+ $(this._element).toggleClass(Button._ClassName.ACTIVE)
+ }
+}
- // BUTTON PLUGIN DEFINITION
- // ========================
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.button')
- var options = typeof option == 'object' && option
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
- if (!data) $this.data('bs.button', (data = new Button(this, options)))
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Button._NAME] = Button._jQueryInterface
- if (option == 'toggle') data.toggle()
- else if (option) data.setState(option)
- })
- }
- var old = $.fn.button
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Button._NAME]['Constructor'] = 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
- // ==================
- $.fn.button.noConflict = function () {
- $.fn.button = old
- return this
- }
+/**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+$(document)
+ .on('click.bs.button.data-api', Button._Selector.DATA_TOGGLE_CARROT, function (event) {
+ event.preventDefault()
- // BUTTON DATA-API
- // ===============
+ var button = event.target
- $(document)
- .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
- var $btn = $(e.target)
- if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
- Plugin.call($btn, 'toggle')
- e.preventDefault()
- })
- .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
- $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
- })
+ if (!$(button).hasClass(Button._ClassName.BUTTON)) {
+ button = $(button).closest(Button._Selector.BUTTON)
+ }
-}(jQuery);
+ Button._jQueryInterface.call($(button), 'toggle')
+ })
+ .on('focus.bs.button.data-api blur.bs.button.data-api', Button._Selector.DATA_TOGGLE_CARROT, function (event) {
+ var button = $(event.target).closest(Button._Selector.BUTTON)[0]
+ $(button).toggleClass(Button._ClassName.FOCUS, /^focus(in)?$/.test(event.type))
+ })
-/* ========================================================================
- * Bootstrap: carousel.js v3.3.2
+/** =======================================================================
+ * Bootstrap: carousel.js v4.0.0
* http://getbootstrap.com/javascript/#carousel
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @fileoverview - Bootstrap's carousel. A slideshow component for cycling
+ * through elements, like a carousel. Nested carousels are not supported.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.carousel
+ * + $.carousel.noConflict
+ * + $.carousel.Constructor
+ * + $.carousel.Constructor.VERSION
+ * + $.carousel.Constructor.Defaults
+ * + $.carousel.Constructor.Defaults.interval
+ * + $.carousel.Constructor.Defaults.pause
+ * + $.carousel.Constructor.Defaults.wrap
+ * + $.carousel.Constructor.Defaults.keyboard
+ * + $.carousel.Constructor.Defaults.slide
+ * + $.carousel.Constructor.prototype.next
+ * + $.carousel.Constructor.prototype.prev
+ * + $.carousel.Constructor.prototype.pause
+ * + $.carousel.Constructor.prototype.cycle
+ *
+ * ========================================================================
+ */
+'use strict';
-+function ($) {
- 'use strict';
- // CAROUSEL CLASS DEFINITION
- // =========================
+/**
+ * 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
- var Carousel = function (element, options) {
- this.$element = $(element)
- this.$indicators = this.$element.find('.carousel-indicators')
- this.options = options
- this.paused =
- this.sliding =
- this.interval =
- this.$active =
- this.$items = null
+ /** @private {boolean} */
+ this._isPaused = false
- this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))
+ /** @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'
- 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))
+
+/**
+ * @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)
+
+ if (!selector) {
+ return
}
- Carousel.VERSION = '3.3.2'
+ var target = $(selector)[0]
+
+ if (!target || !$(target).hasClass(Carousel._ClassName.CAROUSEL)) {
+ return
+ }
- Carousel.TRANSITION_DURATION = 600
+ var config = $.extend({}, $(target).data(), $(this).data())
- Carousel.DEFAULTS = {
- interval: 5000,
- pause: 'hover',
- wrap: true,
- keyboard: true
+ var slideIndex = this.getAttribute('data-slide-to')
+ if (slideIndex) {
+ config.interval = false
}
- Carousel.prototype.keydown = function (e) {
- if (/input|textarea/i.test(e.target.tagName)) return
- switch (e.which) {
- case 37: this.prev(); break
- case 39: this.next(); break
- default: return
- }
+ Carousel._jQueryInterface.call($(target), config)
- e.preventDefault()
+ if (slideIndex) {
+ $(target).data(Carousel._DATA_KEY).to(slideIndex)
}
- Carousel.prototype.cycle = function (e) {
- e || (this.paused = false)
+ event.preventDefault()
+}
- this.interval && clearInterval(this.interval)
- this.options.interval
- && !this.paused
- && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
+/**
+ * Advance the carousel to the next slide
+ */
+Carousel.prototype['next'] = function () {
+ if (!this._isSliding) {
+ this._slide(Carousel._Direction.NEXT)
+ }
+}
- return this
+
+/**
+ * Return the carousel to the previous slide
+ */
+Carousel.prototype['prev'] = function () {
+ if (!this._isSliding) {
+ this._slide(Carousel._Direction.PREVIOUS)
}
+}
+
- Carousel.prototype.getItemIndex = function (item) {
- this.$items = item.parent().children('.carousel-item')
- return this.$items.index(item || this.$active)
+/**
+ * Pause the carousel cycle
+ * @param {Event=} opt_event
+ */
+Carousel.prototype['pause'] = function (opt_event) {
+ if (!opt_event) {
+ this._isPaused = true
}
- Carousel.prototype.getItemForDirection = function (direction, active) {
- var activeIndex = this.getItemIndex(active)
- var willWrap = (direction == 'prev' && activeIndex === 0)
- || (direction == 'next' && activeIndex == (this.$items.length - 1))
- if (willWrap && !this.options.wrap) return active
- var delta = direction == 'prev' ? -1 : 1
- var itemIndex = (activeIndex + delta) % this.$items.length
- return this.$items.eq(itemIndex)
+ if ($(this._element).find(Carousel._Selector.NEXT_PREV)[0] && Bootstrap.transition) {
+ $(this._element).trigger(Bootstrap.transition.end)
+ this['cycle'](true)
}
- Carousel.prototype.to = function (pos) {
- var that = this
- var activeIndex = this.getItemIndex(this.$active = this.$element.find('.carousel-item.active'))
+ clearInterval(this._interval)
+ this._interval = null
+}
- if (pos > (this.$items.length - 1) || pos < 0) return
- if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid"
- if (activeIndex == pos) return this.pause().cycle()
+/**
+ * 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.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos))
+ if (this._interval) {
+ clearInterval(this._interval)
+ this._interval = null
}
- Carousel.prototype.pause = function (e) {
- e || (this.paused = true)
+ if (this._config['interval'] && !this._isPaused) {
+ this._interval = setInterval(this['next'].bind(this), this._config['interval'])
+ }
+}
+
+
+/**
+ * @return {Object}
+ */
+Carousel.prototype['getConfig'] = function () {
+ return this._config
+}
- if (this.$element.find('.next, .prev').length && $.support.transition) {
- this.$element.trigger($.support.transition.end)
- this.cycle(true)
- }
- this.interval = clearInterval(this.interval)
+/**
+ * Move active carousel item to specified index
+ * @param {number} index
+ */
+Carousel.prototype.to = function (index) {
+ this._activeElement = $(this._element).find(Carousel._Selector.ACTIVE_ITEM)[0]
- return this
+ var activeIndex = this._getItemIndex(this._activeElement)
+
+ if (index > (this._items.length - 1) || index < 0) {
+ return
}
- Carousel.prototype.next = function () {
- if (this.sliding) return
- return this.slide('next')
+ if (this._isSliding) {
+ $(this._element).one(Carousel._Event.SLID, function () { this.to(index) }.bind(this))
+ return
}
- Carousel.prototype.prev = function () {
- if (this.sliding) return
- return this.slide('prev')
+ if (activeIndex == index) {
+ this['pause']()
+ this['cycle']()
+ return
}
- Carousel.prototype.slide = function (type, next) {
- var $active = this.$element.find('.carousel-item.active')
- var $next = next || this.getItemForDirection(type, $active)
- var isCycling = this.interval
- var direction = type == 'next' ? 'left' : 'right'
- var that = this
+ var direction = index > activeIndex ?
+ Carousel._Direction.NEXT :
+ Carousel._Direction.PREVIOUS
- if ($next.hasClass('active')) return (this.sliding = false)
+ this._slide(direction, this._items[index])
+}
- var relatedTarget = $next[0]
- var slideEvent = $.Event('slide.bs.carousel', {
- relatedTarget: relatedTarget,
- direction: direction
- })
- this.$element.trigger(slideEvent)
- if (slideEvent.isDefaultPrevented()) return
-
- this.sliding = true
-
- 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)
- }
- isCycling && this.cycle()
+/**
+ * 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 PLUGIN DEFINITION
- // ==========================
+/**
+ * Keydown handler
+ * @param {Event} event
+ * @private
+ */
+Carousel.prototype._keydown = function (event) {
+ event.preventDefault()
- 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 (/input|textarea/i.test(event.target.tagName)) 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()
- })
+ switch (event.which) {
+ case 37: this['prev'](); break
+ case 39: this['next'](); break
+ default: return
}
+}
- var old = $.fn.carousel
- $.fn.carousel = Plugin
- $.fn.carousel.Constructor = 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)
+}
- // CAROUSEL NO CONFLICT
- // ====================
- $.fn.carousel.noConflict = function () {
- $.fn.carousel = old
- return this
+/**
+ * Get next displayed item based on direction
+ * @param {Carousel._Direction} direction
+ * @param {Element} activeElement
+ * @return {Element}
+ * @private
+ */
+Carousel.prototype._getItemByDirection = function (direction, activeElement) {
+ var activeIndex = this._getItemIndex(activeElement)
+ var isGoingToWrap = (direction === Carousel._Direction.PREVIOUS && activeIndex === 0) ||
+ (direction === Carousel._Direction.NEXT && activeIndex == (this._items.length - 1))
+
+ if (isGoingToWrap && !this._config['wrap']) {
+ return activeElement
}
+ var delta = direction == Carousel._Direction.PREVIOUS ? -1 : 1
+ var itemIndex = (activeIndex + delta) % this._items.length
- // CAROUSEL DATA-API
- // =================
+ return itemIndex === -1 ? this._items[this._items.length - 1] : this._items[itemIndex]
+}
- 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)
+/**
+ * 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
+ })
- if (slideIndex) {
- $target.data('bs.carousel').to(slideIndex)
+ $(this._element).trigger(slideEvent)
+
+ return slideEvent
+}
+
+
+/**
+ * Set the active indicator if available
+ * @param {Element} element
+ * @private
+ */
+Carousel.prototype._setActiveIndicatorElement = function (element) {
+ if (this._indicatorsElement) {
+ $(this._indicatorsElement)
+ .find(Carousel._Selector.ACTIVE)
+ .removeClass(Carousel._ClassName.ACTIVE)
+
+ var nextIndicator = this._indicatorsElement.children[this._getItemIndex(element)]
+ if (nextIndicator) {
+ $(nextIndicator).addClass(Carousel._ClassName.ACTIVE)
}
+ }
+}
+
+
+/**
+ * Slide the carousel element in a direction
+ * @param {Carousel._Direction} direction
+ * @param {Element=} opt_nextElement
+ */
+Carousel.prototype._slide = function (direction, opt_nextElement) {
+ var activeElement = $(this._element).find(Carousel._Selector.ACTIVE_ITEM)[0]
+ var nextElement = opt_nextElement || activeElement && this._getItemByDirection(direction, activeElement)
+
+ var isCycling = !!this._interval
- e.preventDefault()
+ var directionalClassName = direction == Carousel._Direction.NEXT ?
+ Carousel._ClassName.LEFT :
+ Carousel._ClassName.RIGHT
+
+ if (nextElement && $(nextElement).hasClass(Carousel._ClassName.ACTIVE)) {
+ this._isSliding = false
+ return
}
- $(document)
- .on('click.bs.carousel.data-api', '[data-slide]', clickHandler)
- .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler)
+ var slideEvent = this._triggerSlideEvent(nextElement, directionalClassName)
+ if (slideEvent.isDefaultPrevented()) {
+ return
+ }
- $(window).on('load', function () {
- $('[data-ride="carousel"]').each(function () {
- var $carousel = $(this)
- Plugin.call($carousel, $carousel.data())
- })
- })
+ if (!activeElement || !nextElement) {
+ // some weirdness is happening, so we bail (maybe throw exception here alerting user that they're dom is off
+ return
+ }
-}(jQuery);
+ 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)
-/* ========================================================================
- * Bootstrap: collapse.js v3.3.2
+ $(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)
+
+$(window).on('load', function () {
+ $('[data-ride="carousel"]').each(function () {
+ var $carousel = $(this)
+ Carousel._jQueryInterface.call($carousel, /** @type {Object} */ ($carousel.data()))
+ })
+})
+
+/** =======================================================================
+ * Bootstrap: collapse.js v4.0.0
* http://getbootstrap.com/javascript/#collapse
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @fileoverview - Bootstrap's collapse plugin. Flexible support for
+ * collapsible components like accordions and navigation.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.carousel
+ * + $.carousel.noConflict
+ * + $.carousel.Constructor
+ * + $.carousel.Constructor.VERSION
+ * + $.carousel.Constructor.Defaults
+ * + $.carousel.Constructor.Defaults.toggle
+ * + $.carousel.Constructor.Defaults.trigger
+ * + $.carousel.Constructor.Defaults.parent
+ * + $.carousel.Constructor.prototype.toggle
+ * + $.carousel.Constructor.prototype.show
+ * + $.carousel.Constructor.prototype.hide
+ *
+ * ========================================================================
+ */
+'use strict';
-+function ($) {
- 'use strict';
- // COLLAPSE PUBLIC CLASS DEFINITION
- // ================================
+/**
+ * Our collapse class.
+ * @param {Element!} element
+ * @param {Object=} opt_config
+ * @constructor
+ */
+var Collapse = function (element, opt_config) {
- var Collapse = function (element, options) {
- this.$element = $(element)
- this.options = $.extend({}, Collapse.DEFAULTS, options)
- this.$trigger = $(this.options.trigger).filter('[href="#' + element.id + '"], [data-target="#' + element.id + '"]')
- this.transitioning = null
+ /** @private {Element} */
+ this._element = element
- if (this.options.parent) {
- this.$parent = this.getParent()
- } else {
- this.addAriaAndCollapsedClass(this.$element, this.$trigger)
- }
+ /** @private {Object} */
+ this._config = $.extend({}, Collapse['Defaults'], opt_config)
- if (this.options.toggle) this.toggle()
- }
+ /** @private {Element} */
+ this._trigger = typeof this._config['trigger'] == 'string' ?
+ $(this._config['trigger'])[0] : this._config['trigger']
- Collapse.VERSION = '3.3.2'
+ /** @private {boolean} */
+ this._isTransitioning = false
- Collapse.TRANSITION_DURATION = 350
+ /** @private {?Element} */
+ this._parent = this._config['parent'] ? this._getParent() : null
- Collapse.DEFAULTS = {
- toggle: true,
- trigger: '[data-toggle="collapse"]'
+ if (!this._config['parent']) {
+ this._addAriaAndCollapsedClass(this._element, this._trigger)
}
- Collapse.prototype.dimension = function () {
- var hasWidth = this.$element.hasClass('width')
- return hasWidth ? 'width' : 'height'
+ if (this._config['toggle']) {
+ this['toggle']()
}
- Collapse.prototype.show = function () {
- if (this.transitioning || this.$element.hasClass('in')) return
+}
- var activesData
- var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing')
- if (actives && actives.length) {
- activesData = actives.data('bs.collapse')
- if (activesData && activesData.transitioning) return
- }
+/**
+ * @const
+ * @type {string}
+ */
+Collapse['VERSION'] = '4.0.0'
- var startEvent = $.Event('show.bs.collapse')
- this.$element.trigger(startEvent)
- if (startEvent.isDefaultPrevented()) return
- if (actives && actives.length) {
- Plugin.call(actives, 'hide')
- activesData || actives.data('bs.collapse', null)
- }
+/**
+ * @const
+ * @type {Object}
+ */
+Collapse['Defaults'] = {
+ 'toggle' : true,
+ 'trigger' : '[data-toggle="collapse"]',
+ 'parent' : null
+}
- var dimension = this.dimension()
- this.$element
- .removeClass('collapse')
- .addClass('collapsing')[dimension](0)
- .attr('aria-expanded', true)
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Collapse._NAME = 'collapse'
- this.$trigger
- .removeClass('collapsed')
- .attr('aria-expanded', true)
- this.transitioning = 1
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Collapse._DATA_KEY = 'bs.collapse'
- var complete = function () {
- this.$element
- .removeClass('collapsing')
- .addClass('collapse in')[dimension]('')
- this.transitioning = 0
- this.$element
- .trigger('shown.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
}
- if (!$.support.transition) return complete.call(this)
+ if (!data) {
+ data = new Collapse(this, config)
+ $this.data(Collapse._DATA_KEY, data)
+ }
- var scrollSize = $.camelCase(['scroll', dimension].join('-'))
+ if (typeof opt_config == 'string') {
+ data[opt_config]()
+ }
+ })
+}
- this.$element
- .one('bsTransitionEnd', $.proxy(complete, this))
- .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
- }
- Collapse.prototype.hide = function () {
- if (this.transitioning || !this.$element.hasClass('in')) return
+/**
+ * Function for getting target element from element
+ * @return {Element}
+ * @private
+ */
+Collapse._getTargetFromElement = function (element) {
+ var selector = Bootstrap.getSelectorFromElement(element)
- var startEvent = $.Event('hide.bs.collapse')
- this.$element.trigger(startEvent)
- if (startEvent.isDefaultPrevented()) return
+ return selector ? $(selector)[0] : null
+}
- var dimension = this.dimension()
- this.$element[dimension](this.$element[dimension]())[0].offsetHeight
+/**
+ * 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
- .addClass('collapsing')
- .removeClass('collapse in')
- .attr('aria-expanded', false)
- this.$trigger
- .addClass('collapsed')
- .attr('aria-expanded', false)
+/**
+ * Show's the collapsing element
+ */
+Collapse.prototype['show'] = function () {
+ if (this._isTransitioning || $(this._element).hasClass(Collapse._ClassName.IN)) {
+ return
+ }
- this.transitioning = 1
+ var activesData, actives
- var complete = function () {
- this.transitioning = 0
- this.$element
- .removeClass('collapsing')
- .addClass('collapse')
- .trigger('hidden.bs.collapse')
+ if (this._parent) {
+ actives = $.makeArray($(Collapse._Selector.ACTIVES))
+ if (!actives.length) {
+ actives = null
}
+ }
- if (!$.support.transition) return complete.call(this)
+ if (actives) {
+ activesData = $(actives).data(Collapse._DATA_KEY)
+ if (activesData && activesData._isTransitioning) {
+ return
+ }
+ }
- this.$element
- [dimension](0)
- .one('bsTransitionEnd', $.proxy(complete, this))
- .emulateTransitionEnd(Collapse.TRANSITION_DURATION)
+ var startEvent = $.Event(Collapse._Event.SHOW)
+ $(this._element).trigger(startEvent)
+ if (startEvent.isDefaultPrevented()) {
+ return
}
- Collapse.prototype.toggle = function () {
- this[this.$element.hasClass('in') ? 'hide' : 'show']()
+ if (actives) {
+ Collapse._jQueryInterface.call($(actives), 'hide')
+ if (!activesData) {
+ $(actives).data(Collapse._DATA_KEY, null)
+ }
}
- Collapse.prototype.getParent = function () {
- return $(this.options.parent)
- .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
- .each($.proxy(function (i, element) {
- var $element = $(element)
- this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)
- }, this))
- .end()
+ var dimension = this._getDimension()
+
+ $(this._element)
+ .removeClass(Collapse._ClassName.COLLAPSE)
+ .addClass(Collapse._ClassName.COLLAPSING)
+
+ this._element.style[dimension] = 0
+ this._element.setAttribute('aria-expanded', true)
+
+ if (this._trigger) {
+ $(this._trigger).removeClass(Collapse._ClassName.COLLAPSED)
+ this._trigger.setAttribute('aria-expanded', true)
}
- Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
- var isOpen = $element.hasClass('in')
+ this['setTransitioning'](true)
- $element.attr('aria-expanded', isOpen)
- $trigger
- .toggleClass('collapsed', !isOpen)
- .attr('aria-expanded', isOpen)
+ var complete = function () {
+ $(this._element)
+ .removeClass(Collapse._ClassName.COLLAPSING)
+ .addClass(Collapse._ClassName.COLLAPSE)
+ .addClass(Collapse._ClassName.IN)
+
+ this._element.style[dimension] = ''
+
+ this['setTransitioning'](false)
+
+ $(this._element).trigger(Collapse._Event.SHOWN)
+ }.bind(this)
+
+ if (!Bootstrap.transition) {
+ complete()
+ return
}
- function getTargetFromTrigger($trigger) {
- var href
- var target = $trigger.attr('data-target')
- || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
+ var scrollSize = 'scroll' + (dimension[0].toUpperCase() + dimension.slice(1))
+
+ $(this._element)
+ .one(Bootstrap.TRANSITION_END, complete)
+ .emulateTransitionEnd(Collapse._TRANSITION_DURATION)
- return $(target)
+ 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
}
+ var startEvent = $.Event(Collapse._Event.HIDE)
+ $(this._element).trigger(startEvent)
+ if (startEvent.isDefaultPrevented()) return
- // COLLAPSE PLUGIN DEFINITION
- // ==========================
+ var dimension = this._getDimension()
+ var offsetDimension = dimension === Collapse._Dimension.WIDTH ?
+ 'offsetWidth' : 'offsetHeight'
- 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)
+ this._element.style[dimension] = this._element[offsetDimension] + 'px'
- if (!data && options.toggle && option == 'show') options.toggle = false
- if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
- if (typeof option == 'string') data[option]()
- })
+ 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)
}
- var old = $.fn.collapse
+ this['setTransitioning'](true)
- $.fn.collapse = Plugin
- $.fn.collapse.Constructor = Collapse
+ var complete = function () {
+ this['setTransitioning'](false)
+ $(this._element)
+ .removeClass(Collapse._ClassName.COLLAPSING)
+ .addClass(Collapse._ClassName.COLLAPSE)
+ .trigger(Collapse._Event.HIDDEN)
+ }.bind(this)
- // COLLAPSE NO CONFLICT
- // ====================
+ this._element.style[dimension] = 0
- $.fn.collapse.noConflict = function () {
- $.fn.collapse = old
- return this
+ if (!Bootstrap.transition) {
+ return complete()
}
+ $(this._element)
+ .one(Bootstrap.TRANSITION_END, complete)
+ .emulateTransitionEnd(Collapse._TRANSITION_DURATION)
+}
- // COLLAPSE DATA-API
- // =================
- $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
- var $this = $(this)
- if (!$this.attr('data-target')) e.preventDefault()
+/**
+ * @param {boolean} isTransitioning
+ */
+Collapse.prototype['setTransitioning'] = function (isTransitioning) {
+ this._isTransitioning = isTransitioning
+}
- var $target = getTargetFromTrigger($this)
- var data = $target.data('bs.collapse')
- var option = data ? 'toggle' : $.extend({}, $this.data(), { trigger: this })
- Plugin.call($target, option)
- })
+/**
+ * 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
+}
-}(jQuery);
-/* ========================================================================
- * Bootstrap: dropdown.js v3.3.2
- * http://getbootstrap.com/javascript/#dropdowns
+/**
+ * Returns the parent element
+ * @return {Element}
+ * @private
+ */
+Collapse.prototype._getParent = function () {
+ var selector = '[data-toggle="collapse"][data-parent="' + this._config['parent'] + '"]'
+ var parent = $(this._config['parent'])[0]
+ var elements = /** @type {Array.<Element>} */ ($.makeArray($(parent).find(selector)))
+
+ for (var i = 0; i < elements.length; i++) {
+ this._addAriaAndCollapsedClass(Collapse._getTargetFromElement(elements[i]), elements[i])
+ }
+
+ return parent
+}
+
+
+/**
+ * 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)
+ }
+ }
+}
+
+
+
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Collapse._NAME] = Collapse._jQueryInterface
+
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Collapse._NAME]['Constructor'] = Collapse
+
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Collapse._NAME]['noConflict'] = function () {
+ $.fn[Collapse._NAME] = Collapse._JQUERY_NO_CONFLICT
+ return this
+}
+
+
+/**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+$(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (event) {
+ event.preventDefault()
+
+ var target = Collapse._getTargetFromElement(this)
+
+ var data = $(target).data(Collapse._DATA_KEY)
+ var config = data ? 'toggle' : $.extend({}, $(this).data(), { trigger: this })
+
+ Collapse._jQueryInterface.call($(target), config)
+})
+
+/** =======================================================================
+ * Bootstrap: dropdown.js v4.0.0
+ * http://getbootstrap.com/javascript/#dropdown
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @fileoverview - Add dropdown menus to nearly anything with this simple
+ * plugin, including the navbar, tabs, and pills.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.dropdown
+ * + $.dropdown.noConflict
+ * + $.dropdown.Constructor
+ * + $.dropdown.Constructor.VERSION
+ * + $.dropdown.Constructor.prototype.toggle
+ *
+ * ========================================================================
+ */
+'use strict';
-+function ($) {
- 'use strict';
- // DROPDOWN CLASS DEFINITION
- // =========================
+/**
+ * Our dropdown class.
+ * @param {Element!} element
+ * @constructor
+ */
+var Dropdown = function (element) {
+ $(element).on('click.bs.dropdown', this['toggle'])
+}
- var backdrop = '.dropdown-backdrop'
- var toggle = '[data-toggle="dropdown"]'
- var Dropdown = function (element) {
- $(element).on('click.bs.dropdown', this.toggle)
- }
- Dropdown.VERSION = '3.3.2'
+/**
+ * @const
+ * @type {string}
+ */
+Dropdown['VERSION'] = '4.0.0'
- Dropdown.prototype.toggle = function (e) {
- var $this = $(this)
- if ($this.is('.disabled, :disabled')) return
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Dropdown._NAME = 'dropdown'
- var $parent = getParent($this)
- var isActive = $parent.hasClass('open')
- clearMenus()
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Dropdown._DATA_KEY = 'bs.dropdown'
- if (!isActive) {
- if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
- // if mobile we use a backdrop because click events don't delegate
- $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
- }
- var relatedTarget = { relatedTarget: this }
- $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+Dropdown._JQUERY_NO_CONFLICT = $.fn[Dropdown._NAME]
- if (e.isDefaultPrevented()) return
- $this
- .trigger('focus')
- .attr('aria-expanded', 'true')
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Dropdown._Event = {
+ HIDE : 'hide.bs.dropdown',
+ HIDDEN : 'hidden.bs.dropdown',
+ SHOW : 'show.bs.dropdown',
+ SHOWN : 'shown.bs.dropdown'
+}
+
- $parent
- .toggleClass('open')
- .trigger('shown.bs.dropdown', relatedTarget)
+/**
+ * @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)))
}
- return false
- }
+ if (typeof opt_config === 'string') {
+ data[opt_config].call(this)
+ }
+ })
+}
- Dropdown.prototype.keydown = function (e) {
- if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return
- var $this = $(this)
+/**
+ * @param {Event=} opt_event
+ * @private
+ */
+Dropdown._clearMenus = function (opt_event) {
+ if (opt_event && opt_event.which == 3) {
+ return
+ }
- e.preventDefault()
- e.stopPropagation()
+ var backdrop = $(Dropdown._Selector.BACKDROP)[0]
+ if (backdrop) {
+ backdrop.parentNode.removeChild(backdrop)
+ }
- if ($this.is('.disabled, :disabled')) return
+ var toggles = /** @type {Array.<Element>} */ ($.makeArray($(Dropdown._Selector.DATA_TOGGLE)))
- var $parent = getParent($this)
- var isActive = $parent.hasClass('open')
+ for (var i = 0; i < toggles.length; i++) {
+ var parent = Dropdown._getParentFromElement(toggles[i])
+ var relatedTarget = { 'relatedTarget': toggles[i] }
- if ((!isActive && e.which != 27) || (isActive && e.which == 27)) {
- if (e.which == 27) $parent.find(toggle).trigger('focus')
- return $this.trigger('click')
+ if (!$(parent).hasClass(Dropdown._ClassName.OPEN)) {
+ continue
}
- var desc = ' li:not(.divider):visible a'
- var $items = $parent.find('[role="menu"]' + desc + ', [role="listbox"]' + desc)
+ var hideEvent = $.Event(Dropdown._Event.HIDE, relatedTarget)
+ $(parent).trigger(hideEvent)
+ if (hideEvent.isDefaultPrevented()) {
+ continue
+ }
- if (!$items.length) return
+ toggles[i].setAttribute('aria-expanded', 'false')
- var index = $items.index(e.target)
+ $(parent)
+ .removeClass(Dropdown._ClassName.OPEN)
+ .trigger(Dropdown._Event.HIDDEN, relatedTarget)
+ }
+}
- if (e.which == 38 && index > 0) index-- // up
- if (e.which == 40 && index < $items.length - 1) index++ // down
- if (!~index) index = 0
- $items.eq(index).trigger('focus')
+/**
+ * @param {Element} element
+ * @return {Element}
+ * @private
+ */
+Dropdown._getParentFromElement = function (element) {
+ var selector = Bootstrap.getSelectorFromElement(element)
+
+ if (selector) {
+ var parent = $(selector)[0]
}
- 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 }
+ return /** @type {Element} */ (parent || element.parentNode)
+}
- if (!$parent.hasClass('open')) return
- $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
+/**
+ * @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 (e.isDefaultPrevented()) return
+ event.preventDefault()
+ event.stopPropagation()
- $this.attr('aria-expanded', 'false')
- $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
- })
+ if (this.disabled || $(this).hasClass(Dropdown._ClassName.DISABLED)) {
+ return
}
- function getParent($this) {
- var selector = $this.attr('data-target')
+ var parent = Dropdown._getParentFromElement(this)
+ var isActive = $(parent).hasClass(Dropdown._ClassName.OPEN)
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+ if ((!isActive && event.which != 27) || (isActive && event.which == 27)) {
+ if (event.which == 27) {
+ var toggle = $(parent).find(Dropdown._Selector.DATA_TOGGLE)[0]
+ $(toggle).trigger('focus')
}
+ $(this).trigger('click')
+ return
+ }
- var $parent = selector && $(selector)
+ var items = $.makeArray($(Dropdown._Selector.VISIBLE_ITEMS))
- return $parent && $parent.length ? $parent : $this.parent()
+ items = items.filter(function (item) {
+ return item.offsetWidth || item.offsetHeight
+ })
+
+ if (!items.length) {
+ return
}
+ var index = items.indexOf(event.target)
- // DROPDOWN PLUGIN DEFINITION
- // ==========================
+ if (event.which == 38 && index > 0) index-- // up
+ if (event.which == 40 && index < items.length - 1) index++ // down
+ if (!~index) index = 0
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.dropdown')
+ items[index].focus()
+}
- if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
- if (typeof option == 'string') data[option].call($this)
- })
+
+/**
+ * Toggles the dropdown
+ * @this {Element}
+ * @return {boolean|undefined}
+ */
+Dropdown.prototype['toggle'] = function () {
+ if (this.disabled || $(this).hasClass(Dropdown._ClassName.DISABLED)) {
+ return
}
- var old = $.fn.dropdown
+ var parent = Dropdown._getParentFromElement(this)
+ var isActive = $(parent).hasClass(Dropdown._ClassName.OPEN)
- $.fn.dropdown = Plugin
- $.fn.dropdown.Constructor = Dropdown
+ Dropdown._clearMenus()
+ if (isActive) {
+ return false
+ }
+
+ 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)
+ }
- // DROPDOWN NO CONFLICT
- // ====================
+ var relatedTarget = { 'relatedTarget': this }
+ var showEvent = $.Event(Dropdown._Event.SHOW, relatedTarget)
- $.fn.dropdown.noConflict = function () {
- $.fn.dropdown = old
- return this
+ $(parent).trigger(showEvent)
+
+ if (showEvent.isDefaultPrevented()) {
+ return
}
+ this.focus()
+ this.setAttribute('aria-expanded', 'true')
- // APPLY TO STANDARD DROPDOWN ELEMENTS
- // ===================================
+ $(parent).toggleClass(Dropdown._ClassName.OPEN)
- $(document)
- .on('click.bs.dropdown.data-api', clearMenus)
- .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
- .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
- .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
- .on('keydown.bs.dropdown.data-api', '[role="menu"]', Dropdown.prototype.keydown)
- .on('keydown.bs.dropdown.data-api', '[role="listbox"]', Dropdown.prototype.keydown)
+ $(parent).trigger(Dropdown._Event.SHOWN, relatedTarget)
-}(jQuery);
+ return false
+}
-/* ========================================================================
- * Bootstrap: modal.js v3.3.2
- * http://getbootstrap.com/javascript/#modals
+
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Dropdown._NAME] = Dropdown._jQueryInterface
+
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Dropdown._NAME]['Constructor'] = Dropdown
+
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Dropdown._NAME]['noConflict'] = function () {
+ $.fn[Dropdown._NAME] = Dropdown._JQUERY_NO_CONFLICT
+ return this
+}
+
+
+/**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+$(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
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @fileoverview - Bootstrap's modal plugin. Modals are streamlined, but
+ * flexible, dialog prompts with the minimum required functionality and
+ * smart defaults.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.modal
+ * + $.modal.noConflict
+ * + $.modal.Constructor
+ * + $.modal.Constructor.VERSION
+ * + $.modal.Constructor.Defaults
+ * + $.modal.Constructor.Defaults.backdrop
+ * + $.modal.Constructor.Defaults.keyboard
+ * + $.modal.Constructor.Defaults.show
+ * + $.modal.Constructor.prototype.toggle
+ * + $.modal.Constructor.prototype.show
+ * + $.modal.Constructor.prototype.hide
+ *
+ * ========================================================================
+ */
+'use strict';
-+function ($) {
- 'use strict';
- // MODAL CLASS DEFINITION
- // ======================
+/**
+ * Our modal class.
+ * @param {Element} element
+ * @param {Object} config
+ * @constructor
+ */
+var Modal = function (element, config) {
- var Modal = function (element, options) {
- this.options = options
- this.$body = $(document.body)
- this.$element = $(element)
- this.$backdrop =
- this.isShown = null
- this.scrollbarWidth = 0
+ /** @private {Object} */
+ this._config = config
- 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._element = element
- Modal.VERSION = '3.3.2'
+ /** @private {Element} */
+ this._backdrop = null
- Modal.TRANSITION_DURATION = 300
- Modal.BACKDROP_TRANSITION_DURATION = 150
+ /** @private {boolean} */
+ this._isShown = false
- Modal.DEFAULTS = {
- backdrop: true,
- keyboard: true,
- show: true
- }
+ /** @private {boolean} */
+ this._isBodyOverflowing = false
- Modal.prototype.toggle = function (_relatedTarget) {
- return this.isShown ? this.hide() : this.show(_relatedTarget)
- }
+ /** @private {number} */
+ this._scrollbarWidth = 0
- Modal.prototype.show = function (_relatedTarget) {
- var that = this
- var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
+}
- this.$element.trigger(e)
- if (this.isShown || e.isDefaultPrevented()) return
+/**
+ * @const
+ * @type {string}
+ */
+Modal['VERSION'] = '4.0.0'
- this.isShown = true
- this.checkScrollbar()
- this.setScrollbar()
- this.$body.addClass('modal-open')
+/**
+ * @const
+ * @type {Object}
+ */
+Modal['Defaults'] = {
+ 'backdrop' : true,
+ 'keyboard' : true,
+ 'show' : true
+}
- this.escape()
- this.resize()
- this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Modal._NAME = 'modal'
- this.backdrop(function () {
- var transition = $.support.transition && that.$element.hasClass('fade')
- if (!that.$element.parent().length) {
- that.$element.appendTo(that.$body) // don't move modals dom position
- }
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Modal._DATA_KEY = 'bs.modal'
- that.$element
- .show()
- .scrollTop(0)
- if (that.options.backdrop) that.adjustBackdrop()
- that.adjustDialog()
+/**
+ * @const
+ * @type {number}
+ * @private
+ */
+Modal._TRANSITION_DURATION = 300
- if (transition) {
- that.$element[0].offsetWidth // force reflow
- }
- that.$element
- .addClass('in')
- .attr('aria-hidden', false)
+/**
+ * @const
+ * @type {number}
+ * @private
+ */
+Modal._BACKDROP_TRANSITION_DURATION = 150
- that.enforceFocus()
- var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+Modal._JQUERY_NO_CONFLICT = $.fn[Modal._NAME]
- transition ?
- that.$element.find('.modal-dialog') // wait for modal to slide in
- .one('bsTransitionEnd', function () {
- that.$element.trigger('focus').trigger(e)
- })
- .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
- that.$element.trigger('focus').trigger(e)
- })
- }
- Modal.prototype.hide = function (e) {
- if (e) e.preventDefault()
+/**
+ * @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'
+}
- e = $.Event('hide.bs.modal')
- this.$element.trigger(e)
- if (!this.isShown || e.isDefaultPrevented()) return
+/**
+ * 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.isShown = false
+ if (typeof opt_config == 'string') {
+ data[opt_config](opt_relatedTarget)
+
+ } else if (config['show']) {
+ data['show'](opt_relatedTarget)
+ }
+ })
+}
- this.escape()
- this.resize()
- $(document).off('focusin.bs.modal')
+/**
+ * @param {Element} relatedTarget
+ */
+Modal.prototype['toggle'] = function (relatedTarget) {
+ return this._isShown ? this['hide']() : this['show'](relatedTarget)
+}
- this.$element
- .removeClass('in')
- .attr('aria-hidden', true)
- .off('click.dismiss.bs.modal')
- $.support.transition && this.$element.hasClass('fade') ?
- this.$element
- .one('bsTransitionEnd', $.proxy(this.hideModal, this))
- .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
- this.hideModal()
+/**
+ * @param {Element} relatedTarget
+ */
+Modal.prototype['show'] = function (relatedTarget) {
+ var showEvent = $.Event(Modal._Event.SHOW, { relatedTarget: relatedTarget })
+
+ $(this._element).trigger(showEvent)
+
+ if (this._isShown || showEvent.isDefaultPrevented()) {
+ return
}
- 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))
+ this._isShown = true
+
+ this._checkScrollbar()
+ this._setScrollbar()
+
+ $(document.body).addClass(Modal._ClassName.OPEN)
+
+ this._escape()
+ this._resize()
+
+ $(this._element).on('click.dismiss.bs.modal', Modal._Selector.DATA_DISMISS, this['hide'].bind(this))
+
+ this._showBackdrop(this._showElement.bind(this, relatedTarget))
+}
+
+
+/**
+ * @param {Event} event
+ */
+Modal.prototype['hide'] = function (event) {
+ if (event) {
+ event.preventDefault()
}
- 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')
- }
+ var hideEvent = $.Event(Modal._Event.HIDE)
+
+ $(this._element).trigger(hideEvent)
+
+ if (!this._isShown || hideEvent.isDefaultPrevented()) {
+ return
}
- Modal.prototype.resize = function () {
- if (this.isShown) {
- $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
- } else {
- $(window).off('resize.bs.modal')
- }
+ 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.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')
- })
+
+/**
+ * @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.removeBackdrop = function () {
- this.$backdrop && this.$backdrop.remove()
- this.$backdrop = null
+ this._element.style.display = 'block'
+ this._element.scrollTop = 0
+
+ if (this._config['backdrop']) {
+ this._adjustBackdrop()
}
- Modal.prototype.backdrop = function (callback) {
- var that = this
- var animate = this.$element.hasClass('fade') ? 'fade' : ''
+ if (transition) {
+ Bootstrap.reflow(this._element)
+ }
- if (this.isShown && this.options.backdrop) {
- var doAnimate = $.support.transition && animate
+ $(this._element).addClass(Modal._ClassName.IN)
+ this._element.setAttribute('aria-hidden', false)
- this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
- .prependTo(this.$element)
- .on('click.dismiss.bs.modal', $.proxy(function (e) {
- if (e.target !== e.currentTarget) return
- this.options.backdrop == 'static'
- ? this.$element[0].focus.call(this.$element[0])
- : this.hide.call(this)
- }, this))
+ this._enforceFocus()
- if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
+ var shownEvent = $.Event(Modal._Event.SHOWN, { relatedTarget: relatedTarget })
- this.$backdrop.addClass('in')
+ var transitionComplete = function () {
+ this._element.focus()
+ $(this._element).trigger(shownEvent)
+ }.bind(this)
- if (!callback) return
+ if (transition) {
+ var dialog = $(this._element).find(Modal._Selector.DIALOG)[0]
+ $(dialog)
+ .one(Bootstrap.TRANSITION_END, transitionComplete)
+ .emulateTransitionEnd(Modal._TRANSITION_DURATION)
+ } else {
+ transitionComplete()
+ }
+}
- doAnimate ?
- this.$backdrop
- .one('bsTransitionEnd', callback)
- .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
- callback()
- } else if (!this.isShown && this.$backdrop) {
- this.$backdrop.removeClass('in')
- var callbackRemove = function () {
- that.removeBackdrop()
- callback && callback()
+/**
+ * @private
+ */
+Modal.prototype._enforceFocus = function () {
+ $(document)
+ .off('focusin.bs.modal') // guard against infinite focus loop
+ .on('focusin.bs.modal', function (e) {
+ if (this._element !== e.target && !$(this._element).has(e.target).length) {
+ this._element.focus()
}
- $.support.transition && this.$element.hasClass('fade') ?
- this.$backdrop
- .one('bsTransitionEnd', callbackRemove)
- .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
- callbackRemove()
+ }.bind(this))
+}
- } else if (callback) {
- callback()
- }
- }
- // these following methods are used to handle overflowing modals
+/**
+ * @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))
- Modal.prototype.handleUpdate = function () {
- if (this.options.backdrop) this.adjustBackdrop()
- this.adjustDialog()
+ } else if (!this._isShown) {
+ $(this._element).off('keydown.dismiss.bs.modal')
}
+}
+
- Modal.prototype.adjustBackdrop = function () {
- this.$backdrop
- .css('height', 0)
- .css('height', this.$element[0].scrollHeight)
+/**
+ * @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.adjustDialog = function () {
- var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
- this.$element.css({
- paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
- paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
- })
- }
+/**
+ * @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.resetAdjustments = function () {
- this.$element.css({
- paddingLeft: '',
- paddingRight: ''
- })
- }
- Modal.prototype.checkScrollbar = function () {
- this.bodyIsOverflowing = document.body.scrollHeight > document.documentElement.clientHeight
- this.scrollbarWidth = this.measureScrollbar()
+/**
+ * @private
+ */
+Modal.prototype._removeBackdrop = function () {
+ if (this._backdrop) {
+ this._backdrop.parentNode.removeChild(this._backdrop)
+ this._backdrop = null
}
+}
+
+
+/**
+ * @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
- Modal.prototype.setScrollbar = function () {
- var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
- if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
+ 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)
+
+ if (Bootstrap.transition && $(this._element).hasClass(Modal._ClassName.FADE)) {
+ $(this._backdrop)
+ .one(Bootstrap.TRANSITION_END, callbackRemove)
+ .emulateTransitionEnd(Modal._BACKDROP_TRANSITION_DURATION)
+ } else {
+ callbackRemove()
+ }
+
+ } else if (callback) {
+ callback()
}
+}
+
+
+/**
+ * ------------------------------------------------------------------------
+ * the following methods are used to handle overflowing modals
+ * todo (fat): these should probably be refactored into a
+ * ------------------------------------------------------------------------
+ */
+
+
+/**
+ * @private
+ */
+Modal.prototype._handleUpdate = function () {
+ if (this._config['backdrop']) this._adjustBackdrop()
+ this._adjustDialog()
+}
+
+/**
+ * @private
+ */
+Modal.prototype._adjustBackdrop = function () {
+ this._backdrop.style.height = 0 // todo (fat): no clue why we do this
+ this._backdrop.style.height = this._element.scrollHeight + 'px'
+}
+
+
+/**
+ * @private
+ */
+Modal.prototype._adjustDialog = function () {
+ var isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight
- Modal.prototype.resetScrollbar = function () {
- this.$body.css('padding-right', '')
+ if (!this._isBodyOverflowing && isModalOverflowing) {
+ this._element.style.paddingLeft = 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
+ if (this._isBodyOverflowing && !isModalOverflowing) {
+ this._element.style.paddingRight = this._scrollbarWidth + 'px'
}
+}
- // MODAL PLUGIN DEFINITION
- // =======================
+/**
+ * @private
+ */
+Modal.prototype._resetAdjustments = function () {
+ this._element.style.paddingLeft = ''
+ this._element.style.paddingRight = ''
+}
- function Plugin(option, _relatedTarget) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.modal')
- var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
- if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
- if (typeof option == 'string') data[option](_relatedTarget)
- else if (options.show) data.show(_relatedTarget)
- })
+/**
+ * @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'
}
+}
- var old = $.fn.modal
- $.fn.modal = Plugin
- $.fn.modal.Constructor = Modal
+/**
+ * @private
+ */
+Modal.prototype._resetScrollbar = function () {
+ document.body.style.paddingRight = ''
+}
- // MODAL NO CONFLICT
- // =================
+/**
+ * @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
+}
- $.fn.modal.noConflict = function () {
- $.fn.modal = old
- return this
- }
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Modal._NAME] = Modal._jQueryInterface
- // 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())
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Modal._NAME]['Constructor'] = Modal
- 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)
- })
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Modal._NAME]['noConflict'] = function () {
+ $.fn[Modal._NAME] = Modal._JQUERY_NO_CONFLICT
+ return this
+}
-}(jQuery);
-/* ========================================================================
- * Bootstrap: tooltip.js v3.3.2
- * http://getbootstrap.com/javascript/#tooltip
- * Inspired by the original jQuery.tipsy by Jason Frame
+/**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+$(document).on('click.bs.modal.data-api', Modal._Selector.DATA_TOGGLE, function (event) {
+ var selector = Bootstrap.getSelectorFromElement(this)
+
+ if (selector) {
+ var target = $(selector)[0]
+ }
+
+ var config = $(target).data(Modal._DATA_KEY) ? 'toggle' : $.extend({}, $(target).data(), $(this).data())
+
+ if (this.tagName == 'A') {
+ event.preventDefault()
+ }
+
+ var $target = $(target).one(Modal._Event.SHOW, function (showEvent) {
+ if (showEvent.isDefaultPrevented()) {
+ return // only register focus restorer if modal will actually get shown
+ }
+
+ $target.one(Modal._Event.HIDDEN, function () {
+ if ($(this).is(':visible')) {
+ this.focus()
+ }
+ }.bind(this))
+ }.bind(this))
+
+ Modal._jQueryInterface.call($(target), config, this)
+})
+
+/** =======================================================================
+ * Bootstrap: scrollspy.js v4.0.0
+ * http://getbootstrap.com/javascript/#scrollspy
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @fileoverview - Bootstrap's scrollspy plugin.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.scrollspy
+ * + $.scrollspy.noConflict
+ * + $.scrollspy.Constructor
+ * + $.scrollspy.Constructor.VERSION
+ * + $.scrollspy.Constructor.Defaults
+ * + $.scrollspy.Constructor.Defaults.offset
+ * + $.scrollspy.Constructor.prototype.refresh
+ *
+ * ========================================================================
+ */
+'use strict';
-+function ($) {
- 'use strict';
- // TOOLTIP PUBLIC CLASS DEFINITION
- // ===============================
+/**
+ * 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
- var Tooltip = function (element, options) {
- this.type =
- this.options =
- this.enabled =
- this.timeout =
- this.hoverState =
- this.$element = null
+ /** @private {Object} */
+ this._config = $.extend({}, ScrollSpy['Defaults'], opt_config)
- this.init('tooltip', element, options)
- }
+ /** @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'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+ScrollSpy._ClassName = {
+ DROPDOWN_MENU : 'dropdown-menu',
+ ACTIVE : 'active'
+}
- Tooltip.VERSION = '3.3.2'
- Tooltip.TRANSITION_DURATION = 150
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+ScrollSpy._Selector = {
+ DATA_SPY : '[data-spy="scroll"]',
+ ACTIVE : '.active',
+ LI_DROPDOWN : 'li.dropdown',
+ LI : 'li'
+}
- 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
+
+/**
+ * @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]()
+ }
+ })
+}
+
+
+/**
+ * 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()
}
- Tooltip.prototype.init = function (type, element, options) {
- this.enabled = true
- this.type = type
- this.$element = $(element)
- this.options = this.getOptions(options)
- this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)
+ this._offsets = []
+ this._targets = []
- var triggers = this.options.trigger.split(' ')
+ this._scrollHeight = this._getScrollHeight()
- for (var i = triggers.length; i--;) {
- var trigger = triggers[i]
+ var targets = /** @type {Array.<Element>} */ ($.makeArray($(this._selector)))
- 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'
+ targets
+ .map(function (element, index) {
+ var target
+ var targetSelector = Bootstrap.getSelectorFromElement(element)
- 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 (targetSelector) {
+ target = $(targetSelector)[0]
}
- }
- this.options.selector ?
- (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
- this.fixTitle()
+ 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']()
}
- Tooltip.prototype.getDefaults = function () {
- return Tooltip.DEFAULTS
+ if (scrollTop >= maxScroll) {
+ var target = this._targets[this._targets.length - 1]
+
+ if (this._activeTarget != target) {
+ this._activate(target)
+ }
}
- Tooltip.prototype.getOptions = function (options) {
- options = $.extend({}, this.getDefaults(), this.$element.data(), options)
+ if (this._activeTarget && scrollTop < this._offsets[0]) {
+ this._activeTarget = null
+ this._clear()
+ return
+ }
- if (options.delay && typeof options.delay == 'number') {
- options.delay = {
- show: options.delay,
- hide: options.delay
- }
+ 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])
}
+ }
+}
+
+
+/**
+ * @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)
- return options
+ for (var i = parentListItems.length; i--;) {
+ $(parentListItems[i]).addClass(ScrollSpy._ClassName.ACTIVE)
+
+ var itemParent = parentListItems[i].parentNode
+
+ if (itemParent && $(itemParent).hasClass(ScrollSpy._ClassName.DROPDOWN_MENU)) {
+ var closestDropdown = $(itemParent).closest(ScrollSpy._Selector.LI_DROPDOWN)[0]
+ $(closestDropdown).addClass(ScrollSpy._ClassName.ACTIVE)
+ }
}
- Tooltip.prototype.getDelegateOptions = function () {
- var options = {}
- var defaults = this.getDefaults()
+ $(this._scrollElement).trigger(ScrollSpy._Event.ACTIVATE, {
+ relatedTarget: target
+ })
+}
- this._options && $.each(this._options, function (key, value) {
- if (defaults[key] != value) options[key] = value
- })
- return options
+/**
+ * @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)
}
+}
- Tooltip.prototype.enter = function (obj) {
- var self = obj instanceof this.constructor ?
- obj : $(obj.currentTarget).data('bs.' + this.type)
- if (self && self.$tip && self.$tip.is(':visible')) {
- self.hoverState = 'in'
- return
- }
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
- if (!self) {
- self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
- $(obj.currentTarget).data('bs.' + this.type, self)
- }
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[ScrollSpy._NAME] = ScrollSpy._jQueryInterface
- clearTimeout(self.timeout)
- self.hoverState = 'in'
+/**
+ * @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
+}
- if (!self.options.delay || !self.options.delay.show) return self.show()
- self.timeout = setTimeout(function () {
- if (self.hoverState == 'in') self.show()
- }, self.options.delay.show)
+/**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+$(window).on('load.bs.scrollspy.data-api', function () {
+ var scrollSpys = /** @type {Array.<Element>} */ ($.makeArray($(ScrollSpy._Selector.DATA_SPY)))
+
+ for (var i = scrollSpys.length; i--;) {
+ var $spy = $(scrollSpys[i])
+ ScrollSpy._jQueryInterface.call($spy, /** @type {Object|null} */ ($spy.data()))
}
+})
- Tooltip.prototype.leave = function (obj) {
- var self = obj instanceof this.constructor ?
- obj : $(obj.currentTarget).data('bs.' + this.type)
+/** =======================================================================
+ * 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
+ *
+ * ========================================================================
+ */
- if (!self) {
- self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
- $(obj.currentTarget).data('bs.' + this.type, self)
- }
+'use strict';
+
+
+/**
+ * Our tooltip class.
+ * @param {Element!} element
+ * @param {Object=} opt_config
+ * @constructor
+ */
+var Tooltip = function (element, opt_config) {
+
+ /** @private {boolean} */
+ this._isEnabled = true
+
+ /** @private {number} */
+ this._timeout = 0
+
+ /** @private {string} */
+ this._hoverState = ''
+
+ /** @protected {Element} */
+ this.element = element
- clearTimeout(self.timeout)
+ /** @protected {Object} */
+ this.config = this._getConfig(opt_config)
- self.hoverState = 'out'
+ /** @protected {Element} */
+ this.tip = null
- if (!self.options.delay || !self.options.delay.hide) return self.hide()
+ /** @protected {Element} */
+ this.arrow = null
+
+ if (this.config['viewport']) {
+
+ /** @private {Element} */
+ this._viewport = $(this.config['viewport']['selector'] || this.config['viewport'])[0]
- 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)
+ this._setListeners()
+}
- if (this.hasContent() && this.enabled) {
- this.$element.trigger(e)
- var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
- if (e.isDefaultPrevented() || !inDom) return
- var that = this
+/**
+ * @const
+ * @type {string}
+ */
+Tooltip['VERSION'] = '4.0.0'
- var $tip = this.tip()
- var tipId = this.getUID(this.type)
+/**
+ * @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
+ }
+}
- this.setContent()
- $tip.attr('id', tipId)
- this.$element.attr('aria-describedby', tipId)
- if (this.options.animation) $tip.addClass('fade')
+/**
+ * @const
+ * @enum {string}
+ * @protected
+ */
+Tooltip.Direction = {
+ TOP: 'top',
+ LEFT: 'left',
+ RIGHT: 'right',
+ BOTTOM: 'bottom'
+}
- 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'
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Tooltip._NAME = 'tooltip'
- $tip
- .detach()
- .css({ top: 0, left: 0, display: 'block' })
- .addClass(this.type + '-' + placement)
- .data('bs.' + this.type, this)
- this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Tooltip._DATA_KEY = 'bs.tooltip'
- var pos = this.getPosition()
- var actualWidth = $tip[0].offsetWidth
- var actualHeight = $tip[0].offsetHeight
- if (autoPlace) {
- var origPlacement = placement
- var $container = this.options.container ? $(this.options.container) : this.$element.parent()
- var containerDim = this.getPosition($container)
+/**
+ * @const
+ * @type {number}
+ * @private
+ */
+Tooltip._TRANSITION_DURATION = 150
- placement = placement == 'bottom' && pos.bottom + actualHeight > containerDim.bottom ? 'top' :
- placement == 'top' && pos.top - actualHeight < containerDim.top ? 'bottom' :
- placement == 'right' && pos.right + actualWidth > containerDim.width ? 'left' :
- placement == 'left' && pos.left - actualWidth < containerDim.left ? 'right' :
- placement
- $tip
- .removeClass(this.type + '-' + origPlacement)
- .addClass(this.type + '-' + placement)
- }
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Tooltip._HoverState = {
+ IN: 'in',
+ OUT: 'out'
+}
- var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
- this.applyPlacement(calculatedOffset, placement)
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Tooltip._Event = {
+ HIDE : 'hide.bs.tooltip',
+ HIDDEN : 'hidden.bs.tooltip',
+ SHOW : 'show.bs.tooltip',
+ SHOWN : 'shown.bs.tooltip'
+}
- var complete = function () {
- var prevHoverState = that.hoverState
- that.$element.trigger('shown.bs.' + that.type)
- that.hoverState = null
- if (prevHoverState == 'out') that.leave(that)
- }
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Tooltip._ClassName = {
+ FADE : 'fade',
+ IN : 'in'
+}
- $.support.transition && this.$tip.hasClass('fade') ?
- $tip
- .one('bsTransitionEnd', complete)
- .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
- complete()
- }
- }
- Tooltip.prototype.applyPlacement = function (offset, placement) {
- var $tip = this.tip()
- var width = $tip[0].offsetWidth
- var height = $tip[0].offsetHeight
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Tooltip._Selector = {
+ TOOLTIP : '.tooltip',
+ TOOLTIP_INNER : '.tooltip-inner',
+ TOOLTIP_ARROW : '.tooltip-arrow'
+}
- // manually read margins because getBoundingClientRect includes difference
- var marginTop = parseInt($tip.css('margin-top'), 10)
- var marginLeft = parseInt($tip.css('margin-left'), 10)
- // we must check for NaN for IE9
- if (isNaN(marginTop)) marginTop = 0
- if (isNaN(marginLeft)) marginLeft = 0
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+Tooltip._JQUERY_NO_CONFLICT = $.fn[Tooltip._NAME]
- offset.top = offset.top + marginTop
- offset.left = offset.left + marginLeft
- // $.fn.offset doesn't round pixel values
- // so we use setOffset directly with our own function B-0
- $.offset.setOffset($tip[0], $.extend({
- using: function (props) {
- $tip.css({
- top: Math.round(props.top),
- left: Math.round(props.left)
- })
- }
- }, offset), 0)
+/**
+ * @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
- $tip.addClass('in')
+ if (!data && opt_config == 'destroy') {
+ return
+ }
- // 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 (!data) {
+ data = new Tooltip(this, config)
+ $(this).data(Tooltip._DATA_KEY, data)
+ }
- if (placement == 'top' && actualHeight != height) {
- offset.top = offset.top + height - actualHeight
+ if (typeof opt_config === 'string') {
+ data[opt_config]()
}
+ })
+}
- var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
- if (delta.left) offset.left += delta.left
- else offset.top += delta.top
+/**
+ * Enable tooltip
+ */
+Tooltip.prototype['enable'] = function () {
+ this._isEnabled = true
+}
- var isVertical = /top|bottom/.test(placement)
- var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
- var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
- $tip.offset(offset)
- this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
- }
+/**
+ * Disable tooltip
+ */
+Tooltip.prototype['disable'] = function () {
+ this._isEnabled = false
+}
- Tooltip.prototype.replaceArrow = function (delta, dimension, isHorizontal) {
- this.arrow()
- .css(isHorizontal ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
- .css(isHorizontal ? 'top' : 'left', '')
- }
- Tooltip.prototype.setContent = function () {
- var $tip = this.tip()
- var title = this.getTitle()
+/**
+ * Toggle the tooltip enable state
+ */
+Tooltip.prototype['toggleEnabled'] = function () {
+ this._isEnabled = !this._isEnabled
+}
- $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
- $tip.removeClass('fade in tooltip-top tooltip-bottom tooltip-left tooltip-right')
- }
+/**
+ * Toggle the tooltips display
+ * @param {Event} opt_event
+ */
+Tooltip.prototype['toggle'] = function (opt_event) {
+ var context = this
+ var dataKey = this.getDataKey()
- Tooltip.prototype.hide = function (callback) {
- var that = this
- var $tip = this.tip()
- var e = $.Event('hide.bs.' + this.type)
+ if (opt_event) {
+ context = $(opt_event.currentTarget).data(dataKey)
- function complete() {
- if (that.hoverState != 'in') $tip.detach()
- that.$element
- .removeAttr('aria-describedby')
- .trigger('hidden.bs.' + that.type)
- callback && callback()
+ if (!context) {
+ context = new this.constructor(opt_event.currentTarget, this._getDelegateConfig())
+ $(opt_event.currentTarget).data(dataKey, context)
}
+ }
- this.$element.trigger(e)
+ $(context.getTipElement()).hasClass(Tooltip._ClassName.IN) ?
+ context._leave(null, context) :
+ context._enter(null, context)
+}
- if (e.isDefaultPrevented()) return
- $tip.removeClass('in')
+/**
+ * Remove tooltip functionality
+ */
+Tooltip.prototype['destroy'] = function () {
+ clearTimeout(this._timeout)
+ this['hide'](function () {
+ $(this.element)
+ .off(Tooltip._Selector.TOOLTIP)
+ .removeData(this.getDataKey())
+ }.bind(this))
+}
- $.support.transition && this.$tip.hasClass('fade') ?
- $tip
- .one('bsTransitionEnd', complete)
- .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
- complete()
- this.hoverState = null
+/**
+ * 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)
- return this
- }
+ if (this.isWithContent() && this._isEnabled) {
+ $(this.element).trigger(showEvent)
- 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 isInTheDom = $.contains(this.element.ownerDocument.documentElement, this.element)
+
+ if (showEvent.isDefaultPrevented() || !isInTheDom) {
+ return
}
- }
- Tooltip.prototype.hasContent = function () {
- return this.getTitle()
- }
+ var tip = this.getTipElement()
+ var tipId = Bootstrap.getUID(this.getName())
- Tooltip.prototype.getPosition = function ($element) {
- $element = $element || this.$element
+ tip.setAttribute('id', tipId)
+ this.element.setAttribute('aria-describedby', tipId)
- var el = $element[0]
- var isBody = el.tagName == 'BODY'
+ this.setContent()
- var elRect = el.getBoundingClientRect()
- if (elRect.width == null) {
- // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
- elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
+ if (this.config['animation']) {
+ $(tip).addClass(Tooltip._ClassName.FADE)
}
- var elOffset = isBody ? { top: 0, left: 0 } : $element.offset()
- var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
- var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
- return $.extend({}, elRect, scroll, outerDims, elOffset)
- }
+ var placement = typeof this.config['placement'] == 'function' ?
+ this.config['placement'].call(this, tip, this.element) :
+ this.config['placement']
- 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 }
+ var autoToken = /\s?auto?\s?/i
+ var isWithAutoPlacement = autoToken.test(placement)
- }
+ if (isWithAutoPlacement) {
+ placement = placement.replace(autoToken, '') || Tooltip.Direction.TOP
+ }
- Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
- var delta = { top: 0, left: 0 }
- if (!this.$viewport) return delta
+ if (tip.parentNode && tip.parentNode.nodeType == Node.ELEMENT_NODE) {
+ tip.parentNode.removeChild(tip)
+ }
- var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
- var viewportDimensions = this.getPosition(this.$viewport)
+ tip.style.top = 0
+ tip.style.left = 0
+ tip.style.display = 'block'
- 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
- }
+ $(tip).addClass(Tooltip._NAME + '-' + placement)
+
+ $(tip).data(this.getDataKey(), this)
+
+ if (this.config['container']) {
+ $(this.config['container'])[0].appendChild(tip)
} else {
- var leftEdgeOffset = pos.left - viewportPadding
- var rightEdgeOffset = pos.left + viewportPadding + actualWidth
- if (leftEdgeOffset < viewportDimensions.left) { // left overflow
- delta.left = viewportDimensions.left - leftEdgeOffset
- } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
- delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
- }
+ this.element.parentNode.insertBefore(tip, this.element.nextSibling)
}
- return delta
- }
+ var position = this._getPosition()
+ var actualWidth = tip.offsetWidth
+ var actualHeight = tip.offsetHeight
- Tooltip.prototype.getTitle = function () {
- var title
- var $e = this.$element
- var o = this.options
+ var calculatedPlacement = this._getCalculatedAutoPlacement(isWithAutoPlacement, placement, position, actualWidth, actualHeight)
+ var calculatedOffset = this._getCalculatedOffset(calculatedPlacement, position, actualWidth, actualHeight)
- title = $e.attr('data-original-title')
- || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
+ this._applyCalculatedPlacement(calculatedOffset, calculatedPlacement)
- return title
- }
+ var complete = function () {
+ var prevHoverState = this.hoverState
+ $(this.element).trigger(this.getEventObject().SHOWN)
+ this.hoverState = null
- Tooltip.prototype.getUID = function (prefix) {
- do prefix += ~~(Math.random() * 1000000)
- while (document.getElementById(prefix))
- return prefix
- }
+ if (prevHoverState == 'out') this._leave(null, this)
+ }.bind(this)
- Tooltip.prototype.tip = function () {
- return (this.$tip = this.$tip || $(this.options.template))
+ Bootstrap.transition && $(this._tip).hasClass(Tooltip._ClassName.FADE) ?
+ $(this._tip)
+ .one(Bootstrap.TRANSITION_END, complete)
+ .emulateTransitionEnd(Tooltip._TRANSITION_DURATION) :
+ complete()
}
+}
- Tooltip.prototype.arrow = function () {
- return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
- }
- Tooltip.prototype.enable = function () {
- this.enabled = true
- }
+/**
+ * Hide the tooltip breh
+ */
+Tooltip.prototype['hide'] = function (callback) {
+ var tip = this.getTipElement()
+ var hideEvent = $.Event(this.getEventObject().HIDE)
- Tooltip.prototype.disable = function () {
- this.enabled = false
- }
+ var complete = function () {
+ if (this._hoverState != Tooltip._HoverState.IN) {
+ tip.parentNode.removeChild(tip)
+ }
- Tooltip.prototype.toggleEnabled = function () {
- this.enabled = !this.enabled
- }
+ this.element.removeAttribute('aria-describedby')
+ $(this.element).trigger(this.getEventObject().HIDDEN)
- 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 (callback) {
+ callback()
}
+ }.bind(this)
- self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
- }
+ $(this.element).trigger(hideEvent)
- Tooltip.prototype.destroy = function () {
- var that = this
- clearTimeout(this.timeout)
- this.hide(function () {
- that.$element.off('.' + that.type).removeData('bs.' + that.type)
- })
+ 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()
}
+ this._hoverState = ''
+}
- // 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}
+ */
+Tooltip.prototype['getHoverState'] = function (callback) {
+ return this._hoverState
+}
- if (!data && option == 'destroy') return
- if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
- var old = $.fn.tooltip
+/**
+ * @return {string}
+ * @protected
+ */
+Tooltip.prototype.getName = function () {
+ return Tooltip._NAME
+}
- $.fn.tooltip = Plugin
- $.fn.tooltip.Constructor = Tooltip
+/**
+ * @return {string}
+ * @protected
+ */
+Tooltip.prototype.getDataKey = function () {
+ return Tooltip._DATA_KEY
+}
- // TOOLTIP NO CONFLICT
- // ===================
- $.fn.tooltip.noConflict = function () {
- $.fn.tooltip = old
- return this
- }
+/**
+ * @return {Object}
+ * @protected
+ */
+Tooltip.prototype.getEventObject = function () {
+ return Tooltip._Event
+}
-}(jQuery);
-/* ========================================================================
- * Bootstrap: popover.js v3.3.2
- * http://getbootstrap.com/javascript/#popovers
- * ========================================================================
- * Copyright 2011-2015 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+/**
+ * @return {string}
+ * @protected
+ */
+Tooltip.prototype.getTitle = function () {
+ var title = this.element.getAttribute('data-original-title')
+ if (!title) {
+ title = typeof this.config['title'] === 'function' ?
+ this.config['title'].call(this.element) :
+ this.config['title']
+ }
-+function ($) {
- 'use strict';
+ return /** @type {string} */ (title)
+}
- // POPOVER PUBLIC CLASS DEFINITION
- // ===============================
- var Popover = function (element, options) {
- this.init('popover', element, options)
- }
+/**
+ * @return {Element}
+ * @protected
+ */
+Tooltip.prototype.getTipElement = function () {
+ return (this._tip = this._tip || $(this.config['template'])[0])
+}
- if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
- Popover.VERSION = '3.3.2'
+/**
+ * @return {Element}
+ * @protected
+ */
+Tooltip.prototype.getArrowElement = function () {
+ return (this.arrow = this.arrow || $(this.getTipElement()).find(Tooltip._Selector.TOOLTIP_ARROW)[0])
+}
- 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>'
- })
+
+/**
+ * @return {boolean}
+ * @protected
+ */
+Tooltip.prototype.isWithContent = function () {
+ return !!this.getTitle()
+}
- // NOTE: POPOVER EXTENDS tooltip.js
- // ================================
+/**
+ * @protected
+ */
+Tooltip.prototype.setContent = function () {
+ var tip = this.getTipElement()
+ var title = this.getTitle()
- Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
+ $(tip).find(Tooltip._Selector.TOOLTIP_INNER)[0][this.config['html'] ? 'innerHTML' : 'innerText'] = title
- Popover.prototype.constructor = Popover
+ $(tip)
+ .removeClass(Tooltip._ClassName.FADE)
+ .removeClass(Tooltip._ClassName.IN)
- Popover.prototype.getDefaults = function () {
- return Popover.DEFAULTS
+ for (var direction in Tooltip.Direction) {
+ $(tip).removeClass(Tooltip._NAME + '-' + direction)
}
+}
- 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)
+/**
+ * @private
+ */
+Tooltip.prototype._setListeners = function () {
+ var triggers = this.config['trigger'].split(' ')
- $tip.removeClass('fade popover-top popover-bottom popover-left popover-right in')
+ triggers.forEach(function (trigger) {
+ if (trigger == 'click') {
+ $(this.element).on('click.bs.tooltip', this.config['selector'], this['toggle'].bind(this))
- // 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()
- }
+ } else if (trigger != 'manual') {
+ var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
+ var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
- Popover.prototype.hasContent = function () {
- return this.getTitle() || this.getContent()
+ $(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))
+
+ if (this.config['selector']) {
+ this.config = $.extend({}, this.config, { 'trigger': 'manual', 'selector': '' })
+ } else {
+ this._fixTitle()
}
+}
- 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)
- }
+/**
+ * @param {Object=} opt_config
+ * @return {Object}
+ * @private
+ */
+Tooltip.prototype._getConfig = function (opt_config) {
+ var config = $.extend({}, this.constructor['Defaults'], $(this.element).data(), opt_config)
- Popover.prototype.arrow = function () {
- return (this.$arrow = this.$arrow || this.tip().find('.popover-arrow'))
+ if (config['delay'] && typeof config['delay'] == 'number') {
+ config['delay'] = {
+ 'show': config['delay'],
+ 'hide': config['delay']
+ }
}
- Popover.prototype.tip = function () {
- if (!this.$tip) this.$tip = $(this.options.template)
- return this.$tip
+ return config
+}
+
+
+/**
+ * @return {Object}
+ * @private
+ */
+Tooltip.prototype._getDelegateConfig = function () {
+ var config = {}
+ var defaults = this.constructor['Defaults']
+
+ if (this.config) {
+ for (var key in this.config) {
+ var value = this.config[key]
+ if (defaults[key] != value) config[key] = value
+ }
}
+ return config
+}
- // 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 && option == 'destroy') return
- if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
- if (typeof option == 'string') data[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)
}
- var old = $.fn.popover
+ return placement
+}
- $.fn.popover = Plugin
- $.fn.popover.Constructor = 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 }
+}
- // POPOVER NO CONFLICT
- // ===================
- $.fn.popover.noConflict = function () {
- $.fn.popover = old
- return this
+/**
+ * @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
}
-}(jQuery);
+ var viewportPadding = this.config['viewport'] && this.config['viewport']['padding'] || 0
+ var viewportDimensions = this._getPosition(this._viewport)
-/* ========================================================================
- * Bootstrap: scrollspy.js v3.3.2
- * http://getbootstrap.com/javascript/#scrollspy
- * ========================================================================
- * Copyright 2011-2015 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ if (placement === Tooltip.Direction.RIGHT || placement === Tooltip.Direction.LEFT) {
+ var topEdgeOffset = position.top - viewportPadding - viewportDimensions.scroll
+ var bottomEdgeOffset = position.top + viewportPadding - viewportDimensions.scroll + actualHeight
+ if (topEdgeOffset < viewportDimensions.top) { // top overflow
+ delta.top = viewportDimensions.top - topEdgeOffset
-+function ($) {
- 'use strict';
-
- // SCROLLSPY CLASS DEFINITION
- // ==========================
+ } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
+ delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
+ }
- function ScrollSpy(element, options) {
- var process = $.proxy(this.process, this)
+ } else {
+ var leftEdgeOffset = position.left - viewportPadding
+ var rightEdgeOffset = position.left + viewportPadding + actualWidth
- this.$body = $('body')
- this.$scrollElement = $(element).is('body') ? $(window) : $(element)
- this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
- this.selector = (this.options.target || '') + ' .nav li > a'
- this.offsets = []
- this.targets = []
- this.activeTarget = null
- this.scrollHeight = 0
+ if (leftEdgeOffset < viewportDimensions.left) { // left overflow
+ delta.left = viewportDimensions.left - leftEdgeOffset
- this.$scrollElement.on('scroll.bs.scrollspy', process)
- this.refresh()
- this.process()
+ } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
+ delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
+ }
}
- ScrollSpy.VERSION = '3.3.2'
+ 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)
+}
- ScrollSpy.DEFAULTS = {
- offset: 10
- }
- ScrollSpy.prototype.getScrollHeight = function () {
- return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
+/**
+ * @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
}
- ScrollSpy.prototype.refresh = function () {
- var offsetMethod = 'offset'
- var offsetBase = 0
+ offset.top = offset.top + marginTop
+ offset.left = offset.left + marginLeft
- if (!$.isWindow(this.$scrollElement[0])) {
- offsetMethod = 'position'
- offsetBase = this.$scrollElement.scrollTop()
+ // $.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()
-
- var self = this
+ $(tip).addClass(Tooltip._ClassName.IN)
- this.$body
- .find(this.selector)
- .map(function () {
- var $el = $(this)
- var href = $el.data('target') || $el.attr('href')
- var $href = /^#./.test(href) && $(href)
+ // check to see if placing tip in new offset caused the tip to resize itself
+ var actualWidth = tip.offsetWidth
+ var actualHeight = tip.offsetHeight
- return ($href
- && $href.length
- && $href.is(':visible')
- && [[$href[offsetMethod]().top + offsetBase, href]]) || null
- })
- .sort(function (a, b) { return a[0] - b[0] })
- .each(function () {
- self.offsets.push(this[0])
- self.targets.push(this[1])
- })
+ if (placement == Tooltip.Direction.TOP && actualHeight != height) {
+ offset.top = offset.top + height - 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
+ var delta = this._getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
- if (this.scrollHeight != scrollHeight) {
- this.refresh()
- }
+ if (delta.left) {
+ offset.left += delta.left
+ } else {
+ offset.top += delta.top
+ }
- if (scrollTop >= maxScroll) {
- return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
- }
+ 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 (activeTarget && scrollTop < offsets[0]) {
- this.activeTarget = null
- return this.clear()
- }
+ $(tip).offset(offset)
- for (i = offsets.length; i--;) {
- activeTarget != targets[i]
- && scrollTop >= offsets[i]
- && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
- && this.activate(targets[i])
- }
- }
+ this._replaceArrow(arrowDelta, tip[arrowOffsetPosition], isVertical)
+}
- ScrollSpy.prototype.activate = function (target) {
- this.activeTarget = target
- this.clear()
+/**
+ * @param {number} delta
+ * @param {number} dimension
+ * @param {boolean} isHorizontal
+ * @private
+ */
+Tooltip.prototype._replaceArrow = function (delta, dimension, isHorizontal) {
+ var arrow = this.getArrowElement()
- var selector = this.selector +
- '[data-target="' + target + '"],' +
- this.selector + '[href="' + target + '"]'
+ arrow.style[isHorizontal ? 'left' : 'top'] = 50 * (1 - delta / dimension) + '%'
+ arrow.style[isHorizontal ? 'top' : 'left'] = ''
+}
- var active = $(selector)
- .parents('li')
- .addClass('active')
- if (active.parent('.dropdown-menu').length) {
- active = active
- .closest('li.dropdown')
- .addClass('active')
- }
- active.trigger('activate.bs.scrollspy')
+/**
+ * @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', '')
}
+}
+
- ScrollSpy.prototype.clear = function () {
- $(this.selector)
- .parentsUntil(this.options.target, '.active')
- .removeClass('active')
+/**
+ * @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
}
+ if (!context) {
+ context = new this.constructor(opt_event.currentTarget, this._getDelegateConfig())
+ $(opt_event.currentTarget).data(dataKey, context)
+ }
- // SCROLLSPY PLUGIN DEFINITION
- // ===========================
+ clearTimeout(context._timeout)
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.scrollspy')
- var options = typeof option == 'object' && option
+ context._hoverState = Tooltip._HoverState.IN
- if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
- if (typeof option == 'string') data[option]()
- })
+ if (!context.config['delay'] || !context.config['delay']['show']) {
+ context['show']()
+ return
}
- var old = $.fn.scrollspy
+ context._timeout = setTimeout(function () {
+ if (context._hoverState == Tooltip._HoverState.IN) {
+ context['show']()
+ }
+ }, context.config['delay']['show'])
+}
- $.fn.scrollspy = Plugin
- $.fn.scrollspy.Constructor = 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)
- // SCROLLSPY NO CONFLICT
- // =====================
+ if (!context) {
+ context = new this.constructor(opt_event.currentTarget, this._getDelegateConfig())
+ $(opt_event.currentTarget).data(dataKey, context)
+ }
- $.fn.scrollspy.noConflict = function () {
- $.fn.scrollspy = old
- return this
+ clearTimeout(context._timeout)
+
+ context._hoverState = Tooltip._HoverState.OUT
+
+ if (!context.config['delay'] || !context.config['delay']['hide']) {
+ context['hide']()
+ return
}
+ context._timeout = setTimeout(function () {
+ if (context._hoverState == Tooltip._HoverState.OUT) {
+ context['hide']()
+ }
+ }, context.config['delay']['hide'])
+}
- // SCROLLSPY DATA-API
- // ==================
- $(window).on('load.bs.scrollspy.data-api', function () {
- $('[data-spy="scroll"]').each(function () {
- var $spy = $(this)
- Plugin.call($spy, $spy.data())
- })
- })
-}(jQuery);
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
-/* ========================================================================
- * Bootstrap: tab.js v3.3.2
- * http://getbootstrap.com/javascript/#tabs
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Tooltip._NAME] = Tooltip._jQueryInterface
+
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Tooltip._NAME]['Constructor'] = Tooltip
+
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Tooltip._NAME]['noConflict'] = function () {
+ $.fn[Tooltip._NAME] = Tooltip._JQUERY_NO_CONFLICT
+ return this
+}
+
+/** =======================================================================
+ * Bootstrap: popover.js v4.0.0
+ * http://getbootstrap.com/javascript/#popovers
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @fileoverview - Bootstrap's popover plugin - extends tooltip.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.popover
+ * + $.popover.noConflict
+ * + $.popover.Constructor
+ * + $.popover.Constructor.VERSION
+ * + $.popover.Constructor.Defaults
+ * + $.popover.Constructor.Defaults.container
+ * + $.popover.Constructor.Defaults.animation
+ * + $.popover.Constructor.Defaults.placement
+ * + $.popover.Constructor.Defaults.selector
+ * + $.popover.Constructor.Defaults.template
+ * + $.popover.Constructor.Defaults.trigger
+ * + $.popover.Constructor.Defaults.title
+ * + $.popover.Constructor.Defaults.content
+ * + $.popover.Constructor.Defaults.delay
+ * + $.popover.Constructor.Defaults.html
+ * + $.popover.Constructor.Defaults.viewport
+ * + $.popover.Constructor.Defaults.viewport.selector
+ * + $.popover.Constructor.Defaults.viewport.padding
+ * + $.popover.Constructor.prototype.enable
+ * + $.popover.Constructor.prototype.disable
+ * + $.popover.Constructor.prototype.destroy
+ * + $.popover.Constructor.prototype.toggleEnabled
+ * + $.popover.Constructor.prototype.toggle
+ * + $.popover.Constructor.prototype.show
+ * + $.popover.Constructor.prototype.hide
+ *
+ * ========================================================================
+ */
-+function ($) {
- 'use strict';
+'use strict';
- // TAB CLASS DEFINITION
- // ====================
- var Tab = function (element) {
- this.element = $(element)
- }
+if (!Tooltip) throw new Error('Popover requires tooltip.js')
- Tab.VERSION = '3.3.2'
- Tab.TRANSITION_DURATION = 150
+/**
+ * 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)
- Tab.prototype.show = function () {
- var $this = this.element
- var $ul = $this.closest('ul:not(.dropdown-menu)')
- var selector = $this.data('target')
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
- }
+/**
+ * @const
+ * @type {string}
+ */
+Popover['VERSION'] = '4.0.0'
- if ($this.parent('li').hasClass('active')) return
- 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]
- })
+/**
+ * @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'
- $previous.trigger(hideEvent)
- $this.trigger(showEvent)
- if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Popover._DATA_KEY = 'bs.popover'
- 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]
- })
- })
- }
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Popover._Event = {
+ HIDE : 'hide.bs.popover',
+ HIDDEN : 'hidden.bs.popover',
+ SHOW : 'show.bs.popover',
+ SHOWN : 'shown.bs.popover'
+}
- Tab.prototype.activate = function (element, container, callback) {
- var $active = container.find('> .active')
- var transition = callback
- && $.support.transition
- && (($active.length && $active.hasClass('fade')) || !!container.find('> .fade').length)
-
- function next() {
- $active
- .removeClass('active')
- .find('> .dropdown-menu > .active')
- .removeClass('active')
- .end()
- .find('[data-toggle="tab"]')
- .attr('aria-expanded', false)
-
- element
- .addClass('active')
- .find('[data-toggle="tab"]')
- .attr('aria-expanded', true)
-
- if (transition) {
- element[0].offsetWidth // reflow for transition
- element.addClass('in')
- } else {
- element.removeClass('fade')
- }
- if (element.parent('.dropdown-menu')) {
- element
- .closest('li.dropdown')
- .addClass('active')
- .end()
- .find('[data-toggle="tab"]')
- .attr('aria-expanded', true)
- }
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Popover._ClassName = {
+ FADE : 'fade',
+ IN : 'in'
+}
- callback && callback()
+
+/**
+ * @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
}
- $active.length && transition ?
- $active
- .one('bsTransitionEnd', next)
- .emulateTransitionEnd(Tab.TRANSITION_DURATION) :
- next()
+ if (!data) {
+ data = new Popover(this, config)
+ $(this).data(Popover._DATA_KEY, data)
+ }
- $active.removeClass('in')
- }
+ if (typeof opt_config === 'string') {
+ data[opt_config]()
+ }
+ })
+}
- // TAB PLUGIN DEFINITION
- // =====================
+/**
+ * @return {string}
+ * @protected
+ */
+Popover.prototype.getName = function () {
+ return Popover._NAME
+}
+
- 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
- // ===============
- $.fn.tab.noConflict = function () {
- $.fn.tab = old
- return this
+/**
+ * @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
}
+ // 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)
- // TAB DATA-API
- // ============
+ $(tip)
+ .removeClass(Popover._ClassName.FADE)
+ .removeClass(Popover._ClassName.IN)
- var clickHandler = function (e) {
- e.preventDefault()
- Plugin.call($(this), 'show')
+ for (var direction in Tooltip.Direction) {
+ $(tip).removeClass(Popover._NAME + '-' + Tooltip.Direction[direction])
}
+}
- $(document)
- .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
- .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
-}(jQuery);
+/**
+ * @override
+ */
+Popover.prototype.isWithContent = function () {
+ return this.getTitle() || this._getContent()
+}
+
+
+/**
+ * @override
+ */
+Popover.prototype.getTipElement = function () {
+ return (this.tip = this.tip || $(this.config['template'])[0])
+}
-/* ========================================================================
- * Bootstrap: affix.js v3.3.2
- * http://getbootstrap.com/javascript/#affix
+
+/**
+ * @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
+
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Popover._NAME]['noConflict'] = function () {
+ $.fn[Popover._NAME] = Popover._JQUERY_NO_CONFLICT
+ return this
+}
+
+/** =======================================================================
+ * Bootstrap: tab.js v4.0.0
+ * http://getbootstrap.com/javascript/#tabs
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @fileoverview - Bootstrap's tab plugin. Tab O_O
+ *
+ * Public Methods & Properties:
+ *
+ * + $.tab
+ * + $.tab.noConflict
+ * + $.tab.Constructor
+ * + $.tab.Constructor.VERSION
+ * + $.tab.Constructor.prototype.show
+ *
+ * ========================================================================
+ */
-+function ($) {
- 'use strict';
+'use strict';
- // AFFIX CLASS DEFINITION
- // ======================
+/**
+ * Our Tab class.
+ * @param {Element!} element
+ * @constructor
+ */
+var Tab = function (element) {
- var Affix = function (element, options) {
- this.options = $.extend({}, Affix.DEFAULTS, options)
+ /** @type {Element} */
+ this._element = element
- 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 =
- this.unpin =
- this.pinnedOffset = null
- this.checkPosition()
- }
+/**
+ * @const
+ * @type {string}
+ */
+Tab['VERSION'] = '4.0.0'
- Affix.VERSION = '3.3.2'
- Affix.RESET = 'affix affix-top affix-bottom'
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Tab._NAME = 'tab'
- 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()
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Tab._DATA_KEY = 'bs.tab'
+
- if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false
+/**
+ * @const
+ * @type {number}
+ * @private
+ */
+Tab._TRANSITION_DURATION = 150
- if (this.affixed == 'bottom') {
- if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
- return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
+
+/**
+ * @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)
}
- var initializing = this.affixed == null
- var colliderTop = initializing ? scrollTop : position.top
- var colliderHeight = initializing ? targetHeight : height
+ if (typeof opt_config === 'string') {
+ data[opt_config]()
+ }
+ })
+}
- if (offsetTop != null && scrollTop <= offsetTop) return 'top'
- if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'
- return false
+/**
+ * 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)
+
+ if (ulElement) {
+ var previous = /** @type {Array.<Element>} */ ($.makeArray($(ulElement).find(Tab._Selector.ACTIVE)))
+ previous = previous[previous.length - 1]
+
+ if (previous) {
+ previous = $(previous).find('a')[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)
+ var hideEvent = $.Event(Tab._Event.HIDE, {
+ relatedTarget: this._element
+ })
+
+ var showEvent = $.Event(Tab._Event.SHOW, {
+ relatedTarget: previous
+ })
+
+ if (previous) {
+ $(previous).trigger(hideEvent)
}
- Affix.prototype.checkPositionWithEventLoop = function () {
- setTimeout($.proxy(this.checkPosition, this), 1)
+ $(this._element).trigger(showEvent)
+
+ if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
+
+ if (selector) {
+ var target = $(selector)[0]
}
- Affix.prototype.checkPosition = function () {
- if (!this.$element.is(':visible')) return
+ this._activate($(this._element).closest(Tab._Selector.LI)[0], ulElement)
- var height = this.$element.height()
- var offset = this.options.offset
- var offsetTop = offset.top
- var offsetBottom = offset.bottom
- var scrollHeight = $('body').height()
+ var complete = function () {
+ var hiddenEvent = $.Event(Tab._Event.HIDDEN, {
+ relatedTarget: this._element
+ })
- 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 shownEvent = $.Event(Tab._Event.SHOWN, {
+ relatedTarget: previous
+ })
- var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)
+ $(previous).trigger(hiddenEvent)
+ $(this._element).trigger(shownEvent)
+ }.bind(this)
- if (this.affixed != affix) {
- if (this.unpin != null) this.$element.css('top', '')
+ if (target) {
+ this._activate(target, /** @type {Element} */ (target.parentNode), complete)
+ } else {
+ complete()
+ }
+}
- var affixType = 'affix' + (affix ? '-' + affix : '')
- var e = $.Event(affixType + '.bs.affix')
- this.$element.trigger(e)
+/**
+ * @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 (active) {
+ $(active).removeClass(Tab._ClassName.IN)
+ }
+}
- if (e.isDefaultPrevented()) return
- this.affixed = affix
- this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
+/**
+ * @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.$element
- .removeClass(Affix.RESET)
- .addClass(affixType)
- .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
+ var dropdownChild = $(active).find(Tab._Selector.DROPDOWN_ACTIVE_CHILD)[0]
+ if (dropdownChild) {
+ $(dropdownChild).removeClass(Tab._ClassName.ACTIVE)
}
- if (affix == 'bottom') {
- this.$element.offset({
- top: scrollHeight - height - offsetBottom
- })
+ var activeToggle = $(active).find(Tab._Selector.DATA_TOGGLE)[0]
+ if (activeToggle) {
+ activeToggle.setAttribute('aria-expanded', false)
}
}
+ $(element).addClass(Tab._ClassName.ACTIVE)
- // AFFIX PLUGIN DEFINITION
- // =======================
+ var 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 (isTransitioning) {
+ Bootstrap.reflow(element)
+ $(element).addClass(Tab._ClassName.IN)
+ } else {
+ $(element).removeClass(Tab._ClassName.FADE)
+ }
- if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
- if (typeof option == 'string') data[option]()
- })
+ 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)
+ }
+
+ elementToggle = $(element).find(Tab._Selector.DATA_TOGGLE)[0]
+ if (elementToggle) {
+ elementToggle.setAttribute('aria-expanded', true)
+ }
}
- var old = $.fn.affix
+ if (opt_callback) {
+ opt_callback()
+ }
+}
- $.fn.affix = Plugin
- $.fn.affix.Constructor = Affix
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Tab._NAME] = Tab._jQueryInterface
- // AFFIX NO CONFLICT
- // =================
- $.fn.affix.noConflict = function () {
- $.fn.affix = old
- return this
- }
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Tab._NAME]['Constructor'] = Tab
- // AFFIX DATA-API
- // ==============
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Tab._NAME]['noConflict'] = function () {
+ $.fn[Tab._NAME] = Tab._JQUERY_NO_CONFLICT
+ return this
+}
- $(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
+// TAB DATA-API
+// ============
- Plugin.call($spy, data)
- })
- })
+var clickHandler = function (e) {
+ e.preventDefault()
+ Tab._jQueryInterface.call($(this), 'show')
+}
-}(jQuery);
+$(document)
+ .on('click.bs.tab.data-api', Tab._Selector.DATA_TOGGLE, clickHandler)
diff --git a/dist/js/bootstrap.min.js b/dist/js/bootstrap.min.js
index a17f4b3bfd..089780400d 100644
--- a/dist/js/bootstrap.min.js
+++ b/dist/js/bootstrap.min.js
@@ -3,5 +3,75 @@
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
-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.2",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.2",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")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active"));a&&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"),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=this.sliding=this.interval=this.$active=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.2",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(".carousel-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(".carousel-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(".carousel-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"==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(this.options.trigger).filter('[href="#'+b.id+'"], [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.2",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0,trigger:'[data-toggle="collapse"]'},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":a.extend({},e.data(),{trigger:this});c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=c(d),f={relatedTarget:this};e.hasClass("open")&&(e.trigger(b=a.Event("hide.bs.dropdown",f)),b.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f)))}))}function c(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 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.2",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('<div class="dropdown-backdrop"/>').insertAfter(a(this)).on("click",b);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(b){if(/(38|40|27|32)/.test(b.which)&&!/input|textarea/i.test(b.target.tagName)){var d=a(this);if(b.preventDefault(),b.stopPropagation(),!d.is(".disabled, :disabled")){var e=c(d),g=e.hasClass("open");if(!g&&27!=b.which||g&&27==b.which)return 27==b.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.divider):visible a",i=e.find('[role="menu"]'+h+', [role="listbox"]'+h);if(i.length){var j=i.index(b.target);38==b.which&&j>0&&j--,40==b.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",b).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",'[role="menu"]',g.prototype.keydown).on("keydown.bs.dropdown.data-api",'[role="listbox"]',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.$backdrop=this.isShown=null,this.scrollbarWidth=0,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.2",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.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.options.backdrop&&d.adjustBackdrop(),d.adjustDialog(),e&&d.$element[0].offsetWidth,d.$element.addClass("in").attr("aria-hidden",!1),d.enforceFocus();var f=a.Event("shown.bs.modal",{relatedTarget:b});e?d.$element.find(".modal-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").attr("aria-hidden",!0).off("click.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('<div class="modal-backdrop '+e+'" />').prependTo(this.$element).on("click.dismiss.bs.modal",a.proxy(function(a){a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus.call(this.$element[0]):this.hide.call(this))},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.options.backdrop&&this.adjustBackdrop(),this.adjustDialog()},c.prototype.adjustBackdrop=function(){this.$backdrop.css("height",0).css("height",this.$element[0].scrollHeight)},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(){this.bodyIsOverflowing=document.body.scrollHeight>document.documentElement.clientHeight,this.scrollbarWidth=this.measureScrollbar()},c.prototype.setScrollbar=function(){var a=parseInt(this.$body.css("padding-right")||0,10);this.bodyIsOverflowing&&this.$body.css("padding-right",a+this.scrollbarWidth)},c.prototype.resetScrollbar=function(){this.$body.css("padding-right","")},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"!=b)&&(e||d.data("bs.tooltip",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.type=this.options=this.enabled=this.timeout=this.hoverState=this.$element=null,this.init("tooltip",a,b)};c.VERSION="3.3.2",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){this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(this.options.viewport.selector||this.options.viewport);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.$tip&&c.$tip.is(":visible")?void(c.hoverState="in"):(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),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.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)),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(this.type+"-"+h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.options.container?a(this.options.container):this.$element.parent(),p=this.getPosition(o);h="bottom"==h&&k.bottom+m>p.bottom?"top":"top"==h&&k.top-m<p.top?"bottom":"right"==h&&k.right+l>p.width?"left":"left"==h&&k.left-l<p.left?"right":h,f.removeClass(this.type+"-"+n).addClass(this.type+"-"+h)}var q=this.getCalculatedOffset(h,k,l,m);this.applyPlacement(q,h);var r=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",r).emulateTransitionEnd(c.TRANSITION_DURATION):r()}},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=b.top+g,b.left=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 tooltip-top tooltip-bottom tooltip-left tooltip-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=this.tip(),g=a.Event("hide.bs."+this.type);return this.$element.trigger(g),g.isDefaultPrevented()?void 0:(f.removeClass("in"),a.support.transition&&this.$tip.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.width&&(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(){return this.$tip=this.$tip||a(this.options.template)},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))),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)})};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"!=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.2",c.DEFAULTS=a.extend({},a.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>'}),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 popover-top popover-bottom popover-left popover-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(".popover-arrow")},c.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};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){var e=a.proxy(this.process,this);this.$body=a("body"),this.$scrollElement=a(a(c).is("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",e),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.2",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="offset",c=0;a.isWindow(this.$scrollElement[0])||(b="position",c=this.$scrollElement.scrollTop()),this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight();var d=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[b]().top+c,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){d.offsets.push(this[0]),d.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]&&(!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.2",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")&&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=this.unpin=this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.2",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=a("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
+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);
diff --git a/dist/js/bootstrap.min.js.gz b/dist/js/bootstrap.min.js.gz
new file mode 100644
index 0000000000..84313c3936
--- /dev/null
+++ b/dist/js/bootstrap.min.js.gz
Binary files differ
diff --git a/dist/js/npm.js b/dist/js/npm.js
index bf6aa80602..ed761ca338 100644
--- a/dist/js/npm.js
+++ b/dist/js/npm.js
@@ -1,13 +1,13 @@
// This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.
-require('../../js/transition.js')
+require('../../js/util.js')
+require('../../node_modules/mq4-hover-shim/dist/browser/mq4-hover-shim.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/scrollspy.js')
-require('../../js/tab.js')
-require('../../js/affix.js') \ No newline at end of file
+require('../../js/tab.js') \ No newline at end of file
diff --git a/js/alert.js b/js/alert.js
index e8212caf01..38bce06a4d 100644
--- a/js/alert.js
+++ b/js/alert.js
@@ -1,94 +1,260 @@
-/* ========================================================================
- * Bootstrap: alert.js v3.3.2
+/** =======================================================================
+ * Bootstrap: alert.js v4.0.0
* http://getbootstrap.com/javascript/#alerts
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
+ * ========================================================================
+ * @fileoverview - Bootstrap's generic alert component. Add dismiss
+ * functionality to all alert messages with this plugin.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.alert
+ * + $.alert.noConflict
+ * + $.alert.Constructor
+ * + $.alert.Constructor.VERSION
+ * + $.alert.Constructor.prototype.close
+ *
+ * ========================================================================
+ */
-+function ($) {
- 'use strict';
+'use strict';
- // ALERT CLASS DEFINITION
- // ======================
- var dismiss = '[data-dismiss="alert"]'
- var Alert = function (el) {
- $(el).on('click', dismiss, this.close)
+/**
+ * Our Alert class.
+ * @param {Element=} opt_element
+ * @constructor
+ */
+var Alert = function (opt_element) {
+ if (opt_element) {
+ $(opt_element).on('click', Alert._DISMISS_SELECTOR, Alert._handleDismiss(this))
}
+}
- Alert.VERSION = '3.3.2'
- Alert.TRANSITION_DURATION = 150
+/**
+ * @const
+ * @type {string}
+ */
+Alert['VERSION'] = '4.0.0'
- Alert.prototype.close = function (e) {
- var $this = $(this)
- var selector = $this.attr('data-target')
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
- }
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Alert._NAME = 'alert'
- var $parent = $(selector)
- if (e) e.preventDefault()
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Alert._DATA_KEY = 'bs.alert'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Alert._DISMISS_SELECTOR = '[data-dismiss="alert"]'
+
+
+/**
+ * @const
+ * @type {number}
+ * @private
+ */
+Alert._TRANSITION_DURATION = 150
- if (!$parent.length) {
- $parent = $this.closest('.alert')
- }
- $parent.trigger(e = $.Event('close.bs.alert'))
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+Alert._JQUERY_NO_CONFLICT = $.fn[Alert._NAME]
- if (e.isDefaultPrevented()) return
- $parent.removeClass('in')
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Alert._Event = {
+ CLOSE : 'close.bs.alert',
+ CLOSED : 'closed.bs.alert'
+}
- function removeElement() {
- // detach from parent, fire event then clean up data
- $parent.detach().trigger('closed.bs.alert').remove()
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Alert._ClassName = {
+ ALERT : 'alert',
+ FADE : 'fade',
+ IN : 'in'
+}
+
+
+/**
+ * Provides the jQuery Interface for the alert component.
+ * @param {string=} opt_config
+ * @this {jQuery}
+ * @return {jQuery}
+ * @private
+ */
+Alert._jQueryInterface = function (opt_config) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data(Alert._DATA_KEY)
+
+ if (!data) {
+ data = new Alert(this)
+ $this.data(Alert._DATA_KEY, data)
}
- $.support.transition && $parent.hasClass('fade') ?
- $parent
- .one('bsTransitionEnd', removeElement)
- .emulateTransitionEnd(Alert.TRANSITION_DURATION) :
- removeElement()
+ if (opt_config === 'close') {
+ data[opt_config](this)
+ }
+ })
+}
+
+
+/**
+ * Close the alert component
+ * @param {Alert} alertInstance
+ * @return {Function}
+ * @private
+ */
+Alert._handleDismiss = function (alertInstance) {
+ return function (event) {
+ if (event) {
+ event.preventDefault()
+ }
+
+ alertInstance['close'](this)
}
+}
+
+
+/**
+ * Close the alert component
+ * @param {Element} element
+ */
+Alert.prototype['close'] = function (element) {
+ var rootElement = this._getRootElement(element)
+ var customEvent = this._triggerCloseEvent(rootElement)
+
+ if (customEvent.isDefaultPrevented()) return
+ this._removeElement(rootElement)
+}
- // ALERT PLUGIN DEFINITION
- // =======================
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.alert')
+/**
+ * Tries to get the alert's root element
+ * @return {Element}
+ * @private
+ */
+Alert.prototype._getRootElement = function (element) {
+ var parent = false
+ var selector = Bootstrap.getSelectorFromElement(element)
- if (!data) $this.data('bs.alert', (data = new Alert(this)))
- if (typeof option == 'string') data[option].call($this)
- })
+ if (selector) {
+ parent = $(selector)[0]
}
- var old = $.fn.alert
+ if (!parent) {
+ parent = $(element).closest('.' + Alert._ClassName.ALERT)[0]
+ }
+
+ return parent
+}
- $.fn.alert = Plugin
- $.fn.alert.Constructor = Alert
+/**
+ * Trigger close event on element
+ * @return {$.Event}
+ * @private
+ */
+Alert.prototype._triggerCloseEvent = function (element) {
+ var closeEvent = $.Event(Alert._Event.CLOSE)
+ $(element).trigger(closeEvent)
+ return closeEvent
+}
- // ALERT NO CONFLICT
- // =================
- $.fn.alert.noConflict = function () {
- $.fn.alert = old
- return this
+/**
+ * Trigger closed event and remove element from dom
+ * @private
+ */
+Alert.prototype._removeElement = function (element) {
+ $(element).removeClass(Alert._ClassName.IN)
+
+ if (!Bootstrap.transition || !$(element).hasClass(Alert._ClassName.FADE)) {
+ this._destroyElement(element)
+ return
}
+ $(element)
+ .one(Bootstrap.TRANSITION_END, this._destroyElement.bind(this, element))
+ .emulateTransitionEnd(Alert._TRANSITION_DURATION)
+}
+
+
+/**
+ * clean up any lingering jquery data and kill element
+ * @private
+ */
+Alert.prototype._destroyElement = function (element) {
+ $(element)
+ .detach()
+ .trigger(Alert._Event.CLOSED)
+ .remove()
+}
+
+
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Alert._NAME] = Alert._jQueryInterface
+
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Alert._NAME]['Constructor'] = Alert
+
+
+/**
+ * @return {Function}
+ */
+$.fn[Alert._NAME]['noConflict'] = function () {
+ $.fn[Alert._NAME] = Alert._JQUERY_NO_CONFLICT
+ return Alert._jQueryInterface
+}
- // ALERT DATA-API
- // ==============
- $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
+/**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
-}(jQuery);
+$(document).on('click.bs.alert.data-api', Alert._DISMISS_SELECTOR, Alert._handleDismiss(new Alert))
diff --git a/js/button.js b/js/button.js
index 4d56901737..8ee2d6b084 100644
--- a/js/button.js
+++ b/js/button.js
@@ -1,116 +1,207 @@
-/* ========================================================================
- * Bootstrap: button.js v3.3.2
+/** =======================================================================
+ * Bootstrap: button.js v4.0.0
* http://getbootstrap.com/javascript/#buttons
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
-
-+function ($) {
- 'use strict';
-
- // BUTTON PUBLIC CLASS DEFINITION
- // ==============================
-
- var Button = function (element, options) {
- this.$element = $(element)
- this.options = $.extend({}, Button.DEFAULTS, options)
- this.isLoading = false
- }
-
- Button.VERSION = '3.3.2'
-
- Button.DEFAULTS = {
- loadingText: 'loading...'
- }
-
- Button.prototype.setState = function (state) {
- var d = 'disabled'
- var $el = this.$element
- var val = $el.is('input') ? 'val' : 'html'
- var data = $el.data()
-
- state = state + 'Text'
-
- if (data.resetText == null) $el.data('resetText', $el[val]())
-
- // push to event loop to allow forms to submit
- setTimeout($.proxy(function () {
- $el[val](data[state] == null ? this.options[state] : data[state])
+ * ========================================================================
+ * @fileoverview - Bootstrap's generic button component.
+ *
+ * Note (@fat): Deprecated "setState" – imo, better solutions for managing a
+ * buttons state should exist outside this plugin.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.button
+ * + $.button.noConflict
+ * + $.button.Constructor
+ * + $.button.Constructor.VERSION
+ * + $.button.Constructor.prototype.toggle
+ *
+ * ========================================================================
+ */
+
+'use strict';
+
+
+/**
+ * Our Button class.
+ * @param {Element!} element
+ * @constructor
+ */
+var Button = function (element) {
+
+ /** @private {Element} */
+ this._element = element
+
+}
+
+
+/**
+ * @const
+ * @type {string}
+ */
+Button['VERSION'] = '4.0.0'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Button._NAME = 'button'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Button._DATA_KEY = 'bs.button'
+
+
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+Button._JQUERY_NO_CONFLICT = $.fn[Button._NAME]
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Button._ClassName = {
+ ACTIVE : 'active',
+ BUTTON : 'btn',
+ FOCUS : 'focus'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Button._Selector = {
+ DATA_TOGGLE_CARROT : '[data-toggle^="button"]',
+ DATA_TOGGLE : '[data-toggle="buttons"]',
+ INPUT : 'input',
+ ACTIVE : '.active',
+ BUTTON : '.btn'
+}
+
+
+/**
+ * Provides the jQuery Interface for the Button component.
+ * @param {string=} opt_config
+ * @this {jQuery}
+ * @return {jQuery}
+ * @private
+ */
+Button._jQueryInterface = function (opt_config) {
+ return this.each(function () {
+ var data = $(this).data(Button._DATA_KEY)
+
+ if (!data) {
+ data = new Button(this)
+ $(this).data(Button._DATA_KEY, data)
+ }
- if (state == 'loadingText') {
- this.isLoading = true
- $el.addClass(d).attr(d, d)
- } else if (this.isLoading) {
- this.isLoading = false
- $el.removeClass(d).removeAttr(d)
+ if (opt_config === 'toggle') {
+ data[opt_config]()
+ }
+ })
+}
+
+
+/**
+ * Toggle's the button active state
+ */
+Button.prototype['toggle'] = function () {
+ var triggerChangeEvent = true
+ var rootElement = $(this._element).closest(Button._Selector.DATA_TOGGLE)[0]
+
+ if (rootElement) {
+ var input = $(this._element).find(Button._Selector.INPUT)[0]
+ if (input) {
+ if (input.type == 'radio') {
+ if (input.checked && $(this._element).hasClass(Button._ClassName.ACTIVE)) {
+ triggerChangeEvent = false
+ } else {
+ var activeElement = $(rootElement).find(Button._Selector.ACTIVE)[0]
+ if (activeElement) {
+ $(activeElement).removeClass(Button._ClassName.ACTIVE)
+ }
+ }
}
- }, this), 0)
- }
-
- Button.prototype.toggle = function () {
- var changed = true
- var $parent = this.$element.closest('[data-toggle="buttons"]')
- if ($parent.length) {
- var $input = this.$element.find('input')
- if ($input.prop('type') == 'radio') {
- if ($input.prop('checked') && this.$element.hasClass('active')) changed = false
- else $parent.find('.active').removeClass('active')
+ if (triggerChangeEvent) {
+ input.checked = !$(this._element).hasClass(Button._ClassName.ACTIVE)
+ $(this._element).trigger('change')
}
- if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change')
- } else {
- this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
}
+ } else {
+ this._element.setAttribute('aria-pressed', !$(this._element).hasClass(Button._ClassName.ACTIVE))
+ }
- if (changed) this.$element.toggleClass('active')
+ if (triggerChangeEvent) {
+ $(this._element).toggleClass(Button._ClassName.ACTIVE)
}
+}
- // BUTTON PLUGIN DEFINITION
- // ========================
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.button')
- var options = typeof option == 'object' && option
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Button._NAME] = Button._jQueryInterface
- if (!data) $this.data('bs.button', (data = new Button(this, options)))
- if (option == 'toggle') data.toggle()
- else if (option) data.setState(option)
- })
- }
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Button._NAME]['Constructor'] = Button
- var old = $.fn.button
- $.fn.button = Plugin
- $.fn.button.Constructor = Button
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Button._NAME]['noConflict'] = function () {
+ $.fn[Button._NAME] = Button._JQUERY_NO_CONFLICT
+ return this
+}
- // BUTTON NO CONFLICT
- // ==================
+/**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
- $.fn.button.noConflict = function () {
- $.fn.button = old
- return this
- }
+$(document)
+ .on('click.bs.button.data-api', Button._Selector.DATA_TOGGLE_CARROT, function (event) {
+ event.preventDefault()
+ var button = event.target
- // BUTTON DATA-API
- // ===============
-
- $(document)
- .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
- var $btn = $(e.target)
- if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
- Plugin.call($btn, 'toggle')
- e.preventDefault()
- })
- .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
- $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
- })
+ if (!$(button).hasClass(Button._ClassName.BUTTON)) {
+ button = $(button).closest(Button._Selector.BUTTON)
+ }
-}(jQuery);
+ Button._jQueryInterface.call($(button), 'toggle')
+ })
+ .on('focus.bs.button.data-api blur.bs.button.data-api', Button._Selector.DATA_TOGGLE_CARROT, function (event) {
+ var button = $(event.target).closest(Button._Selector.BUTTON)[0]
+ $(button).toggleClass(Button._ClassName.FOCUS, /^focus(in)?$/.test(event.type))
+ })
diff --git a/js/carousel.js b/js/carousel.js
index 8aa04e1170..8f33f48601 100644
--- a/js/carousel.js
+++ b/js/carousel.js
@@ -1,237 +1,576 @@
-/* ========================================================================
- * Bootstrap: carousel.js v3.3.2
+/** =======================================================================
+ * Bootstrap: carousel.js v4.0.0
* http://getbootstrap.com/javascript/#carousel
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @fileoverview - Bootstrap's carousel. A slideshow component for cycling
+ * through elements, like a carousel. Nested carousels are not supported.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.carousel
+ * + $.carousel.noConflict
+ * + $.carousel.Constructor
+ * + $.carousel.Constructor.VERSION
+ * + $.carousel.Constructor.Defaults
+ * + $.carousel.Constructor.Defaults.interval
+ * + $.carousel.Constructor.Defaults.pause
+ * + $.carousel.Constructor.Defaults.wrap
+ * + $.carousel.Constructor.Defaults.keyboard
+ * + $.carousel.Constructor.Defaults.slide
+ * + $.carousel.Constructor.prototype.next
+ * + $.carousel.Constructor.prototype.prev
+ * + $.carousel.Constructor.prototype.pause
+ * + $.carousel.Constructor.prototype.cycle
+ *
+ * ========================================================================
+ */
+
+'use strict';
+
+
+/**
+ * Our carousel class.
+ * @param {Element!} element
+ * @param {Object=} opt_config
+ * @constructor
+ */
+var Carousel = function (element, opt_config) {
+
+ /** @private {Element} */
+ this._element = $(element)[0]
+
+ /** @private {Element} */
+ this._indicatorsElement = $(this._element).find(Carousel._Selector.INDICATORS)[0]
+
+ /** @private {?Object} */
+ this._config = opt_config || null
+ /** @private {boolean} */
+ this._isPaused = false
-+function ($) {
- 'use strict';
+ /** @private {boolean} */
+ this._isSliding = false
+
+ /** @private {?number} */
+ this._interval = null
+
+ /** @private {?Element} */
+ this._activeElement = null
+
+ /** @private {?Array} */
+ this._items = null
+
+ this._addEventListeners()
+
+}
+
+
+/**
+ * @const
+ * @type {string}
+ */
+Carousel['VERSION'] = '4.0.0'
+
+
+/**
+ * @const
+ * @type {Object}
+ */
+Carousel['Defaults'] = {
+ 'interval' : 5000,
+ 'pause' : 'hover',
+ 'wrap' : true,
+ 'keyboard' : true,
+ 'slide' : false
+}
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Carousel._NAME = 'carousel'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Carousel._DATA_KEY = 'bs.carousel'
+
+
+/**
+ * @const
+ * @type {number}
+ * @private
+ */
+Carousel._TRANSITION_DURATION = 600
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Carousel._Direction = {
+ NEXT : 'next',
+ PREVIOUS : 'prev'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Carousel._Event = {
+ SLIDE : 'slide.bs.carousel',
+ SLID : 'slid.bs.carousel'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Carousel._ClassName = {
+ CAROUSEL : 'carousel',
+ ACTIVE : 'active',
+ SLIDE : 'slide',
+ RIGHT : 'right',
+ LEFT : 'left',
+ ITEM : 'carousel-item'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Carousel._Selector = {
+ ACTIVE : '.active',
+ ACTIVE_ITEM : '.active.carousel-item',
+ ITEM : '.carousel-item',
+ NEXT_PREV : '.next, .prev',
+ INDICATORS : '.carousel-indicators'
+}
+
+
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+Carousel._JQUERY_NO_CONFLICT = $.fn[Carousel._NAME]
+
+
+/**
+ * @param {Object=} opt_config
+ * @this {jQuery}
+ * @return {jQuery}
+ * @private
+ */
+Carousel._jQueryInterface = function (opt_config) {
+ return this.each(function () {
+ var data = $(this).data(Carousel._DATA_KEY)
+ var config = $.extend({}, Carousel['Defaults'], $(this).data(), typeof opt_config == 'object' && opt_config)
+ var action = typeof opt_config == 'string' ? opt_config : config.slide
+
+ if (!data) {
+ data = new Carousel(this, config)
+ $(this).data(Carousel._DATA_KEY, data)
+ }
+
+ if (typeof opt_config == 'number') {
+ data.to(opt_config)
- // CAROUSEL CLASS DEFINITION
- // =========================
+ } else if (action) {
+ data[action]()
+
+ } else if (config.interval) {
+ data['pause']()
+ data['cycle']()
+ }
+ })
+}
- var Carousel = function (element, options) {
- this.$element = $(element)
- this.$indicators = this.$element.find('.carousel-indicators')
- this.options = options
- this.paused =
- this.sliding =
- this.interval =
- this.$active =
- this.$items = null
- this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))
+/**
+ * Click handler for data api
+ * @param {Event} event
+ * @this {Element}
+ * @private
+ */
+Carousel._dataApiClickHandler = function (event) {
+ var selector = Bootstrap.getSelectorFromElement(this)
- this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element
- .on('mouseenter.bs.carousel', $.proxy(this.pause, this))
- .on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
+ if (!selector) {
+ return
}
- Carousel.VERSION = '3.3.2'
+ var target = $(selector)[0]
+
+ if (!target || !$(target).hasClass(Carousel._ClassName.CAROUSEL)) {
+ return
+ }
- Carousel.TRANSITION_DURATION = 600
+ var config = $.extend({}, $(target).data(), $(this).data())
- Carousel.DEFAULTS = {
- interval: 5000,
- pause: 'hover',
- wrap: true,
- keyboard: true
+ var slideIndex = this.getAttribute('data-slide-to')
+ if (slideIndex) {
+ config.interval = false
}
- Carousel.prototype.keydown = function (e) {
- if (/input|textarea/i.test(e.target.tagName)) return
- switch (e.which) {
- case 37: this.prev(); break
- case 39: this.next(); break
- default: return
- }
+ Carousel._jQueryInterface.call($(target), config)
- e.preventDefault()
+ if (slideIndex) {
+ $(target).data(Carousel._DATA_KEY).to(slideIndex)
}
- Carousel.prototype.cycle = function (e) {
- e || (this.paused = false)
+ event.preventDefault()
+}
- this.interval && clearInterval(this.interval)
- this.options.interval
- && !this.paused
- && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
+/**
+ * Advance the carousel to the next slide
+ */
+Carousel.prototype['next'] = function () {
+ if (!this._isSliding) {
+ this._slide(Carousel._Direction.NEXT)
+ }
+}
+
- return this
+/**
+ * Return the carousel to the previous slide
+ */
+Carousel.prototype['prev'] = function () {
+ if (!this._isSliding) {
+ this._slide(Carousel._Direction.PREVIOUS)
}
+}
- Carousel.prototype.getItemIndex = function (item) {
- this.$items = item.parent().children('.carousel-item')
- return this.$items.index(item || this.$active)
+
+/**
+ * Pause the carousel cycle
+ * @param {Event=} opt_event
+ */
+Carousel.prototype['pause'] = function (opt_event) {
+ if (!opt_event) {
+ this._isPaused = true
}
- Carousel.prototype.getItemForDirection = function (direction, active) {
- var activeIndex = this.getItemIndex(active)
- var willWrap = (direction == 'prev' && activeIndex === 0)
- || (direction == 'next' && activeIndex == (this.$items.length - 1))
- if (willWrap && !this.options.wrap) return active
- var delta = direction == 'prev' ? -1 : 1
- var itemIndex = (activeIndex + delta) % this.$items.length
- return this.$items.eq(itemIndex)
+ if ($(this._element).find(Carousel._Selector.NEXT_PREV)[0] && Bootstrap.transition) {
+ $(this._element).trigger(Bootstrap.transition.end)
+ this['cycle'](true)
}
- Carousel.prototype.to = function (pos) {
- var that = this
- var activeIndex = this.getItemIndex(this.$active = this.$element.find('.carousel-item.active'))
+ clearInterval(this._interval)
+ this._interval = null
+}
+
- if (pos > (this.$items.length - 1) || pos < 0) return
+/**
+ * Cycle to the next carousel item
+ * @param {Event|boolean=} opt_event
+ */
+Carousel.prototype['cycle'] = function (opt_event) {
+ if (!opt_event) {
+ this._isPaused = false
+ }
- if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid"
- if (activeIndex == pos) return this.pause().cycle()
+ if (this._interval) {
+ clearInterval(this._interval)
+ this._interval = null
+ }
- return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos))
+ if (this._config['interval'] && !this._isPaused) {
+ this._interval = setInterval(this['next'].bind(this), this._config['interval'])
}
+}
- Carousel.prototype.pause = function (e) {
- e || (this.paused = true)
- if (this.$element.find('.next, .prev').length && $.support.transition) {
- this.$element.trigger($.support.transition.end)
- this.cycle(true)
- }
+/**
+ * @return {Object}
+ */
+Carousel.prototype['getConfig'] = function () {
+ return this._config
+}
+
- this.interval = clearInterval(this.interval)
+/**
+ * Move active carousel item to specified index
+ * @param {number} index
+ */
+Carousel.prototype.to = function (index) {
+ this._activeElement = $(this._element).find(Carousel._Selector.ACTIVE_ITEM)[0]
- return this
+ var activeIndex = this._getItemIndex(this._activeElement)
+
+ if (index > (this._items.length - 1) || index < 0) {
+ return
}
- Carousel.prototype.next = function () {
- if (this.sliding) return
- return this.slide('next')
+ if (this._isSliding) {
+ $(this._element).one(Carousel._Event.SLID, function () { this.to(index) }.bind(this))
+ return
}
- Carousel.prototype.prev = function () {
- if (this.sliding) return
- return this.slide('prev')
+ if (activeIndex == index) {
+ this['pause']()
+ this['cycle']()
+ return
}
- Carousel.prototype.slide = function (type, next) {
- var $active = this.$element.find('.carousel-item.active')
- var $next = next || this.getItemForDirection(type, $active)
- var isCycling = this.interval
- var direction = type == 'next' ? 'left' : 'right'
- var that = this
+ var direction = index > activeIndex ?
+ Carousel._Direction.NEXT :
+ Carousel._Direction.PREVIOUS
- if ($next.hasClass('active')) return (this.sliding = false)
+ this._slide(direction, this._items[index])
+}
- var relatedTarget = $next[0]
- var slideEvent = $.Event('slide.bs.carousel', {
- relatedTarget: relatedTarget,
- direction: direction
- })
- this.$element.trigger(slideEvent)
- if (slideEvent.isDefaultPrevented()) return
- this.sliding = true
+/**
+ * Add event listeners to root element
+ * @private
+ */
+Carousel.prototype._addEventListeners = function () {
+ if (this._config['keyboard']) {
+ $(this._element).on('keydown.bs.carousel', this._keydown.bind(this))
+ }
- isCycling && this.pause()
+ if (this._config['pause'] == 'hover' && !('ontouchstart' in document.documentElement)) {
+ $(this._element)
+ .on('mouseenter.bs.carousel', this['pause'].bind(this))
+ .on('mouseleave.bs.carousel', this['cycle'].bind(this))
+ }
+}
- if (this.$indicators.length) {
- this.$indicators.find('.active').removeClass('active')
- var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)])
- $nextIndicator && $nextIndicator.addClass('active')
- }
- var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid"
- if ($.support.transition && this.$element.hasClass('slide')) {
- $next.addClass(type)
- $next[0].offsetWidth // force reflow
- $active.addClass(direction)
- $next.addClass(direction)
- $active
- .one('bsTransitionEnd', function () {
- $next.removeClass([type, direction].join(' ')).addClass('active')
- $active.removeClass(['active', direction].join(' '))
- that.sliding = false
- setTimeout(function () {
- that.$element.trigger(slidEvent)
- }, 0)
- })
- .emulateTransitionEnd(Carousel.TRANSITION_DURATION)
- } else {
- $active.removeClass('active')
- $next.addClass('active')
- this.sliding = false
- this.$element.trigger(slidEvent)
- }
+/**
+ * Keydown handler
+ * @param {Event} event
+ * @private
+ */
+Carousel.prototype._keydown = function (event) {
+ event.preventDefault()
- isCycling && this.cycle()
+ if (/input|textarea/i.test(event.target.tagName)) return
- return this
+ switch (event.which) {
+ case 37: this['prev'](); break
+ case 39: this['next'](); break
+ default: return
+ }
+}
+
+
+/**
+ * Get item index
+ * @param {Element} element
+ * @return {number}
+ * @private
+ */
+Carousel.prototype._getItemIndex = function (element) {
+ this._items = $.makeArray($(element).parent().find(Carousel._Selector.ITEM))
+
+ return this._items.indexOf(element)
+}
+
+
+/**
+ * Get next displayed item based on direction
+ * @param {Carousel._Direction} direction
+ * @param {Element} activeElement
+ * @return {Element}
+ * @private
+ */
+Carousel.prototype._getItemByDirection = function (direction, activeElement) {
+ var activeIndex = this._getItemIndex(activeElement)
+ var isGoingToWrap = (direction === Carousel._Direction.PREVIOUS && activeIndex === 0) ||
+ (direction === Carousel._Direction.NEXT && activeIndex == (this._items.length - 1))
+
+ if (isGoingToWrap && !this._config['wrap']) {
+ return activeElement
}
+ var delta = direction == Carousel._Direction.PREVIOUS ? -1 : 1
+ var itemIndex = (activeIndex + delta) % this._items.length
- // CAROUSEL PLUGIN DEFINITION
- // ==========================
+ return itemIndex === -1 ? this._items[this._items.length - 1] : this._items[itemIndex]
+}
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.carousel')
- var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
- var action = typeof option == 'string' ? option : options.slide
- if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))
- if (typeof option == 'number') data.to(option)
- else if (action) data[action]()
- else if (options.interval) data.pause().cycle()
- })
+/**
+ * Trigger slide event on element
+ * @param {Element} relatedTarget
+ * @param {Carousel._ClassName} directionalClassname
+ * @return {$.Event}
+ * @private
+ */
+Carousel.prototype._triggerSlideEvent = function (relatedTarget, directionalClassname) {
+ var slideEvent = $.Event(Carousel._Event.SLIDE, {
+ relatedTarget: relatedTarget,
+ direction: directionalClassname
+ })
+
+ $(this._element).trigger(slideEvent)
+
+ return slideEvent
+}
+
+
+/**
+ * Set the active indicator if available
+ * @param {Element} element
+ * @private
+ */
+Carousel.prototype._setActiveIndicatorElement = function (element) {
+ if (this._indicatorsElement) {
+ $(this._indicatorsElement)
+ .find(Carousel._Selector.ACTIVE)
+ .removeClass(Carousel._ClassName.ACTIVE)
+
+ var nextIndicator = this._indicatorsElement.children[this._getItemIndex(element)]
+ if (nextIndicator) {
+ $(nextIndicator).addClass(Carousel._ClassName.ACTIVE)
+ }
}
+}
+
+
+/**
+ * Slide the carousel element in a direction
+ * @param {Carousel._Direction} direction
+ * @param {Element=} opt_nextElement
+ */
+Carousel.prototype._slide = function (direction, opt_nextElement) {
+ var activeElement = $(this._element).find(Carousel._Selector.ACTIVE_ITEM)[0]
+ var nextElement = opt_nextElement || activeElement && this._getItemByDirection(direction, activeElement)
+
+ var isCycling = !!this._interval
- var old = $.fn.carousel
+ var directionalClassName = direction == Carousel._Direction.NEXT ?
+ Carousel._ClassName.LEFT :
+ Carousel._ClassName.RIGHT
- $.fn.carousel = Plugin
- $.fn.carousel.Constructor = Carousel
+ if (nextElement && $(nextElement).hasClass(Carousel._ClassName.ACTIVE)) {
+ this._isSliding = false
+ return
+ }
+
+ var slideEvent = this._triggerSlideEvent(nextElement, directionalClassName)
+ if (slideEvent.isDefaultPrevented()) {
+ return
+ }
+ if (!activeElement || !nextElement) {
+ // some weirdness is happening, so we bail (maybe throw exception here alerting user that they're dom is off
+ return
+ }
- // CAROUSEL NO CONFLICT
- // ====================
+ this._isSliding = true
- $.fn.carousel.noConflict = function () {
- $.fn.carousel = old
- return this
+ if (isCycling) {
+ this['pause']()
}
+ this._setActiveIndicatorElement(nextElement)
- // CAROUSEL DATA-API
- // =================
+ var slidEvent = $.Event(Carousel._Event.SLID, { relatedTarget: nextElement, direction: directionalClassName })
- var clickHandler = function (e) {
- var href
- var $this = $(this)
- var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
- if (!$target.hasClass('carousel')) return
- var options = $.extend({}, $target.data(), $this.data())
- var slideIndex = $this.attr('data-slide-to')
- if (slideIndex) options.interval = false
+ if (Bootstrap.transition && $(this._element).hasClass(Carousel._ClassName.SLIDE)) {
+ $(nextElement).addClass(direction)
- Plugin.call($target, options)
+ Bootstrap.reflow(nextElement)
- if (slideIndex) {
- $target.data('bs.carousel').to(slideIndex)
- }
+ $(activeElement).addClass(directionalClassName)
+ $(nextElement).addClass(directionalClassName)
+
+ $(activeElement)
+ .one(Bootstrap.TRANSITION_END, function () {
+ $(nextElement)
+ .removeClass(directionalClassName)
+ .removeClass(direction)
+
+ $(nextElement).addClass(Carousel._ClassName.ACTIVE)
+
+ $(activeElement)
+ .removeClass(Carousel._ClassName.ACTIVE)
+ .removeClass(direction)
+ .removeClass(directionalClassName)
- e.preventDefault()
+ this._isSliding = false
+
+ setTimeout(function () {
+ $(this._element).trigger(slidEvent)
+ }.bind(this), 0)
+ }.bind(this))
+ .emulateTransitionEnd(Carousel._TRANSITION_DURATION)
+
+ } else {
+ $(activeElement).removeClass(Carousel._ClassName.ACTIVE)
+ $(nextElement).addClass(Carousel._ClassName.ACTIVE)
+
+ this._isSliding = false
+ $(this._element).trigger(slidEvent)
+ }
+
+ if (isCycling) {
+ this['cycle']()
}
+}
- $(document)
- .on('click.bs.carousel.data-api', '[data-slide]', clickHandler)
- .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler)
- $(window).on('load', function () {
- $('[data-ride="carousel"]').each(function () {
- var $carousel = $(this)
- Plugin.call($carousel, $carousel.data())
- })
- })
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Carousel._NAME] = Carousel._jQueryInterface
+
-}(jQuery);
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Carousel._NAME]['Constructor'] = Carousel
+
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Carousel._NAME]['noConflict'] = function () {
+ $.fn[Carousel._NAME] = Carousel._JQUERY_NO_CONFLICT
+ return this
+}
+
+
+/**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+$(document)
+ .on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', Carousel._dataApiClickHandler)
+
+$(window).on('load', function () {
+ $('[data-ride="carousel"]').each(function () {
+ var $carousel = $(this)
+ Carousel._jQueryInterface.call($carousel, /** @type {Object} */ ($carousel.data()))
+ })
+})
diff --git a/js/collapse.js b/js/collapse.js
index 2bc30e7ba4..156163e3f9 100644
--- a/js/collapse.js
+++ b/js/collapse.js
@@ -1,211 +1,455 @@
-/* ========================================================================
- * Bootstrap: collapse.js v3.3.2
+/** =======================================================================
+ * Bootstrap: collapse.js v4.0.0
* http://getbootstrap.com/javascript/#collapse
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @fileoverview - Bootstrap's collapse plugin. Flexible support for
+ * collapsible components like accordions and navigation.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.carousel
+ * + $.carousel.noConflict
+ * + $.carousel.Constructor
+ * + $.carousel.Constructor.VERSION
+ * + $.carousel.Constructor.Defaults
+ * + $.carousel.Constructor.Defaults.toggle
+ * + $.carousel.Constructor.Defaults.trigger
+ * + $.carousel.Constructor.Defaults.parent
+ * + $.carousel.Constructor.prototype.toggle
+ * + $.carousel.Constructor.prototype.show
+ * + $.carousel.Constructor.prototype.hide
+ *
+ * ========================================================================
+ */
+'use strict';
-+function ($) {
- 'use strict';
- // COLLAPSE PUBLIC CLASS DEFINITION
- // ================================
+/**
+ * Our collapse class.
+ * @param {Element!} element
+ * @param {Object=} opt_config
+ * @constructor
+ */
+var Collapse = function (element, opt_config) {
- var Collapse = function (element, options) {
- this.$element = $(element)
- this.options = $.extend({}, Collapse.DEFAULTS, options)
- this.$trigger = $(this.options.trigger).filter('[href="#' + element.id + '"], [data-target="#' + element.id + '"]')
- this.transitioning = null
+ /** @private {Element} */
+ this._element = element
- if (this.options.parent) {
- this.$parent = this.getParent()
- } else {
- this.addAriaAndCollapsedClass(this.$element, this.$trigger)
- }
+ /** @private {Object} */
+ this._config = $.extend({}, Collapse['Defaults'], opt_config)
- if (this.options.toggle) this.toggle()
- }
+ /** @private {Element} */
+ this._trigger = typeof this._config['trigger'] == 'string' ?
+ $(this._config['trigger'])[0] : this._config['trigger']
- Collapse.VERSION = '3.3.2'
+ /** @private {boolean} */
+ this._isTransitioning = false
- Collapse.TRANSITION_DURATION = 350
+ /** @private {?Element} */
+ this._parent = this._config['parent'] ? this._getParent() : null
- Collapse.DEFAULTS = {
- toggle: true,
- trigger: '[data-toggle="collapse"]'
+ if (!this._config['parent']) {
+ this._addAriaAndCollapsedClass(this._element, this._trigger)
}
- Collapse.prototype.dimension = function () {
- var hasWidth = this.$element.hasClass('width')
- return hasWidth ? 'width' : 'height'
+ if (this._config['toggle']) {
+ this['toggle']()
}
- Collapse.prototype.show = function () {
- if (this.transitioning || this.$element.hasClass('in')) return
-
- var activesData
- var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing')
+}
+
+
+/**
+ * @const
+ * @type {string}
+ */
+Collapse['VERSION'] = '4.0.0'
+
+
+/**
+ * @const
+ * @type {Object}
+ */
+Collapse['Defaults'] = {
+ 'toggle' : true,
+ 'trigger' : '[data-toggle="collapse"]',
+ 'parent' : null
+}
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Collapse._NAME = 'collapse'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Collapse._DATA_KEY = 'bs.collapse'
+
+
+/**
+ * @const
+ * @type {number}
+ * @private
+ */
+Collapse._TRANSITION_DURATION = 600
+
+
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+Collapse._JQUERY_NO_CONFLICT = $.fn[Collapse._NAME]
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Collapse._Event = {
+ SHOW : 'show.bs.collapse',
+ SHOWN : 'shown.bs.collapse',
+ HIDE : 'hide.bs.collapse',
+ HIDDEN : 'hidden.bs.collapse'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Collapse._ClassName = {
+ IN : 'in',
+ COLLAPSE : 'collapse',
+ COLLAPSING : 'collapsing',
+ COLLAPSED : 'collapsed'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Collapse._Dimension = {
+ WIDTH : 'width',
+ HEIGHT : 'height'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Collapse._Selector = {
+ ACTIVES : '.panel > .in, .panel > .collapsing'
+}
+
+
+/**
+ * Provides the jQuery Interface for the alert component.
+ * @param {Object|string=} opt_config
+ * @this {jQuery}
+ * @return {jQuery}
+ * @private
+ */
+Collapse._jQueryInterface = function (opt_config) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data(Collapse._DATA_KEY)
+ var config = $.extend({}, Collapse['Defaults'], $this.data(), typeof opt_config == 'object' && opt_config)
- if (actives && actives.length) {
- activesData = actives.data('bs.collapse')
- if (activesData && activesData.transitioning) return
+ if (!data && config['toggle'] && opt_config == 'show') {
+ config['toggle'] = false
}
- var startEvent = $.Event('show.bs.collapse')
- this.$element.trigger(startEvent)
- if (startEvent.isDefaultPrevented()) return
+ if (!data) {
+ data = new Collapse(this, config)
+ $this.data(Collapse._DATA_KEY, data)
+ }
- if (actives && actives.length) {
- Plugin.call(actives, 'hide')
- activesData || actives.data('bs.collapse', null)
+ if (typeof opt_config == 'string') {
+ data[opt_config]()
}
+ })
+}
+
+
+/**
+ * Function for getting target element from element
+ * @return {Element}
+ * @private
+ */
+Collapse._getTargetFromElement = function (element) {
+ var selector = Bootstrap.getSelectorFromElement(element)
+
+ return selector ? $(selector)[0] : null
+}
+
- var dimension = this.dimension()
+/**
+ * Toggles the collapse element based on the presence of the 'in' class
+ */
+Collapse.prototype['toggle'] = function () {
+ if ($(this._element).hasClass(Collapse._ClassName.IN)) {
+ this['hide']()
+ } else {
+ this['show']()
+ }
+}
- this.$element
- .removeClass('collapse')
- .addClass('collapsing')[dimension](0)
- .attr('aria-expanded', true)
- this.$trigger
- .removeClass('collapsed')
- .attr('aria-expanded', true)
+/**
+ * Show's the collapsing element
+ */
+Collapse.prototype['show'] = function () {
+ if (this._isTransitioning || $(this._element).hasClass(Collapse._ClassName.IN)) {
+ return
+ }
- this.transitioning = 1
+ var activesData, actives
- var complete = function () {
- this.$element
- .removeClass('collapsing')
- .addClass('collapse in')[dimension]('')
- this.transitioning = 0
- this.$element
- .trigger('shown.bs.collapse')
+ if (this._parent) {
+ actives = $.makeArray($(Collapse._Selector.ACTIVES))
+ if (!actives.length) {
+ actives = null
}
+ }
- if (!$.support.transition) return complete.call(this)
+ if (actives) {
+ activesData = $(actives).data(Collapse._DATA_KEY)
+ if (activesData && activesData._isTransitioning) {
+ return
+ }
+ }
- var scrollSize = $.camelCase(['scroll', dimension].join('-'))
+ var startEvent = $.Event(Collapse._Event.SHOW)
+ $(this._element).trigger(startEvent)
+ if (startEvent.isDefaultPrevented()) {
+ return
+ }
- this.$element
- .one('bsTransitionEnd', $.proxy(complete, this))
- .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
+ if (actives) {
+ Collapse._jQueryInterface.call($(actives), 'hide')
+ if (!activesData) {
+ $(actives).data(Collapse._DATA_KEY, null)
+ }
}
- Collapse.prototype.hide = function () {
- if (this.transitioning || !this.$element.hasClass('in')) return
+ var dimension = this._getDimension()
- var startEvent = $.Event('hide.bs.collapse')
- this.$element.trigger(startEvent)
- if (startEvent.isDefaultPrevented()) return
+ $(this._element)
+ .removeClass(Collapse._ClassName.COLLAPSE)
+ .addClass(Collapse._ClassName.COLLAPSING)
- var dimension = this.dimension()
+ this._element.style[dimension] = 0
+ this._element.setAttribute('aria-expanded', true)
- this.$element[dimension](this.$element[dimension]())[0].offsetHeight
+ if (this._trigger) {
+ $(this._trigger).removeClass(Collapse._ClassName.COLLAPSED)
+ this._trigger.setAttribute('aria-expanded', true)
+ }
- this.$element
- .addClass('collapsing')
- .removeClass('collapse in')
- .attr('aria-expanded', false)
+ this['setTransitioning'](true)
- this.$trigger
- .addClass('collapsed')
- .attr('aria-expanded', false)
+ var complete = function () {
+ $(this._element)
+ .removeClass(Collapse._ClassName.COLLAPSING)
+ .addClass(Collapse._ClassName.COLLAPSE)
+ .addClass(Collapse._ClassName.IN)
- this.transitioning = 1
+ this._element.style[dimension] = ''
- var complete = function () {
- this.transitioning = 0
- this.$element
- .removeClass('collapsing')
- .addClass('collapse')
- .trigger('hidden.bs.collapse')
- }
+ this['setTransitioning'](false)
- if (!$.support.transition) return complete.call(this)
+ $(this._element).trigger(Collapse._Event.SHOWN)
+ }.bind(this)
- this.$element
- [dimension](0)
- .one('bsTransitionEnd', $.proxy(complete, this))
- .emulateTransitionEnd(Collapse.TRANSITION_DURATION)
+ if (!Bootstrap.transition) {
+ complete()
+ return
}
- Collapse.prototype.toggle = function () {
- this[this.$element.hasClass('in') ? 'hide' : 'show']()
+ var scrollSize = 'scroll' + (dimension[0].toUpperCase() + dimension.slice(1))
+
+ $(this._element)
+ .one(Bootstrap.TRANSITION_END, complete)
+ .emulateTransitionEnd(Collapse._TRANSITION_DURATION)
+
+ this._element.style[dimension] = this._element[scrollSize] + 'px'
+}
+
+
+/**
+ * Hides's the collapsing element
+ */
+Collapse.prototype['hide'] = function () {
+ if (this._isTransitioning || !$(this._element).hasClass(Collapse._ClassName.IN)) {
+ return
}
- Collapse.prototype.getParent = function () {
- return $(this.options.parent)
- .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
- .each($.proxy(function (i, element) {
- var $element = $(element)
- this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)
- }, this))
- .end()
+ var startEvent = $.Event(Collapse._Event.HIDE)
+ $(this._element).trigger(startEvent)
+ if (startEvent.isDefaultPrevented()) return
+
+ var dimension = this._getDimension()
+ var offsetDimension = dimension === Collapse._Dimension.WIDTH ?
+ 'offsetWidth' : 'offsetHeight'
+
+ this._element.style[dimension] = this._element[offsetDimension] + 'px'
+
+ Bootstrap.reflow(this._element)
+
+ $(this._element)
+ .addClass(Collapse._ClassName.COLLAPSING)
+ .removeClass(Collapse._ClassName.COLLAPSE)
+ .removeClass(Collapse._ClassName.IN)
+
+ this._element.setAttribute('aria-expanded', false)
+
+ if (this._trigger) {
+ $(this._trigger).addClass(Collapse._ClassName.COLLAPSED)
+ this._trigger.setAttribute('aria-expanded', false)
}
- Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
- var isOpen = $element.hasClass('in')
+ this['setTransitioning'](true)
+
+ var complete = function () {
+ this['setTransitioning'](false)
+ $(this._element)
+ .removeClass(Collapse._ClassName.COLLAPSING)
+ .addClass(Collapse._ClassName.COLLAPSE)
+ .trigger(Collapse._Event.HIDDEN)
+
+ }.bind(this)
- $element.attr('aria-expanded', isOpen)
- $trigger
- .toggleClass('collapsed', !isOpen)
- .attr('aria-expanded', isOpen)
+ this._element.style[dimension] = 0
+
+ if (!Bootstrap.transition) {
+ return complete()
}
- function getTargetFromTrigger($trigger) {
- var href
- var target = $trigger.attr('data-target')
- || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
+ $(this._element)
+ .one(Bootstrap.TRANSITION_END, complete)
+ .emulateTransitionEnd(Collapse._TRANSITION_DURATION)
+}
+
+
- return $(target)
+/**
+ * @param {boolean} isTransitioning
+ */
+Collapse.prototype['setTransitioning'] = function (isTransitioning) {
+ this._isTransitioning = isTransitioning
+}
+
+
+/**
+ * Returns the collapsing dimension
+ * @return {string}
+ * @private
+ */
+Collapse.prototype._getDimension = function () {
+ var hasWidth = $(this._element).hasClass(Collapse._Dimension.WIDTH)
+ return hasWidth ? Collapse._Dimension.WIDTH : Collapse._Dimension.HEIGHT
+}
+
+
+/**
+ * Returns the parent element
+ * @return {Element}
+ * @private
+ */
+Collapse.prototype._getParent = function () {
+ var selector = '[data-toggle="collapse"][data-parent="' + this._config['parent'] + '"]'
+ var parent = $(this._config['parent'])[0]
+ var elements = /** @type {Array.<Element>} */ ($.makeArray($(parent).find(selector)))
+
+ for (var i = 0; i < elements.length; i++) {
+ this._addAriaAndCollapsedClass(Collapse._getTargetFromElement(elements[i]), elements[i])
}
+ return parent
+}
- // COLLAPSE PLUGIN DEFINITION
- // ==========================
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.collapse')
- var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
+/**
+ * Returns the parent element
+ * @param {Element} element
+ * @param {Element} trigger
+ * @private
+ */
+Collapse.prototype._addAriaAndCollapsedClass = function (element, trigger) {
+ if (element) {
+ var isOpen = $(element).hasClass(Collapse._ClassName.IN)
+ element.setAttribute('aria-expanded', isOpen)
- if (!data && options.toggle && option == 'show') options.toggle = false
- if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
- if (typeof option == 'string') data[option]()
- })
+ if (trigger) {
+ trigger.setAttribute('aria-expanded', isOpen)
+ $(trigger).toggleClass(Collapse._ClassName.COLLAPSED, !isOpen)
+ }
}
+}
- var old = $.fn.collapse
- $.fn.collapse = Plugin
- $.fn.collapse.Constructor = Collapse
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
- // COLLAPSE NO CONFLICT
- // ====================
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Collapse._NAME] = Collapse._jQueryInterface
- $.fn.collapse.noConflict = function () {
- $.fn.collapse = old
- return this
- }
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Collapse._NAME]['Constructor'] = Collapse
- // COLLAPSE DATA-API
- // =================
- $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
- var $this = $(this)
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Collapse._NAME]['noConflict'] = function () {
+ $.fn[Collapse._NAME] = Collapse._JQUERY_NO_CONFLICT
+ return this
+}
- if (!$this.attr('data-target')) e.preventDefault()
- var $target = getTargetFromTrigger($this)
- var data = $target.data('bs.collapse')
- var option = data ? 'toggle' : $.extend({}, $this.data(), { trigger: this })
+/**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
- Plugin.call($target, option)
- })
+$(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (event) {
+ event.preventDefault()
+
+ var target = Collapse._getTargetFromElement(this)
+
+ var data = $(target).data(Collapse._DATA_KEY)
+ var config = data ? 'toggle' : $.extend({}, $(this).data(), { trigger: this })
-}(jQuery);
+ Collapse._jQueryInterface.call($(target), config)
+})
diff --git a/js/dropdown.js b/js/dropdown.js
index 200e1c67b4..4599d5ba92 100644
--- a/js/dropdown.js
+++ b/js/dropdown.js
@@ -1,161 +1,322 @@
-/* ========================================================================
- * Bootstrap: dropdown.js v3.3.2
- * http://getbootstrap.com/javascript/#dropdowns
+/** =======================================================================
+ * Bootstrap: dropdown.js v4.0.0
+ * http://getbootstrap.com/javascript/#dropdown
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
+ * ========================================================================
+ * @fileoverview - Add dropdown menus to nearly anything with this simple
+ * plugin, including the navbar, tabs, and pills.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.dropdown
+ * + $.dropdown.noConflict
+ * + $.dropdown.Constructor
+ * + $.dropdown.Constructor.VERSION
+ * + $.dropdown.Constructor.prototype.toggle
+ *
+ * ========================================================================
+ */
+
+'use strict';
+
+
+/**
+ * Our dropdown class.
+ * @param {Element!} element
+ * @constructor
+ */
+var Dropdown = function (element) {
+ $(element).on('click.bs.dropdown', this['toggle'])
+}
+
+
+/**
+ * @const
+ * @type {string}
+ */
+Dropdown['VERSION'] = '4.0.0'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Dropdown._NAME = 'dropdown'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Dropdown._DATA_KEY = 'bs.dropdown'
+
+
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+Dropdown._JQUERY_NO_CONFLICT = $.fn[Dropdown._NAME]
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Dropdown._Event = {
+ HIDE : 'hide.bs.dropdown',
+ HIDDEN : 'hidden.bs.dropdown',
+ SHOW : 'show.bs.dropdown',
+ SHOWN : 'shown.bs.dropdown'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Dropdown._ClassName = {
+ BACKDROP : 'dropdown-backdrop',
+ DISABLED : 'disabled',
+ OPEN : 'open'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Dropdown._Selector = {
+ BACKDROP : '.dropdown-backdrop',
+ DATA_TOGGLE : '[data-toggle="dropdown"]',
+ FORM_CHILD : '.dropdown form',
+ ROLE_MENU : '[role="menu"]',
+ ROLE_LISTBOX : '[role="listbox"]',
+ NAVBAR_NAV : '.navbar-nav',
+ VISIBLE_ITEMS : '[role="menu"] li:not(.divider) a, [role="listbox"] li:not(.divider) a'
+}
+
+
+/**
+ * Provides the jQuery Interface for the alert component.
+ * @param {string=} opt_config
+ * @this {jQuery}
+ * @return {jQuery}
+ * @private
+ */
+Dropdown._jQueryInterface = function (opt_config) {
+ return this.each(function () {
+ var data = $(this).data(Dropdown._DATA_KEY)
+
+ if (!data) {
+ $(this).data(Dropdown._DATA_KEY, (data = new Dropdown(this)))
+ }
-+function ($) {
- 'use strict';
+ if (typeof opt_config === 'string') {
+ data[opt_config].call(this)
+ }
+ })
+}
- // DROPDOWN CLASS DEFINITION
- // =========================
- var backdrop = '.dropdown-backdrop'
- var toggle = '[data-toggle="dropdown"]'
- var Dropdown = function (element) {
- $(element).on('click.bs.dropdown', this.toggle)
+/**
+ * @param {Event=} opt_event
+ * @private
+ */
+Dropdown._clearMenus = function (opt_event) {
+ if (opt_event && opt_event.which == 3) {
+ return
}
- Dropdown.VERSION = '3.3.2'
-
- Dropdown.prototype.toggle = function (e) {
- var $this = $(this)
+ var backdrop = $(Dropdown._Selector.BACKDROP)[0]
+ if (backdrop) {
+ backdrop.parentNode.removeChild(backdrop)
+ }
- if ($this.is('.disabled, :disabled')) return
+ var toggles = /** @type {Array.<Element>} */ ($.makeArray($(Dropdown._Selector.DATA_TOGGLE)))
- var $parent = getParent($this)
- var isActive = $parent.hasClass('open')
+ for (var i = 0; i < toggles.length; i++) {
+ var parent = Dropdown._getParentFromElement(toggles[i])
+ var relatedTarget = { 'relatedTarget': toggles[i] }
- clearMenus()
+ if (!$(parent).hasClass(Dropdown._ClassName.OPEN)) {
+ continue
+ }
- if (!isActive) {
- if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
- // if mobile we use a backdrop because click events don't delegate
- $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
- }
+ var hideEvent = $.Event(Dropdown._Event.HIDE, relatedTarget)
+ $(parent).trigger(hideEvent)
+ if (hideEvent.isDefaultPrevented()) {
+ continue
+ }
- var relatedTarget = { relatedTarget: this }
- $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
+ toggles[i].setAttribute('aria-expanded', 'false')
- if (e.isDefaultPrevented()) return
+ $(parent)
+ .removeClass(Dropdown._ClassName.OPEN)
+ .trigger(Dropdown._Event.HIDDEN, relatedTarget)
+ }
+}
- $this
- .trigger('focus')
- .attr('aria-expanded', 'true')
- $parent
- .toggleClass('open')
- .trigger('shown.bs.dropdown', relatedTarget)
- }
+/**
+ * @param {Element} element
+ * @return {Element}
+ * @private
+ */
+Dropdown._getParentFromElement = function (element) {
+ var selector = Bootstrap.getSelectorFromElement(element)
- return false
+ if (selector) {
+ var parent = $(selector)[0]
}
- Dropdown.prototype.keydown = function (e) {
- if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return
+ return /** @type {Element} */ (parent || element.parentNode)
+}
+
- var $this = $(this)
+/**
+ * @param {Event} event
+ * @this {Element}
+ * @private
+ */
+Dropdown._dataApiKeydownHandler = function (event) {
+ if (!/(38|40|27|32)/.test(event.which) || /input|textarea/i.test(event.target.tagName)) {
+ return
+ }
- e.preventDefault()
- e.stopPropagation()
+ event.preventDefault()
+ event.stopPropagation()
- if ($this.is('.disabled, :disabled')) return
+ if (this.disabled || $(this).hasClass(Dropdown._ClassName.DISABLED)) {
+ return
+ }
- var $parent = getParent($this)
- var isActive = $parent.hasClass('open')
+ var parent = Dropdown._getParentFromElement(this)
+ var isActive = $(parent).hasClass(Dropdown._ClassName.OPEN)
- if ((!isActive && e.which != 27) || (isActive && e.which == 27)) {
- if (e.which == 27) $parent.find(toggle).trigger('focus')
- return $this.trigger('click')
+ if ((!isActive && event.which != 27) || (isActive && event.which == 27)) {
+ if (event.which == 27) {
+ var toggle = $(parent).find(Dropdown._Selector.DATA_TOGGLE)[0]
+ $(toggle).trigger('focus')
}
+ $(this).trigger('click')
+ return
+ }
- var desc = ' li:not(.divider):visible a'
- var $items = $parent.find('[role="menu"]' + desc + ', [role="listbox"]' + desc)
+ var items = $.makeArray($(Dropdown._Selector.VISIBLE_ITEMS))
- if (!$items.length) return
+ items = items.filter(function (item) {
+ return item.offsetWidth || item.offsetHeight
+ })
- var index = $items.index(e.target)
+ if (!items.length) {
+ return
+ }
- if (e.which == 38 && index > 0) index-- // up
- if (e.which == 40 && index < $items.length - 1) index++ // down
- if (!~index) index = 0
+ var index = items.indexOf(event.target)
- $items.eq(index).trigger('focus')
- }
+ if (event.which == 38 && index > 0) index-- // up
+ if (event.which == 40 && index < items.length - 1) index++ // down
+ if (!~index) index = 0
+
+ items[index].focus()
+}
- function clearMenus(e) {
- if (e && e.which === 3) return
- $(backdrop).remove()
- $(toggle).each(function () {
- var $this = $(this)
- var $parent = getParent($this)
- var relatedTarget = { relatedTarget: this }
- if (!$parent.hasClass('open')) return
+/**
+ * Toggles the dropdown
+ * @this {Element}
+ * @return {boolean|undefined}
+ */
+Dropdown.prototype['toggle'] = function () {
+ if (this.disabled || $(this).hasClass(Dropdown._ClassName.DISABLED)) {
+ return
+ }
- $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
+ var parent = Dropdown._getParentFromElement(this)
+ var isActive = $(parent).hasClass(Dropdown._ClassName.OPEN)
- if (e.isDefaultPrevented()) return
+ Dropdown._clearMenus()
- $this.attr('aria-expanded', 'false')
- $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
- })
+ if (isActive) {
+ return false
}
- function getParent($this) {
- var selector = $this.attr('data-target')
+ if ('ontouchstart' in document.documentElement && !$(parent).closest(Dropdown._Selector.NAVBAR_NAV).length) {
+ // if mobile we use a backdrop because click events don't delegate
+ var dropdown = document.createElement('div')
+ dropdown.className = Dropdown._ClassName.BACKDROP
+ this.parentNode.insertBefore(this, dropdown)
+ $(dropdown).on('click', Dropdown._clearMenus)
+ }
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
- }
+ var relatedTarget = { 'relatedTarget': this }
+ var showEvent = $.Event(Dropdown._Event.SHOW, relatedTarget)
- var $parent = selector && $(selector)
+ $(parent).trigger(showEvent)
- return $parent && $parent.length ? $parent : $this.parent()
+ if (showEvent.isDefaultPrevented()) {
+ return
}
+ this.focus()
+ this.setAttribute('aria-expanded', 'true')
- // DROPDOWN PLUGIN DEFINITION
- // ==========================
+ $(parent).toggleClass(Dropdown._ClassName.OPEN)
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.dropdown')
+ $(parent).trigger(Dropdown._Event.SHOWN, relatedTarget)
- if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
- if (typeof option == 'string') data[option].call($this)
- })
- }
+ return false
+}
- var old = $.fn.dropdown
- $.fn.dropdown = Plugin
- $.fn.dropdown.Constructor = Dropdown
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Dropdown._NAME] = Dropdown._jQueryInterface
- // DROPDOWN NO CONFLICT
- // ====================
- $.fn.dropdown.noConflict = function () {
- $.fn.dropdown = old
- return this
- }
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Dropdown._NAME]['Constructor'] = Dropdown
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Dropdown._NAME]['noConflict'] = function () {
+ $.fn[Dropdown._NAME] = Dropdown._JQUERY_NO_CONFLICT
+ return this
+}
- // APPLY TO STANDARD DROPDOWN ELEMENTS
- // ===================================
- $(document)
- .on('click.bs.dropdown.data-api', clearMenus)
- .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
- .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
- .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
- .on('keydown.bs.dropdown.data-api', '[role="menu"]', Dropdown.prototype.keydown)
- .on('keydown.bs.dropdown.data-api', '[role="listbox"]', Dropdown.prototype.keydown)
+/**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
-}(jQuery);
+$(document)
+ .on('click.bs.dropdown.data-api', Dropdown._clearMenus)
+ .on('click.bs.dropdown.data-api', Dropdown._Selector.FORM_CHILD, function (e) { e.stopPropagation() })
+ .on('click.bs.dropdown.data-api', Dropdown._Selector.DATA_TOGGLE, Dropdown.prototype['toggle'])
+ .on('keydown.bs.dropdown.data-api', Dropdown._Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler)
+ .on('keydown.bs.dropdown.data-api', Dropdown._Selector.ROLE_MENU, Dropdown._dataApiKeydownHandler)
+ .on('keydown.bs.dropdown.data-api', Dropdown._Selector.ROLE_LISTBOX, Dropdown._dataApiKeydownHandler)
diff --git a/js/externs/bootstrap.js b/js/externs/bootstrap.js
new file mode 100644
index 0000000000..b4c2e36c28
--- /dev/null
+++ b/js/externs/bootstrap.js
@@ -0,0 +1,6 @@
+jQuery.event.prototype.handleObj = function () {}
+jQuery.event.prototype.handleObj.handler = function () {}
+$.event.special = function () {}
+$.event.trueHover = true
+$.offset = {}
+$.offset.setOffset = function (a,b,c) {}
diff --git a/js/externs/jQuery.js b/js/externs/jQuery.js
new file mode 100644
index 0000000000..f9b80c497d
--- /dev/null
+++ b/js/externs/jQuery.js
@@ -0,0 +1,2159 @@
+/*
+ * Copyright 2011 The Closure Compiler Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Externs for jQuery 1.9.1
+ *
+ * Note that some functions use different return types depending on the number
+ * of parameters passed in. In these cases, you may need to annotate the type
+ * of the result in your code, so the JSCompiler understands which type you're
+ * expecting. For example:
+ * <code>var elt = /** @type {Element} * / (foo.get(0));</code>
+ *
+ * @see http://api.jquery.com/
+ * @externs
+ */
+
+/**
+ * @typedef {(Window|Document|Element|Array.<Element>|string|jQuery|
+ * NodeList)}
+ */
+var jQuerySelector;
+
+/** @typedef {function(...)|Array.<function(...)>} */
+var jQueryCallback;
+
+/** @typedef {
+ {
+ accepts: (Object.<string, string>|undefined),
+ async: (?boolean|undefined),
+ beforeSend: (function(jQuery.jqXHR, (jQueryAjaxSettings|Object.<string, *>))|undefined),
+ cache: (?boolean|undefined),
+ complete: (function(jQuery.jqXHR, string)|undefined),
+ contents: (Object.<string, RegExp>|undefined),
+ contentType: (?string|undefined),
+ context: (Object.<?, ?>|jQueryAjaxSettings|undefined),
+ converters: (Object.<string, Function>|undefined),
+ crossDomain: (?boolean|undefined),
+ data: (Object.<?, ?>|?string|Array.<?>|undefined),
+ dataFilter: (function(string, string):?|undefined),
+ dataType: (?string|undefined),
+ error: (function(jQuery.jqXHR, string, string)|undefined),
+ global: (?boolean|undefined),
+ headers: (Object.<?, ?>|undefined),
+ ifModified: (?boolean|undefined),
+ isLocal: (?boolean|undefined),
+ jsonp: (?string|undefined),
+ jsonpCallback: (?string|function()|undefined),
+ mimeType: (?string|undefined),
+ password: (?string|undefined),
+ processData: (?boolean|undefined),
+ scriptCharset: (?string|undefined),
+ statusCode: (Object.<number, function()>|undefined),
+ success: (function(?, string, jQuery.jqXHR)|undefined),
+ timeout: (?number|undefined),
+ traditional: (?boolean|undefined),
+ type: (?string|undefined),
+ url: (?string|undefined),
+ username: (?string|undefined),
+ xhr: (function():(ActiveXObject|XMLHttpRequest)|undefined),
+ xhrFields: (Object.<?, ?>|undefined)
+ }} */
+var jQueryAjaxSettings;
+
+/**
+ * @constructor
+ * @param {(jQuerySelector|Element|Object|Array.<Element>|jQuery|string|
+ * function())=} arg1
+ * @param {(Element|jQuery|Document|
+ * Object.<string, (string|function(!jQuery.event=))>)=} arg2
+ * @return {!jQuery}
+ */
+function jQuery(arg1, arg2) {}
+
+/**
+ * @constructor
+ * @extends {jQuery}
+ * @param {(jQuerySelector|Element|Object|Array.<Element>|jQuery|string|
+ * function())=} arg1
+ * @param {(Element|jQuery|Document|
+ * Object.<string, (string|function(!jQuery.event=))>)=} arg2
+ * @return {!jQuery}
+ */
+function $(arg1, arg2) {}
+
+/**
+ * @param {(jQuerySelector|Array.<Element>|string|jQuery)} arg1
+ * @param {Element=} context
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.add = function(arg1, context) {};
+
+/**
+ * @param {(jQuerySelector|Array.<Element>|string|jQuery)=} arg1
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.addBack = function(arg1) {};
+
+/**
+ * @param {(string|function(number,String))} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.addClass = function(arg1) {};
+
+/**
+ * @param {(string|Element|jQuery|function(number))} arg1
+ * @param {(string|Element|Array.<Element>|jQuery)=} content
+ * @return {!jQuery}
+ */
+jQuery.prototype.after = function(arg1, content) {};
+
+/**
+ * @param {(string|jQueryAjaxSettings|Object.<string,*>)} arg1
+ * @param {(jQueryAjaxSettings|Object.<string, *>)=} settings
+ * @return {jQuery.jqXHR}
+ */
+jQuery.ajax = function(arg1, settings) {};
+
+/**
+ * @param {(string|jQueryAjaxSettings|Object.<string, *>)} arg1
+ * @param {(jQueryAjaxSettings|Object.<string, *>)=} settings
+ * @return {jQuery.jqXHR}
+ */
+$.ajax = function(arg1, settings) {};
+
+/**
+ * @param {function(!jQuery.event,XMLHttpRequest,(jQueryAjaxSettings|Object.<string, *>))} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ajaxComplete = function(handler) {};
+
+/**
+ * @param {function(!jQuery.event,jQuery.jqXHR,(jQueryAjaxSettings|Object.<string, *>),*)} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ajaxError = function(handler) {};
+
+/**
+ * @param {(string|function((jQueryAjaxSettings|Object.<string, *>),(jQueryAjaxSettings|Object.<string, *>),jQuery.jqXHR))} dataTypes
+ * @param {function((jQueryAjaxSettings|Object.<string, *>),(jQueryAjaxSettings|Object.<string, *>),jQuery.jqXHR)=} handler
+ */
+jQuery.ajaxPrefilter = function(dataTypes, handler) {};
+
+/**
+ * @param {(string|function((jQueryAjaxSettings|Object.<string, *>),(jQueryAjaxSettings|Object.<string, *>),jQuery.jqXHR))} dataTypes
+ * @param {function((jQueryAjaxSettings|Object.<string, *>),(jQueryAjaxSettings|Object.<string, *>),jQuery.jqXHR)=} handler
+ */
+$.ajaxPrefilter = function(dataTypes, handler) {};
+
+/**
+ * @param {function(!jQuery.event,jQuery.jqXHR,(jQueryAjaxSettings|Object.<string, *>))} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ajaxSend = function(handler) {};
+
+/** @const {jQueryAjaxSettings|Object.<string, *>} */
+jQuery.ajaxSettings;
+
+/** @const {jQueryAjaxSettings|Object.<string, *>} */
+$.ajaxSettings = {};
+
+/** @type {Object.<string, boolean>} */
+jQuery.ajaxSettings.flatOptions = {};
+
+/** @type {Object.<string, boolean>} */
+$.ajaxSettings.flatOptions = {};
+
+/** @type {boolean} */
+jQuery.ajaxSettings.processData;
+
+/** @type {boolean} */
+$.ajaxSettings.processData;
+
+/** @type {Object.<string, string>} */
+jQuery.ajaxSettings.responseFields = {};
+
+/** @type {Object.<string, string>} */
+$.ajaxSettings.responseFields = {};
+
+/** @param {jQueryAjaxSettings|Object.<string, *>} options */
+jQuery.ajaxSetup = function(options) {};
+
+/** @param {jQueryAjaxSettings|Object.<string, *>} options */
+$.ajaxSetup = function(options) {};
+
+/**
+ * @param {function()} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ajaxStart = function(handler) {};
+
+/**
+ * @param {function()} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ajaxStop = function(handler) {};
+
+/**
+ * @param {function(!jQuery.event,XMLHttpRequest,(jQueryAjaxSettings|Object.<string, *>), ?)} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ajaxSuccess = function(handler) {};
+
+/**
+ * @deprecated Please use .addBack(selector) instead.
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.andSelf = function() {};
+
+/**
+ * @param {Object.<string,*>} properties
+ * @param {(string|number|function()|Object.<string,*>)=} arg2
+ * @param {(string|function())=} easing
+ * @param {function()=} complete
+ * @return {!jQuery}
+ */
+jQuery.prototype.animate = function(properties, arg2, easing, complete) {};
+
+/**
+ * @param {(string|Element|Array.<Element>|jQuery|function(number,string))} arg1
+ * @param {...(string|Element|Array.<Element>|jQuery)} content
+ * @return {!jQuery}
+ */
+jQuery.prototype.append = function(arg1, content) {};
+
+/**
+ * @param {(jQuerySelector|Element|jQuery)} target
+ * @return {!jQuery}
+ */
+jQuery.prototype.appendTo = function(target) {};
+
+/**
+ * @param {(string|Object.<string,*>)} arg1
+ * @param {(string|number|boolean|function(number,string))=} arg2
+ * @return {(string|!jQuery)}
+ */
+jQuery.prototype.attr = function(arg1, arg2) {};
+
+/**
+ * @param {(string|Element|jQuery|function())} arg1
+ * @param {(string|Element|Array.<Element>|jQuery)=} content
+ * @return {!jQuery}
+ */
+jQuery.prototype.before = function(arg1, content) {};
+
+/**
+ * @param {(string|Object.<string, function(!jQuery.event=)>)} arg1
+ * @param {(Object.<string, *>|function(!jQuery.event=)|boolean)=} eventData
+ * @param {(function(!jQuery.event=)|boolean)=} arg3
+ * @return {!jQuery}
+ */
+jQuery.prototype.bind = function(arg1, eventData, arg3) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.blur = function(arg1, handler) {};
+
+/**
+ * @constructor
+ * @private
+ */
+jQuery.callbacks = function () {};
+
+/**
+ * @param {string=} flags
+ * @return {jQuery.callbacks}
+ */
+jQuery.Callbacks = function (flags) {};
+
+/** @param {function()} callbacks */
+jQuery.callbacks.prototype.add = function(callbacks) {};
+
+/** @return {undefined} */
+jQuery.callbacks.prototype.disable = function() {};
+
+/** @return {undefined} */
+jQuery.callbacks.prototype.empty = function() {};
+
+/** @param {...*} var_args */
+jQuery.callbacks.prototype.fire = function(var_args) {};
+
+/** @return {boolean} */
+jQuery.callbacks.prototype.fired = function() {};
+
+/** @param {...*} var_args */
+jQuery.callbacks.prototype.fireWith = function(var_args) {};
+
+/**
+ * @param {function()} callback
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.callbacks.prototype.has = function(callback) {};
+
+/** @return {undefined} */
+jQuery.callbacks.prototype.lock = function() {};
+
+/** @return {boolean} */
+jQuery.callbacks.prototype.locked = function() {};
+
+/** @param {function()} callbacks */
+jQuery.callbacks.prototype.remove = function(callbacks) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.change = function(arg1, handler) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.children = function(selector) {};
+
+/**
+ * @param {string=} queueName
+ * @return {!jQuery}
+ */
+jQuery.prototype.clearQueue = function(queueName) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.click = function(arg1, handler) {};
+
+/**
+ * @param {boolean=} withDataAndEvents
+ * @param {boolean=} deepWithDataAndEvents
+ * @return {!jQuery}
+ * @suppress {checkTypes} see https://code.google.com/p/closure-compiler/issues/detail?id=583
+ */
+jQuery.prototype.clone = function(withDataAndEvents, deepWithDataAndEvents) {};
+
+/**
+ * @param {(jQuerySelector|jQuery|Element|string)} arg1
+ * @param {Element=} context
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.closest = function(arg1, context) {};
+
+/**
+ * @param {Element} container
+ * @param {Element} contained
+ * @return {boolean}
+ */
+jQuery.contains = function(container, contained) {};
+
+/**
+ * @param {Element} container
+ * @param {Element} contained
+ * @return {boolean}
+ */
+$.contains = function(container, contained) {};
+
+/**
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.contents = function() {};
+
+/** @type {Element|Document} */
+jQuery.prototype.context;
+
+/**
+ * @param {(string|Object.<string,*>)} arg1
+ * @param {(string|number|function(number,*))=} arg2
+ * @return {(string|!jQuery)}
+ */
+jQuery.prototype.css = function(arg1, arg2) {};
+
+/** @type {Object.<string, *>} */
+jQuery.cssHooks;
+
+/** @type {Object.<string, *>} */
+$.cssHooks;
+
+/**
+ * @param {Element} elem
+ * @param {string=} key
+ * @param {*=} value
+ * @return {*}
+ */
+jQuery.data = function(elem, key, value) {};
+
+/**
+ * @param {(string|Object.<string, *>)=} arg1
+ * @param {*=} value
+ * @return {*}
+ */
+jQuery.prototype.data = function(arg1, value) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} key
+ * @param {*=} value
+ * @return {*}
+ */
+$.data = function(elem, key, value) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.dblclick = function(arg1, handler) {};
+
+/**
+ * @constructor
+ * @implements {jQuery.Promise}
+ * @param {function()=} opt_fn
+ * @see http://api.jquery.com/category/deferred-object/
+ */
+jQuery.deferred = function(opt_fn) {};
+
+/**
+ * @constructor
+ * @extends {jQuery.deferred}
+ * @param {function()=} opt_fn
+ * @return {jQuery.Deferred}
+ */
+jQuery.Deferred = function(opt_fn) {};
+
+/**
+ * @constructor
+ * @extends {jQuery.deferred}
+ * @param {function()=} opt_fn
+ * @see http://api.jquery.com/category/deferred-object/
+ */
+$.deferred = function(opt_fn) {};
+
+/**
+ * @constructor
+ * @extends {jQuery.deferred}
+ * @param {function()=} opt_fn
+ * @return {jQuery.deferred}
+ */
+$.Deferred = function(opt_fn) {};
+
+/**
+ * @override
+ * @param {jQueryCallback} alwaysCallbacks
+ * @param {jQueryCallback=} alwaysCallbacks2
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.always
+ = function(alwaysCallbacks, alwaysCallbacks2) {};
+
+/**
+ * @override
+ * @param {jQueryCallback} doneCallbacks
+ * @param {jQueryCallback=} doneCallbacks2
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.done = function(doneCallbacks, doneCallbacks2) {};
+
+/**
+ * @override
+ * @param {jQueryCallback} failCallbacks
+ * @param {jQueryCallback=} failCallbacks2
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.fail = function(failCallbacks, failCallbacks2) {};
+
+/**
+ * @param {...*} var_args
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.notify = function(var_args) {};
+
+/**
+ * @param {Object} context
+ * @param {...*} var_args
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.notifyWith = function(context, var_args) {};
+
+/**
+ * @deprecated Please use deferred.then() instead.
+ * @override
+ * @param {function()=} doneFilter
+ * @param {function()=} failFilter
+ * @param {function()=} progressFilter
+ * @return {jQuery.Promise}
+ */
+jQuery.deferred.prototype.pipe =
+ function(doneFilter, failFilter, progressFilter) {};
+
+/**
+ * @param {jQueryCallback} progressCallbacks
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.progress = function(progressCallbacks) {};
+
+/**
+ * @param {Object=} target
+ * @return {jQuery.Promise}
+ */
+jQuery.deferred.prototype.promise = function(target) {};
+
+/**
+ * @param {...*} var_args
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.reject = function(var_args) {};
+
+/**
+ * @param {Object} context
+ * @param {Array.<*>=} args
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.rejectWith = function(context, args) {};
+
+/**
+ * @param {...*} var_args
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.resolve = function(var_args) {};
+
+/**
+ * @param {Object} context
+ * @param {Array.<*>=} args
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.resolveWith = function(context, args) {};
+
+/** @return {string} */
+jQuery.deferred.prototype.state = function() {};
+
+/**
+ * @override
+ * @param {jQueryCallback} doneCallbacks
+ * @param {jQueryCallback=} failCallbacks
+ * @param {jQueryCallback=} progressCallbacks
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.then
+ = function(doneCallbacks, failCallbacks, progressCallbacks) {};
+
+/**
+ * @param {number} duration
+ * @param {string=} queueName
+ * @return {!jQuery}
+ */
+jQuery.prototype.delay = function(duration, queueName) {};
+
+/**
+ * @param {string} selector
+ * @param {(string|Object.<string,*>)} arg2
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg3
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.delegate = function(selector, arg2, arg3, handler) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} queueName
+ */
+jQuery.dequeue = function(elem, queueName) {};
+
+/**
+ * @param {string=} queueName
+ * @return {!jQuery}
+ */
+jQuery.prototype.dequeue = function(queueName) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} queueName
+ */
+$.dequeue = function(elem, queueName) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ */
+jQuery.prototype.detach = function(selector) {};
+
+/**
+ * @param {Object} collection
+ * @param {function((number|string),?)} callback
+ * @return {Object}
+ */
+jQuery.each = function(collection, callback) {};
+
+/**
+ * @param {function(number,Element)} fnc
+ * @return {!jQuery}
+ */
+jQuery.prototype.each = function(fnc) {};
+
+/**
+ * @param {Object} collection
+ * @param {function((number|string),?)} callback
+ * @return {Object}
+ */
+$.each = function(collection, callback) {};
+
+/** @return {!jQuery} */
+jQuery.prototype.empty = function() {};
+
+/**
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.end = function() {};
+
+/**
+ * @param {number} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.eq = function(arg1) {};
+
+/** @param {string} message */
+jQuery.error = function(message) {};
+
+/**
+ * @deprecated Please use .on( "error", handler ) instead.
+ * @param {(function(!jQuery.event=)|Object.<string, *>)} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.error = function(arg1, handler) {};
+
+/** @param {string} message */
+$.error = function(message) {};
+
+/**
+ * @constructor
+ * @param {string} eventType
+ */
+jQuery.event = function(eventType) {};
+
+/**
+ * @constructor
+ * @extends {jQuery.event}
+ * @param {string} eventType
+ * @param {Object=} properties
+ * @return {jQuery.Event}
+ */
+jQuery.Event = function(eventType, properties) {};
+
+/**
+ * @constructor
+ * @extends {jQuery.event}
+ * @param {string} eventType
+ */
+$.event = function(eventType) {};
+
+/**
+ * @constructor
+ * @extends {jQuery.event}
+ * @param {string} eventType
+ * @param {Object=} properties
+ * @return {$.Event}
+ */
+$.Event = function(eventType, properties) {};
+
+/** @type {Element} */
+jQuery.event.prototype.currentTarget;
+
+/** @type {Object.<string, *>} */
+jQuery.event.prototype.data;
+
+/** @type {Element} */
+jQuery.event.prototype.delegateTarget;
+
+/**
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.event.prototype.isDefaultPrevented = function() {};
+
+/**
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.event.prototype.isImmediatePropagationStopped = function() {};
+
+/**
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.event.prototype.isPropagationStopped = function() {};
+
+/** @type {string} */
+jQuery.event.prototype.namespace;
+
+/** @type {Event} */
+jQuery.event.prototype.originalEvent;
+
+/** @type {number} */
+jQuery.event.prototype.pageX;
+
+/** @type {number} */
+jQuery.event.prototype.pageY;
+
+/** @return {undefined} */
+jQuery.event.prototype.preventDefault = function() {};
+
+/** @type {Object.<string, *>} */
+jQuery.event.prototype.props;
+
+/** @type {Element} */
+jQuery.event.prototype.relatedTarget;
+
+/** @type {*} */
+jQuery.event.prototype.result;
+
+/** @return {undefined} */
+jQuery.event.prototype.stopImmediatePropagation = function() {};
+
+/** @return {undefined} */
+jQuery.event.prototype.stopPropagation = function() {};
+
+/** @type {Element} */
+jQuery.event.prototype.target;
+
+/** @type {number} */
+jQuery.event.prototype.timeStamp;
+
+/** @type {string} */
+jQuery.event.prototype.type;
+
+/** @type {number} */
+jQuery.event.prototype.which;
+
+/**
+ * @param {(Object|boolean)} arg1
+ * @param {...*} var_args
+ * @return {Object}
+ */
+jQuery.extend = function(arg1, var_args) {};
+
+/**
+ * @param {(Object|boolean)} arg1
+ * @param {...*} var_args
+ * @return {Object}
+ */
+jQuery.prototype.extend = function(arg1, var_args) {};
+
+/**
+ * @param {(Object|boolean)} arg1
+ * @param {...*} var_args
+ * @return {Object}
+ */
+$.extend = function(arg1, var_args) {};
+
+/**
+ * @param {(string|number|function())=} duration
+ * @param {(function()|string)=} arg2
+ * @param {function()=} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.fadeIn = function(duration, arg2, callback) {};
+
+/**
+ * @param {(string|number|function())=} duration
+ * @param {(function()|string)=} arg2
+ * @param {function()=} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.fadeOut = function(duration, arg2, callback) {};
+
+/**
+ * @param {(string|number)} duration
+ * @param {number} opacity
+ * @param {(function()|string)=} arg3
+ * @param {function()=} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.fadeTo = function(duration, opacity, arg3, callback) {};
+
+/**
+ * @param {(string|number|function())=} duration
+ * @param {(string|function())=} easing
+ * @param {function()=} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.fadeToggle = function(duration, easing, callback) {};
+
+/**
+ * @param {(jQuerySelector|function(number)|Element|jQuery)} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.filter = function(arg1) {};
+
+/**
+ * @param {(jQuerySelector|jQuery|Element)} arg1
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.find = function(arg1) {};
+
+/** @return {!jQuery} */
+jQuery.prototype.first = function() {};
+
+/** @see http://docs.jquery.com/Plugins/Authoring */
+jQuery.fn = jQuery.prototype;
+
+/** @see http://docs.jquery.com/Plugins/Authoring */
+$.fn = $.prototype;
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.focus = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.focusin = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.focusout = function(arg1, handler) {};
+
+/** @const */
+jQuery.fx = {};
+
+/** @const */
+$.fx = {};
+
+/** @type {number} */
+jQuery.fx.interval;
+
+/** @type {number} */
+$.fx.interval;
+
+/** @type {boolean} */
+jQuery.fx.off;
+
+/** @type {boolean} */
+$.fx.off;
+
+/**
+ * @param {string} url
+ * @param {(Object.<string,*>|string|
+ * function(string,string,jQuery.jqXHR))=} data
+ * @param {(function(string,string,jQuery.jqXHR)|string)=} success
+ * @param {string=} dataType
+ * @return {jQuery.jqXHR}
+ */
+jQuery.get = function(url, data, success, dataType) {};
+
+/**
+ * @param {number=} index
+ * @return {(Element|Array.<Element>)}
+ * @nosideeffects
+ */
+jQuery.prototype.get = function(index) {};
+
+/**
+ * @param {string} url
+ * @param {(Object.<string,*>|string|
+ * function(string,string,jQuery.jqXHR))=} data
+ * @param {(function(string,string,jQuery.jqXHR)|string)=} success
+ * @param {string=} dataType
+ * @return {jQuery.jqXHR}
+ */
+$.get = function(url, data, success, dataType) {};
+
+/**
+ * @param {string} url
+ * @param {(Object.<string,*>|
+ * function(Object.<string,*>,string,jQuery.jqXHR))=} data
+ * @param {function(Object.<string,*>,string,jQuery.jqXHR)=} success
+ * @return {jQuery.jqXHR}
+ * @see http://api.jquery.com/jquery.getjson/#jQuery-getJSON-url-data-success
+ */
+jQuery.getJSON = function(url, data, success) {};
+
+/**
+ * @param {string} url
+ * @param {(Object.<string,*>|
+ * function(Object.<string,*>,string,jQuery.jqXHR))=} data
+ * @param {function(Object.<string,*>,string,jQuery.jqXHR)=} success
+ * @return {jQuery.jqXHR}
+ * @see http://api.jquery.com/jquery.getjson/#jQuery-getJSON-url-data-success
+ */
+$.getJSON = function(url, data, success) {};
+
+/**
+ * @param {string} url
+ * @param {function(Node,string,jQuery.jqXHR)=} success
+ * @return {jQuery.jqXHR}
+ */
+jQuery.getScript = function(url, success) {};
+
+/**
+ * @param {string} url
+ * @param {function(Node,string,jQuery.jqXHR)=} success
+ * @return {jQuery.jqXHR}
+ */
+$.getScript = function(url, success) {};
+
+/** @param {string} code */
+jQuery.globalEval = function(code) {};
+
+/** @param {string} code */
+$.globalEval = function(code) {};
+
+/**
+ * @param {Array.<*>} arr
+ * @param {function(*,number)} fnc
+ * @param {boolean=} invert
+ * @return {Array.<*>}
+ */
+jQuery.grep = function(arr, fnc, invert) {};
+
+/**
+ * @param {Array.<*>} arr
+ * @param {function(*,number)} fnc
+ * @param {boolean=} invert
+ * @return {Array.<*>}
+ */
+$.grep = function(arr, fnc, invert) {};
+
+/**
+ * @param {(string|Element)} arg1
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.has = function(arg1) {};
+
+/**
+ * @param {string} className
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.prototype.hasClass = function(className) {};
+
+/**
+ * @param {Element} elem
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.hasData = function(elem) {};
+
+/**
+ * @param {Element} elem
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.hasData = function(elem) {};
+
+/**
+ * @param {(string|number|function(number,number))=} arg1
+ * @return {(number|!jQuery)}
+ */
+jQuery.prototype.height = function(arg1) {};
+
+/**
+ * @param {(string|number|function())=} duration
+ * @param {(function()|string)=} arg2
+ * @param {function()=} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.hide = function(duration, arg2, callback) {};
+
+/** @param {boolean} hold */
+jQuery.holdReady = function(hold) {};
+
+/** @param {boolean} hold */
+$.holdReady = function(hold) {};
+
+/**
+ * @param {function(!jQuery.event=)} arg1
+ * @param {function(!jQuery.event=)=} handlerOut
+ * @return {!jQuery}
+ */
+jQuery.prototype.hover = function(arg1, handlerOut) {};
+
+/**
+ * @param {(string|function(number,string))=} arg1
+ * @return {(string|!jQuery)}
+ */
+jQuery.prototype.html = function(arg1) {};
+
+/**
+ * @param {*} value
+ * @param {Array.<*>} arr
+ * @param {number=} fromIndex
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.inArray = function(value, arr, fromIndex) {};
+
+/**
+ * @param {*} value
+ * @param {Array.<*>} arr
+ * @param {number=} fromIndex
+ * @return {number}
+ * @nosideeffects
+ */
+$.inArray = function(value, arr, fromIndex) {};
+
+/**
+ * @param {(jQuerySelector|Element|jQuery)=} arg1
+ * @return {number}
+ */
+jQuery.prototype.index = function(arg1) {};
+
+/**
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.prototype.innerHeight = function() {};
+
+/**
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.prototype.innerWidth = function() {};
+
+/**
+ * @param {(jQuerySelector|Element|jQuery)} target
+ * @return {!jQuery}
+ */
+jQuery.prototype.insertAfter = function(target) {};
+
+/**
+ * @param {(jQuerySelector|Element|jQuery)} target
+ * @return {!jQuery}
+ */
+jQuery.prototype.insertBefore = function(target) {};
+
+/**
+ * @param {(jQuerySelector|function(number)|jQuery|Element)} arg1
+ * @return {boolean}
+ */
+jQuery.prototype.is = function(arg1) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isArray = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isArray = function(obj) {};
+
+/**
+ * @param {Object} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isEmptyObject = function(obj) {};
+
+/**
+ * @param {Object} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isEmptyObject = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isFunction = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isFunction = function(obj) {};
+
+/**
+ * @param {*} value
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isNumeric = function(value) {};
+
+/**
+ * @param {*} value
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isNumeric = function(value) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isPlainObject = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isPlainObject = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isWindow = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isWindow = function(obj) {};
+
+/**
+ * @param {Element} node
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isXMLDoc = function(node) {};
+
+/**
+ * @param {Element} node
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isXMLDoc = function(node) {};
+
+/** @type {string} */
+jQuery.prototype.jquery;
+
+/**
+ * @constructor
+ * @extends {XMLHttpRequest}
+ * @implements {jQuery.Promise}
+ * @private
+ * @see http://api.jquery.com/jQuery.ajax/#jqXHR
+ */
+jQuery.jqXHR = function () {};
+
+/**
+ * @override
+ * @param {jQueryCallback} alwaysCallbacks
+ * @param {jQueryCallback=} alwaysCallbacks2
+ * @return {jQuery.jqXHR}
+ */
+jQuery.jqXHR.prototype.always =
+ function(alwaysCallbacks, alwaysCallbacks2) {};
+
+/**
+ * @deprecated
+ * @param {function()} callback
+ * @return {jQuery.jqXHR}
+*/
+jQuery.jqXHR.prototype.complete = function (callback) {};
+
+/**
+ * @override
+ * @param {jQueryCallback} doneCallbacks
+ * @return {jQuery.jqXHR}
+ */
+jQuery.jqXHR.prototype.done = function(doneCallbacks) {};
+
+/**
+ * @deprecated
+ * @param {function()} callback
+ * @return {jQuery.jqXHR}
+*/
+jQuery.jqXHR.prototype.error = function (callback) {};
+
+/**
+ * @override
+ * @param {jQueryCallback} failCallbacks
+ * @return {jQuery.jqXHR}
+ */
+jQuery.jqXHR.prototype.fail = function(failCallbacks) {};
+
+/**
+ * @deprecated
+ * @override
+ */
+jQuery.jqXHR.prototype.onreadystatechange = function (callback) {};
+
+/**
+ * @override
+ * @param {function()=} doneFilter
+ * @param {function()=} failFilter
+ * @param {function()=} progressFilter
+ * @return {jQuery.jqXHR}
+ */
+jQuery.jqXHR.prototype.pipe =
+ function(doneFilter, failFilter, progressFilter) {};
+
+/**
+ * @deprecated
+ * @param {function()} callback
+ * @return {jQuery.jqXHR}
+*/
+jQuery.jqXHR.prototype.success = function (callback) {};
+
+/**
+ * @override
+ * @param {jQueryCallback} doneCallbacks
+ * @param {jQueryCallback=} failCallbacks
+ * @param {jQueryCallback=} progressCallbacks
+ * @return {jQuery.jqXHR}
+ */
+jQuery.jqXHR.prototype.then =
+ function(doneCallbacks, failCallbacks, progressCallbacks) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.keydown = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.keypress = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.keyup = function(arg1, handler) {};
+
+/** @return {!jQuery} */
+jQuery.prototype.last = function() {};
+
+/** @type {number} */
+jQuery.prototype.length;
+
+/**
+ * @deprecated Please avoid the document loading Event invocation of
+ * .load() and use .on( "load", handler ) instead. (The AJAX
+ * module invocation signature is OK.)
+ * @param {(function(!jQuery.event=)|Object.<string, *>|string)} arg1
+ * @param {(function(!jQuery.event=)|Object.<string,*>|string)=} arg2
+ * @param {function(string,string,XMLHttpRequest)=} complete
+ * @return {!jQuery}
+ */
+jQuery.prototype.load = function(arg1, arg2, complete) {};
+
+/**
+ * @param {*} obj
+ * @return {Array.<*>}
+ */
+jQuery.makeArray = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {Array.<*>}
+ */
+$.makeArray = function(obj) {};
+
+/**
+ * @param {(Array.<*>|Object.<string, *>)} arg1
+ * @param {(function(*,number)|function(*,(string|number)))} callback
+ * @return {Array.<*>}
+ */
+jQuery.map = function(arg1, callback) {};
+
+/**
+ * @param {function(number,Element)} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.map = function(callback) {};
+
+/**
+ * @param {(Array.<*>|Object.<string, *>)} arg1
+ * @param {(function(*,number)|function(*,(string|number)))} callback
+ * @return {Array.<*>}
+ */
+$.map = function(arg1, callback) {};
+
+/**
+ * @param {Array.<*>} first
+ * @param {Array.<*>} second
+ * @return {Array.<*>}
+ */
+jQuery.merge = function(first, second) {};
+
+/**
+ * @param {Array.<*>} first
+ * @param {Array.<*>} second
+ * @return {Array.<*>}
+ */
+$.merge = function(first, second) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mousedown = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mouseenter = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mouseleave = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mousemove = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mouseout = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mouseover = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mouseup = function(arg1, handler) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.next = function(selector) {};
+
+/**
+ * @param {string=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.nextAll = function(selector) {};
+
+/**
+ * @param {(jQuerySelector|Element)=} arg1
+ * @param {jQuerySelector=} filter
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.nextUntil = function(arg1, filter) {};
+
+/**
+ * @param {boolean=} removeAll
+ * @return {Object}
+ */
+jQuery.noConflict = function(removeAll) {};
+
+/**
+ * @param {boolean=} removeAll
+ * @return {Object}
+ */
+$.noConflict = function(removeAll) {};
+
+/**
+ * @return {function()}
+ * @nosideeffects
+ */
+jQuery.noop = function() {};
+
+/**
+ * @return {function()}
+ * @nosideeffects
+ */
+$.noop = function() {};
+
+/**
+ * @param {(jQuerySelector|Array.<Element>|function(number)|jQuery)} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.not = function(arg1) {};
+
+/**
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.now = function() {};
+
+/**
+ * @return {number}
+ * @nosideeffects
+ */
+$.now = function() {};
+
+/**
+ * @param {(string|Object.<string,*>)=} arg1
+ * @param {(string|function(!jQuery.event=))=} selector
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.off = function(arg1, selector, handler) {};
+
+/**
+ * @param {({left:number,top:number}|
+ * function(number,{top:number,left:number}))=} arg1
+ * @return {({left:number,top:number}|!jQuery)}
+ */
+jQuery.prototype.offset = function(arg1) {};
+
+/**
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.offsetParent = function() {};
+
+/**
+ * @param {(string|Object.<string,*>)} arg1
+ * @param {*=} selector
+ * @param {*=} data
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.on = function(arg1, selector, data, handler) {};
+
+/**
+ * @param {(string|Object.<string,*>)} arg1
+ * @param {*=} arg2
+ * @param {*=} arg3
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.one = function(arg1, arg2, arg3, handler) {};
+
+/**
+ * @param {boolean=} includeMargin
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.prototype.outerHeight = function(includeMargin) {};
+
+/**
+ * @param {boolean=} includeMargin
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.prototype.outerWidth = function(includeMargin) {};
+
+/**
+ * @param {(Object.<string, *>|Array.<Object.<string, *>>)} obj
+ * @param {boolean=} traditional
+ * @return {string}
+ */
+jQuery.param = function(obj, traditional) {};
+
+/**
+ * @param {(Object.<string, *>|Array.<Object.<string, *>>)} obj
+ * @param {boolean=} traditional
+ * @return {string}
+ */
+$.param = function(obj, traditional) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.parent = function(selector) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.parents = function(selector) {};
+
+/**
+ * @param {(jQuerySelector|Element)=} arg1
+ * @param {jQuerySelector=} filter
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.parentsUntil = function(arg1, filter) {};
+
+/**
+ * @param {string} data
+ * @param {(Element|boolean)=} context
+ * @param {boolean=} keepScripts
+ * @return {Array.<Element>}
+ */
+jQuery.parseHTML = function(data, context, keepScripts) {};
+
+/**
+ * @param {string} data
+ * @param {(Element|boolean)=} context
+ * @param {boolean=} keepScripts
+ * @return {Array.<Element>}
+ */
+$.parseHTML = function(data, context, keepScripts) {};
+
+/**
+ * @param {string} json
+ * @return {string|number|Object.<string, *>|Array.<?>|boolean}
+ */
+jQuery.parseJSON = function(json) {};
+
+/**
+ * @param {string} json
+ * @return {Object.<string, *>}
+ */
+$.parseJSON = function(json) {};
+
+/**
+ * @param {string} data
+ * @return {Document}
+ */
+jQuery.parseXML = function(data) {};
+
+/**
+ * @param {string} data
+ * @return {Document}
+ */
+$.parseXML = function(data) {};
+
+/**
+ * @return {{left:number,top:number}}
+ * @nosideeffects
+ */
+jQuery.prototype.position = function() {};
+
+/**
+ * @param {string} url
+ * @param {(Object.<string,*>|string|
+ * function(string,string,jQuery.jqXHR))=} data
+ * @param {(function(string,string,jQuery.jqXHR)|string)=} success
+ * @param {string=} dataType
+ * @return {jQuery.jqXHR}
+ */
+jQuery.post = function(url, data, success, dataType) {};
+
+/**
+ * @param {string} url
+ * @param {(Object.<string,*>|string|
+ * function(string,string,jQuery.jqXHR))=} data
+ * @param {(function(string,string,jQuery.jqXHR)|string)=} success
+ * @param {string=} dataType
+ * @return {jQuery.jqXHR}
+ */
+$.post = function(url, data, success, dataType) {};
+
+/**
+ * @param {(string|Element|jQuery|function(number,string))} arg1
+ * @param {(string|Element|jQuery)=} content
+ * @return {!jQuery}
+ */
+jQuery.prototype.prepend = function(arg1, content) {};
+
+/**
+ * @param {(jQuerySelector|Element|jQuery)} target
+ * @return {!jQuery}
+ */
+jQuery.prototype.prependTo = function(target) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.prev = function(selector) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.prevAll = function(selector) {};
+
+/**
+ * @param {(jQuerySelector|Element)=} arg1
+ * @param {jQuerySelector=} filter
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.prevUntil = function(arg1, filter) {};
+
+/**
+ * @param {(string|Object)=} type
+ * @param {Object=} target
+ * @return {jQuery.Promise}
+ */
+jQuery.prototype.promise = function(type, target) {};
+
+/**
+ * @interface
+ * @private
+ * @see http://api.jquery.com/Types/#Promise
+ */
+jQuery.Promise = function () {};
+
+/**
+ * @param {jQueryCallback} alwaysCallbacks
+ * @param {jQueryCallback=} alwaysCallbacks2
+ * @return {jQuery.Promise}
+ */
+jQuery.Promise.prototype.always =
+ function(alwaysCallbacks, alwaysCallbacks2) {};
+
+/**
+ * @param {jQueryCallback} doneCallbacks
+ * @return {jQuery.Promise}
+ */
+jQuery.Promise.prototype.done = function(doneCallbacks) {};
+
+/**
+ * @param {jQueryCallback} failCallbacks
+ * @return {jQuery.Promise}
+ */
+jQuery.Promise.prototype.fail = function(failCallbacks) {};
+
+/**
+ * @param {function()=} doneFilter
+ * @param {function()=} failFilter
+ * @param {function()=} progressFilter
+ * @return {jQuery.Promise}
+ */
+jQuery.Promise.prototype.pipe =
+ function(doneFilter, failFilter, progressFilter) {};
+
+/**
+ * @param {jQueryCallback} doneCallbacks
+ * @param {jQueryCallback=} failCallbacks
+ * @param {jQueryCallback=} progressCallbacks
+ * @return {jQuery.Promise}
+ */
+jQuery.Promise.prototype.then =
+ function(doneCallbacks, failCallbacks, progressCallbacks) {};
+
+/**
+ * @param {(string|Object.<string,*>)} arg1
+ * @param {(string|number|boolean|function(number,String))=} arg2
+ * @return {(string|boolean|!jQuery)}
+ */
+jQuery.prototype.prop = function(arg1, arg2) {};
+
+/**
+ * @param {...*} var_args
+ * @return {function()}
+ */
+jQuery.proxy = function(var_args) {};
+
+/**
+ * @param {...*} var_args
+ * @return {function()}
+ */
+$.proxy = function(var_args) {};
+
+/**
+ * @param {Array.<Element>} elements
+ * @param {string=} name
+ * @param {Array.<*>=} args
+ * @return {!jQuery}
+ */
+jQuery.prototype.pushStack = function(elements, name, args) {};
+
+/**
+ * @param {(string|Array.<function()>|function(function()))=} queueName
+ * @param {(Array.<function()>|function(function()))=} arg2
+ * @return {(Array.<Element>|!jQuery)}
+ */
+jQuery.prototype.queue = function(queueName, arg2) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} queueName
+ * @param {(Array.<function()>|function())=} arg3
+ * @return {(Array.<Element>|!jQuery)}
+ */
+jQuery.queue = function(elem, queueName, arg3) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} queueName
+ * @param {(Array.<function()>|function())=} arg3
+ * @return {(Array.<Element>|!jQuery)}
+ */
+$.queue = function(elem, queueName, arg3) {};
+
+/**
+ * @param {function()} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ready = function(handler) {};
+
+/**
+ * @param {string=} selector
+ * @return {!jQuery}
+ */
+jQuery.prototype.remove = function(selector) {};
+
+/**
+ * @param {string} attributeName
+ * @return {!jQuery}
+ */
+jQuery.prototype.removeAttr = function(attributeName) {};
+
+/**
+ * @param {(string|function(number,string))=} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.removeClass = function(arg1) {};
+
+/**
+ * @param {(string|Array.<string>)=} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.removeData = function(arg1) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} name
+ * @return {!jQuery}
+ */
+jQuery.removeData = function(elem, name) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} name
+ * @return {!jQuery}
+ */
+$.removeData = function(elem, name) {};
+
+/**
+ * @param {string} propertyName
+ * @return {!jQuery}
+ */
+jQuery.prototype.removeProp = function(propertyName) {};
+
+/**
+ * @param {jQuerySelector} target
+ * @return {!jQuery}
+ */
+jQuery.prototype.replaceAll = function(target) {};
+
+/**
+ * @param {(string|Element|jQuery|function())} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.replaceWith = function(arg1) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.resize = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.scroll = function(arg1, handler) {};
+
+/**
+ * @param {number=} value
+ * @return {(number|!jQuery)}
+ */
+jQuery.prototype.scrollLeft = function(value) {};
+
+/**
+ * @param {number=} value
+ * @return {(number|!jQuery)}
+ */
+jQuery.prototype.scrollTop = function(value) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.select = function(arg1, handler) {};
+
+/**
+ * @return {string}
+ * @nosideeffects
+ */
+jQuery.prototype.serialize = function() {};
+
+/**
+ * @return {Array.<Object.<string, *>>}
+ * @nosideeffects
+ */
+jQuery.prototype.serializeArray = function() {};
+
+/**
+ * @param {(string|number|function())=} duration
+ * @param {(function()|string)=} arg2
+ * @param {function()=} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.show = function(duration, arg2, callback) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.siblings = function(selector) {};
+
+/**
+ * @deprecated Please use the .length property instead.
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.prototype.size = function() {};
+
+/**
+ * @param {number} start
+ * @param {number=} end
+ * @return {!jQuery}
+ */
+jQuery.prototype.slice = function(start, end) {};
+
+/**
+ * @param {(Object.<string,*>|string|number)=} optionsOrDuration
+ * @param {(function()|string)=} completeOrEasing
+ * @param {function()=} complete
+ * @return {!jQuery}
+ */
+jQuery.prototype.slideDown =
+ function(optionsOrDuration, completeOrEasing, complete) {};
+
+/**
+ * @param {(Object.<string,*>|string|number)=} optionsOrDuration
+ * @param {(function()|string)=} completeOrEasing
+ * @param {function()=} complete
+ * @return {!jQuery}
+ */
+jQuery.prototype.slideToggle =
+ function(optionsOrDuration, completeOrEasing, complete) {};
+
+/**
+ * @param {(Object.<string,*>|string|number)=} optionsOrDuration
+ * @param {(function()|string)=} completeOrEasing
+ * @param {function()=} complete
+ * @return {!jQuery}
+ */
+jQuery.prototype.slideUp =
+ function(optionsOrDuration, completeOrEasing, complete) {};
+
+/**
+ * @param {(boolean|string)=} arg1
+ * @param {boolean=} arg2
+ * @param {boolean=} jumpToEnd
+ * @return {!jQuery}
+ */
+jQuery.prototype.stop = function(arg1, arg2, jumpToEnd) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.submit = function(arg1, handler) {};
+
+/** @type {Object.<string, *>}
+ * @deprecated Please try to use feature detection instead.
+ */
+jQuery.support;
+
+/** @type {Object.<string, *>}
+ * @deprecated Please try to use feature detection instead.
+ */
+$.support;
+
+/**
+ * @deprecated Please try to use feature detection instead.
+ * @type {boolean}
+ */
+jQuery.support.boxModel;
+
+/**
+ * @deprecated Please try to use feature detection instead.
+ * @type {boolean}
+ */
+$.support.boxModel;
+
+/** @type {boolean} */
+jQuery.support.changeBubbles;
+
+/** @type {boolean} */
+$.support.changeBubbles;
+
+/** @type {boolean} */
+jQuery.support.cors;
+
+/** @type {boolean} */
+$.support.cors;
+
+/** @type {boolean} */
+jQuery.support.cssFloat;
+
+/** @type {boolean} */
+$.support.cssFloat;
+
+/** @type {boolean} */
+jQuery.support.hrefNormalized;
+
+/** @type {boolean} */
+$.support.hrefNormalized;
+
+/** @type {boolean} */
+jQuery.support.htmlSerialize;
+
+/** @type {boolean} */
+$.support.htmlSerialize;
+
+/** @type {boolean} */
+jQuery.support.leadingWhitespace;
+
+/** @type {boolean} */
+$.support.leadingWhitespace;
+
+/** @type {boolean} */
+jQuery.support.noCloneEvent;
+
+/** @type {boolean} */
+$.support.noCloneEvent;
+
+/** @type {boolean} */
+jQuery.support.opacity;
+
+/** @type {boolean} */
+$.support.opacity;
+
+/** @type {boolean} */
+jQuery.support.style;
+
+/** @type {boolean} */
+$.support.style;
+
+/** @type {boolean} */
+jQuery.support.submitBubbles;
+
+/** @type {boolean} */
+$.support.submitBubbles;
+
+/** @type {boolean} */
+jQuery.support.tbody;
+
+/** @type {boolean} */
+$.support.tbody;
+
+/**
+ * @param {(string|function(number,string))=} arg1
+ * @return {(string|!jQuery)}
+ */
+jQuery.prototype.text = function(arg1) {};
+
+/**
+ * @return {Array.<Element>}
+ * @nosideeffects
+ */
+jQuery.prototype.toArray = function() {};
+
+/**
+ * Refers to the method from the Effects category. There used to be a toggle
+ * method on the Events category which was removed starting version 1.9.
+ * @param {(number|string|Object.<string,*>|boolean)=} arg1
+ * @param {(function()|string)=} arg2
+ * @param {function()=} arg3
+ * @return {!jQuery}
+ */
+jQuery.prototype.toggle = function(arg1, arg2, arg3) {};
+
+/**
+ * @param {(string|boolean|function(number,string,boolean))=} arg1
+ * @param {boolean=} flag
+ * @return {!jQuery}
+ */
+jQuery.prototype.toggleClass = function(arg1, flag) {};
+
+/**
+ * @param {(string|jQuery.event)} arg1
+ * @param {...*} var_args
+ * @return {!jQuery}
+ */
+jQuery.prototype.trigger = function(arg1, var_args) {};
+
+/**
+ * @param {string|jQuery.event} eventType
+ * @param {Array.<*>=} extraParameters
+ * @return {*}
+ */
+jQuery.prototype.triggerHandler = function(eventType, extraParameters) {};
+
+/**
+ * @param {string} str
+ * @return {string}
+ * @nosideeffects
+ */
+jQuery.trim = function(str) {};
+
+/**
+ * @param {string} str
+ * @return {string}
+ * @nosideeffects
+ */
+$.trim = function(str) {};
+
+/**
+ * @param {*} obj
+ * @return {string}
+ * @nosideeffects
+ */
+jQuery.type = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {string}
+ * @nosideeffects
+ */
+$.type = function(obj) {};
+
+/**
+ * @param {(string|function(!jQuery.event=)|jQuery.event)=} arg1
+ * @param {(function(!jQuery.event=)|boolean)=} arg2
+ * @return {!jQuery}
+ */
+jQuery.prototype.unbind = function(arg1, arg2) {};
+
+/**
+ * @param {string=} arg1
+ * @param {(string|Object.<string,*>)=} arg2
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.undelegate = function(arg1, arg2, handler) {};
+
+/**
+ * @param {Array.<Element>} arr
+ * @return {Array.<Element>}
+ */
+jQuery.unique = function(arr) {};
+
+/**
+ * @param {Array.<Element>} arr
+ * @return {Array.<Element>}
+ */
+$.unique = function(arr) {};
+
+/**
+ * @deprecated Please use .on( "unload", handler ) instead.
+ * @param {(function(!jQuery.event=)|Object.<string, *>)} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.unload = function(arg1, handler) {};
+
+/** @return {!jQuery} */
+jQuery.prototype.unwrap = function() {};
+
+/**
+ * @param {(string|Array.<string>|function(number,*))=} arg1
+ * @return {(string|number|Array.<string>|!jQuery)}
+ */
+jQuery.prototype.val = function(arg1) {};
+
+/**
+ * Note: The official documentation (https://api.jquery.com/jQuery.when/) says
+ * jQuery.when accepts deferreds, but it actually accepts any type, e.g.:
+ *
+ * jQuery.when(jQuery.ready, jQuery.ajax(''), jQuery('#my-element'), 1)
+ *
+ * If an argument is not an "observable" (a promise-like object) it is wrapped
+ * into a promise.
+ * @param {*} deferred
+ * @param {...*} deferreds
+ * @return {jQuery.Promise}
+ */
+jQuery.when = function(deferred, deferreds) {};
+
+/**
+ * Note: See jQuery.when().
+ * @param {*} deferred
+ * @param {...*} deferreds
+ * @return {jQuery.Promise}
+ */
+$.when = function(deferred, deferreds) {};
+
+/**
+ * @param {(string|number|function(number,number))=} arg1
+ * @return {(number|!jQuery)}
+ */
+jQuery.prototype.width = function(arg1) {};
+
+/**
+ * @param {(string|jQuerySelector|Element|jQuery|function(number))} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.wrap = function(arg1) {};
+
+/**
+ * @param {(string|jQuerySelector|Element|jQuery)} wrappingElement
+ * @return {!jQuery}
+ */
+jQuery.prototype.wrapAll = function(wrappingElement) {};
+
+/**
+ * @param {(string|jQuerySelector|Element|jQuery|function(number))} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.wrapInner = function(arg1) {};
diff --git a/js/hover.js b/js/hover.js
deleted file mode 100644
index 50ba777681..0000000000
--- a/js/hover.js
+++ /dev/null
@@ -1,4 +0,0 @@
-$(document).on('mq4hsChange', function (e) {
- 'use strict';
- $(document.documentElement).toggleClass('bs-true-hover', e.trueHover);
-});
diff --git a/js/modal.js b/js/modal.js
index e7704b8fc4..fb4cd03296 100644
--- a/js/modal.js
+++ b/js/modal.js
@@ -1,324 +1,598 @@
-/* ========================================================================
- * Bootstrap: modal.js v3.3.2
- * http://getbootstrap.com/javascript/#modals
+/** =======================================================================
+ * Bootstrap: modal.js v4.0.0
+ * http://getbootstrap.com/javascript/#modal
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @fileoverview - Bootstrap's modal plugin. Modals are streamlined, but
+ * flexible, dialog prompts with the minimum required functionality and
+ * smart defaults.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.modal
+ * + $.modal.noConflict
+ * + $.modal.Constructor
+ * + $.modal.Constructor.VERSION
+ * + $.modal.Constructor.Defaults
+ * + $.modal.Constructor.Defaults.backdrop
+ * + $.modal.Constructor.Defaults.keyboard
+ * + $.modal.Constructor.Defaults.show
+ * + $.modal.Constructor.prototype.toggle
+ * + $.modal.Constructor.prototype.show
+ * + $.modal.Constructor.prototype.hide
+ *
+ * ========================================================================
+ */
+'use strict';
-+function ($) {
- 'use strict';
- // MODAL CLASS DEFINITION
- // ======================
+/**
+ * Our modal class.
+ * @param {Element} element
+ * @param {Object} config
+ * @constructor
+ */
+var Modal = function (element, config) {
- var Modal = function (element, options) {
- this.options = options
- this.$body = $(document.body)
- this.$element = $(element)
- this.$backdrop =
- this.isShown = null
- this.scrollbarWidth = 0
+ /** @private {Object} */
+ this._config = config
+
+ /** @private {Element} */
+ this._element = element
- if (this.options.remote) {
- this.$element
- .find('.modal-content')
- .load(this.options.remote, $.proxy(function () {
- this.$element.trigger('loaded.bs.modal')
- }, this))
- }
- }
+ /** @private {Element} */
+ this._backdrop = null
- Modal.VERSION = '3.3.2'
+ /** @private {boolean} */
+ this._isShown = false
+
+ /** @private {boolean} */
+ this._isBodyOverflowing = false
+
+ /** @private {number} */
+ this._scrollbarWidth = 0
- Modal.TRANSITION_DURATION = 300
- Modal.BACKDROP_TRANSITION_DURATION = 150
+}
- Modal.DEFAULTS = {
- backdrop: true,
- keyboard: true,
- show: true
- }
- Modal.prototype.toggle = function (_relatedTarget) {
- return this.isShown ? this.hide() : this.show(_relatedTarget)
- }
+/**
+ * @const
+ * @type {string}
+ */
+Modal['VERSION'] = '4.0.0'
+
+
+/**
+ * @const
+ * @type {Object}
+ */
+Modal['Defaults'] = {
+ 'backdrop' : true,
+ 'keyboard' : true,
+ 'show' : true
+}
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Modal._NAME = 'modal'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Modal._DATA_KEY = 'bs.modal'
+
+
+/**
+ * @const
+ * @type {number}
+ * @private
+ */
+Modal._TRANSITION_DURATION = 300
+
+
+/**
+ * @const
+ * @type {number}
+ * @private
+ */
+Modal._BACKDROP_TRANSITION_DURATION = 150
+
+
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+Modal._JQUERY_NO_CONFLICT = $.fn[Modal._NAME]
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Modal._Event = {
+ HIDE : 'hide.bs.modal',
+ HIDDEN : 'hidden.bs.modal',
+ SHOW : 'show.bs.modal',
+ SHOWN : 'shown.bs.modal'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Modal._ClassName = {
+ BACKDROP : 'modal-backdrop',
+ OPEN : 'modal-open',
+ FADE : 'fade',
+ IN : 'in'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Modal._Selector = {
+ DIALOG : '.modal-dialog',
+ DATA_TOGGLE : '[data-toggle="modal"]',
+ DATA_DISMISS : '[data-dismiss="modal"]',
+ SCROLLBAR_MEASURER : 'modal-scrollbar-measure'
+}
+
+
+
+/**
+ * Provides the jQuery Interface for the alert component.
+ * @param {Object|string=} opt_config
+ * @param {Element=} opt_relatedTarget
+ * @this {jQuery}
+ * @return {jQuery}
+ * @private
+ */
+Modal._jQueryInterface = function Plugin(opt_config, opt_relatedTarget) {
+ return this.each(function () {
+ var data = $(this).data(Modal._DATA_KEY)
+ var config = $.extend({}, Modal['Defaults'], $(this).data(), typeof opt_config == 'object' && opt_config)
+
+ if (!data) {
+ data = new Modal(this, config)
+ $(this).data(Modal._DATA_KEY, data)
+ }
- Modal.prototype.show = function (_relatedTarget) {
- var that = this
- var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
+ if (typeof opt_config == 'string') {
+ data[opt_config](opt_relatedTarget)
- this.$element.trigger(e)
+ } else if (config['show']) {
+ data['show'](opt_relatedTarget)
+ }
+ })
+}
- if (this.isShown || e.isDefaultPrevented()) return
- this.isShown = true
+/**
+ * @param {Element} relatedTarget
+ */
+Modal.prototype['toggle'] = function (relatedTarget) {
+ return this._isShown ? this['hide']() : this['show'](relatedTarget)
+}
- this.checkScrollbar()
- this.setScrollbar()
- this.$body.addClass('modal-open')
- this.escape()
- this.resize()
+/**
+ * @param {Element} relatedTarget
+ */
+Modal.prototype['show'] = function (relatedTarget) {
+ var showEvent = $.Event(Modal._Event.SHOW, { relatedTarget: relatedTarget })
- this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
+ $(this._element).trigger(showEvent)
- this.backdrop(function () {
- var transition = $.support.transition && that.$element.hasClass('fade')
+ if (this._isShown || showEvent.isDefaultPrevented()) {
+ return
+ }
- if (!that.$element.parent().length) {
- that.$element.appendTo(that.$body) // don't move modals dom position
- }
+ this._isShown = true
- that.$element
- .show()
- .scrollTop(0)
+ this._checkScrollbar()
+ this._setScrollbar()
- if (that.options.backdrop) that.adjustBackdrop()
- that.adjustDialog()
+ $(document.body).addClass(Modal._ClassName.OPEN)
- if (transition) {
- that.$element[0].offsetWidth // force reflow
- }
+ this._escape()
+ this._resize()
- that.$element
- .addClass('in')
- .attr('aria-hidden', false)
+ $(this._element).on('click.dismiss.bs.modal', Modal._Selector.DATA_DISMISS, this['hide'].bind(this))
- that.enforceFocus()
+ this._showBackdrop(this._showElement.bind(this, relatedTarget))
+}
- var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
- transition ?
- that.$element.find('.modal-dialog') // wait for modal to slide in
- .one('bsTransitionEnd', function () {
- that.$element.trigger('focus').trigger(e)
- })
- .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
- that.$element.trigger('focus').trigger(e)
- })
+/**
+ * @param {Event} event
+ */
+Modal.prototype['hide'] = function (event) {
+ if (event) {
+ event.preventDefault()
}
- Modal.prototype.hide = function (e) {
- if (e) e.preventDefault()
+ var hideEvent = $.Event(Modal._Event.HIDE)
- e = $.Event('hide.bs.modal')
+ $(this._element).trigger(hideEvent)
- this.$element.trigger(e)
+ if (!this._isShown || hideEvent.isDefaultPrevented()) {
+ return
+ }
- if (!this.isShown || e.isDefaultPrevented()) return
+ this._isShown = false
- this.isShown = false
+ this._escape()
+ this._resize()
- this.escape()
- this.resize()
+ $(document).off('focusin.bs.modal')
- $(document).off('focusin.bs.modal')
+ $(this._element).removeClass(Modal._ClassName.IN)
+ this._element.setAttribute('aria-hidden', true)
- this.$element
- .removeClass('in')
- .attr('aria-hidden', true)
- .off('click.dismiss.bs.modal')
+ $(this._element).off('click.dismiss.bs.modal')
- $.support.transition && this.$element.hasClass('fade') ?
- this.$element
- .one('bsTransitionEnd', $.proxy(this.hideModal, this))
- .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
- this.hideModal()
+ if (Bootstrap.transition && $(this._element).hasClass(Modal._ClassName.FADE)) {
+ $(this._element)
+ .one(Bootstrap.TRANSITION_END, this._hideModal.bind(this))
+ .emulateTransitionEnd(Modal._TRANSITION_DURATION)
+ } else {
+ this._hideModal()
}
+}
- Modal.prototype.enforceFocus = function () {
- $(document)
- .off('focusin.bs.modal') // guard against infinite focus loop
- .on('focusin.bs.modal', $.proxy(function (e) {
- if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
- this.$element.trigger('focus')
- }
- }, this))
- }
- Modal.prototype.escape = function () {
- if (this.isShown && this.options.keyboard) {
- this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
- e.which == 27 && this.hide()
- }, this))
- } else if (!this.isShown) {
- this.$element.off('keydown.dismiss.bs.modal')
- }
- }
+/**
+ * @param {Element} relatedTarget
+ * @private
+ */
+Modal.prototype._showElement = function (relatedTarget) {
+ var transition = Bootstrap.transition && $(this._element).hasClass(Modal._ClassName.FADE)
- Modal.prototype.resize = function () {
- if (this.isShown) {
- $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
- } else {
- $(window).off('resize.bs.modal')
- }
+ if (!this._element.parentNode || this._element.parentNode.nodeType != Node.ELEMENT_NODE) {
+ document.body.appendChild(this._element) // don't move modals dom position
}
- Modal.prototype.hideModal = function () {
- var that = this
- this.$element.hide()
- this.backdrop(function () {
- that.$body.removeClass('modal-open')
- that.resetAdjustments()
- that.resetScrollbar()
- that.$element.trigger('hidden.bs.modal')
- })
- }
+ this._element.style.display = 'block'
+ this._element.scrollTop = 0
- Modal.prototype.removeBackdrop = function () {
- this.$backdrop && this.$backdrop.remove()
- this.$backdrop = null
+ if (this._config['backdrop']) {
+ this._adjustBackdrop()
}
- Modal.prototype.backdrop = function (callback) {
- var that = this
- var animate = this.$element.hasClass('fade') ? 'fade' : ''
+ if (transition) {
+ Bootstrap.reflow(this._element)
+ }
- if (this.isShown && this.options.backdrop) {
- var doAnimate = $.support.transition && animate
+ $(this._element).addClass(Modal._ClassName.IN)
+ this._element.setAttribute('aria-hidden', false)
- this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
- .prependTo(this.$element)
- .on('click.dismiss.bs.modal', $.proxy(function (e) {
- if (e.target !== e.currentTarget) return
- this.options.backdrop == 'static'
- ? this.$element[0].focus.call(this.$element[0])
- : this.hide.call(this)
- }, this))
+ this._enforceFocus()
- if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
+ var shownEvent = $.Event(Modal._Event.SHOWN, { relatedTarget: relatedTarget })
- this.$backdrop.addClass('in')
+ var transitionComplete = function () {
+ this._element.focus()
+ $(this._element).trigger(shownEvent)
+ }.bind(this)
- if (!callback) return
+ if (transition) {
+ var dialog = $(this._element).find(Modal._Selector.DIALOG)[0]
+ $(dialog)
+ .one(Bootstrap.TRANSITION_END, transitionComplete)
+ .emulateTransitionEnd(Modal._TRANSITION_DURATION)
+ } else {
+ transitionComplete()
+ }
+}
- doAnimate ?
- this.$backdrop
- .one('bsTransitionEnd', callback)
- .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
- callback()
- } else if (!this.isShown && this.$backdrop) {
- this.$backdrop.removeClass('in')
- var callbackRemove = function () {
- that.removeBackdrop()
- callback && callback()
+/**
+ * @private
+ */
+Modal.prototype._enforceFocus = function () {
+ $(document)
+ .off('focusin.bs.modal') // guard against infinite focus loop
+ .on('focusin.bs.modal', function (e) {
+ if (this._element !== e.target && !$(this._element).has(e.target).length) {
+ this._element.focus()
}
- $.support.transition && this.$element.hasClass('fade') ?
- this.$backdrop
- .one('bsTransitionEnd', callbackRemove)
- .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
- callbackRemove()
+ }.bind(this))
+}
+
+
+/**
+ * @private
+ */
+Modal.prototype._escape = function () {
+ if (this._isShown && this._config['keyboard']) {
+ $(this._element).on('keydown.dismiss.bs.modal', function (event) {
+ if (event.which === 27) {
+ this['hide']()
+ }
+ }.bind(this))
- } else if (callback) {
- callback()
- }
+ } else if (!this._isShown) {
+ $(this._element).off('keydown.dismiss.bs.modal')
}
+}
- // these following methods are used to handle overflowing modals
- Modal.prototype.handleUpdate = function () {
- if (this.options.backdrop) this.adjustBackdrop()
- this.adjustDialog()
+/**
+ * @private
+ */
+Modal.prototype._resize = function () {
+ if (this._isShown) {
+ $(window).on('resize.bs.modal', this._handleUpdate.bind(this))
+ } else {
+ $(window).off('resize.bs.modal')
}
-
- Modal.prototype.adjustBackdrop = function () {
- this.$backdrop
- .css('height', 0)
- .css('height', this.$element[0].scrollHeight)
+}
+
+
+/**
+ * @private
+ */
+Modal.prototype._hideModal = function () {
+ this._element.style.display = 'none'
+ this._showBackdrop(function () {
+ $(document.body).removeClass(Modal._ClassName.OPEN)
+ this._resetAdjustments()
+ this._resetScrollbar()
+ $(this._element).trigger(Modal._Event.HIDDEN)
+ }.bind(this))
+}
+
+
+/**
+ * @private
+ */
+Modal.prototype._removeBackdrop = function () {
+ if (this._backdrop) {
+ this._backdrop.parentNode.removeChild(this._backdrop)
+ this._backdrop = null
}
+}
- Modal.prototype.adjustDialog = function () {
- var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
- this.$element.css({
- paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
- paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
- })
- }
+/**
+ * @param {Function} callback
+ * @private
+ */
+Modal.prototype._showBackdrop = function (callback) {
+ var animate = $(this._element).hasClass(Modal._ClassName.FADE) ? Modal._ClassName.FADE : ''
- Modal.prototype.resetAdjustments = function () {
- this.$element.css({
- paddingLeft: '',
- paddingRight: ''
- })
- }
+ if (this._isShown && this._config['backdrop']) {
+ var doAnimate = Bootstrap.transition && animate
- Modal.prototype.checkScrollbar = function () {
- this.bodyIsOverflowing = document.body.scrollHeight > document.documentElement.clientHeight
- this.scrollbarWidth = this.measureScrollbar()
- }
+ this._backdrop = document.createElement('div')
+ this._backdrop.className = Modal._ClassName.BACKDROP
- Modal.prototype.setScrollbar = function () {
- var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
- if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
- }
+ if (animate) {
+ $(this._backdrop).addClass(animate)
+ }
- Modal.prototype.resetScrollbar = function () {
- this.$body.css('padding-right', '')
- }
+ $(this._element).prepend(this._backdrop)
- Modal.prototype.measureScrollbar = function () { // thx walsh
- var scrollDiv = document.createElement('div')
- scrollDiv.className = 'modal-scrollbar-measure'
- this.$body.append(scrollDiv)
- var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
- this.$body[0].removeChild(scrollDiv)
- return scrollbarWidth
- }
+ $(this._backdrop).on('click.dismiss.bs.modal', function (event) {
+ if (event.target !== event.currentTarget) return
+ this._config['backdrop'] === 'static'
+ ? this._element.focus()
+ : this['hide']()
+ }.bind(this))
+ if (doAnimate) {
+ Bootstrap.reflow(this._backdrop)
+ }
- // MODAL PLUGIN DEFINITION
- // =======================
+ $(this._backdrop).addClass(Modal._ClassName.IN)
- function Plugin(option, _relatedTarget) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.modal')
- var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
+ if (!callback) {
+ return
+ }
- if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
- if (typeof option == 'string') data[option](_relatedTarget)
- else if (options.show) data.show(_relatedTarget)
- })
- }
+ if (!doAnimate) {
+ callback()
+ return
+ }
- var old = $.fn.modal
+ $(this._backdrop)
+ .one(Bootstrap.TRANSITION_END, callback)
+ .emulateTransitionEnd(Modal._BACKDROP_TRANSITION_DURATION)
- $.fn.modal = Plugin
- $.fn.modal.Constructor = Modal
+ } else if (!this._isShown && this._backdrop) {
+ $(this._backdrop).removeClass(Modal._ClassName.IN)
+ var callbackRemove = function () {
+ this._removeBackdrop()
+ if (callback) {
+ callback()
+ }
+ }.bind(this)
+
+ if (Bootstrap.transition && $(this._element).hasClass(Modal._ClassName.FADE)) {
+ $(this._backdrop)
+ .one(Bootstrap.TRANSITION_END, callbackRemove)
+ .emulateTransitionEnd(Modal._BACKDROP_TRANSITION_DURATION)
+ } else {
+ callbackRemove()
+ }
- // MODAL NO CONFLICT
- // =================
+ } else if (callback) {
+ callback()
+ }
+}
+
+
+/**
+ * ------------------------------------------------------------------------
+ * the following methods are used to handle overflowing modals
+ * todo (fat): these should probably be refactored into a
+ * ------------------------------------------------------------------------
+ */
+
+
+/**
+ * @private
+ */
+Modal.prototype._handleUpdate = function () {
+ if (this._config['backdrop']) this._adjustBackdrop()
+ this._adjustDialog()
+}
+
+/**
+ * @private
+ */
+Modal.prototype._adjustBackdrop = function () {
+ this._backdrop.style.height = 0 // todo (fat): no clue why we do this
+ this._backdrop.style.height = this._element.scrollHeight + 'px'
+}
+
+
+/**
+ * @private
+ */
+Modal.prototype._adjustDialog = function () {
+ var isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight
+
+ if (!this._isBodyOverflowing && isModalOverflowing) {
+ this._element.style.paddingLeft = this._scrollbarWidth + 'px'
+ }
- $.fn.modal.noConflict = function () {
- $.fn.modal = old
- return this
+ if (this._isBodyOverflowing && !isModalOverflowing) {
+ this._element.style.paddingRight = this._scrollbarWidth + 'px'
}
+}
- // MODAL DATA-API
- // ==============
+/**
+ * @private
+ */
+Modal.prototype._resetAdjustments = function () {
+ this._element.style.paddingLeft = ''
+ this._element.style.paddingRight = ''
+}
- $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
- var $this = $(this)
- var href = $this.attr('href')
- var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
- var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
- if ($this.is('a')) e.preventDefault()
+/**
+ * @private
+ */
+Modal.prototype._checkScrollbar = function () {
+ this._isBodyOverflowing = document.body.scrollHeight > document.documentElement.clientHeight
+ this._scrollbarWidth = this._getScrollbarWidth()
+}
- $target.one('show.bs.modal', function (showEvent) {
- if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
- $target.one('hidden.bs.modal', function () {
- $this.is(':visible') && $this.trigger('focus')
- })
- })
- Plugin.call($target, option, this)
- })
-}(jQuery);
+/**
+ * @private
+ */
+Modal.prototype._setScrollbar = function () {
+ var bodyPadding = parseInt(($(document.body).css('padding-right') || 0), 10)
+
+ if (this._isBodyOverflowing) {
+ document.body.style.paddingRight = bodyPadding + this._scrollbarWidth + 'px'
+ }
+}
+
+
+/**
+ * @private
+ */
+Modal.prototype._resetScrollbar = function () {
+ document.body.style.paddingRight = ''
+}
+
+
+/**
+ * @private
+ */
+Modal.prototype._getScrollbarWidth = function () { // thx walsh
+ var scrollDiv = document.createElement('div')
+ scrollDiv.className = Modal._Selector.SCROLLBAR_MEASURER
+ document.body.appendChild(scrollDiv)
+ var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
+ document.body.removeChild(scrollDiv)
+ return scrollbarWidth
+}
+
+
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Modal._NAME] = Modal._jQueryInterface
+
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Modal._NAME]['Constructor'] = Modal
+
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Modal._NAME]['noConflict'] = function () {
+ $.fn[Modal._NAME] = Modal._JQUERY_NO_CONFLICT
+ return this
+}
+
+
+/**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+$(document).on('click.bs.modal.data-api', Modal._Selector.DATA_TOGGLE, function (event) {
+ var selector = Bootstrap.getSelectorFromElement(this)
+
+ if (selector) {
+ var target = $(selector)[0]
+ }
+
+ var config = $(target).data(Modal._DATA_KEY) ? 'toggle' : $.extend({}, $(target).data(), $(this).data())
+
+ if (this.tagName == 'A') {
+ event.preventDefault()
+ }
+
+ var $target = $(target).one(Modal._Event.SHOW, function (showEvent) {
+ if (showEvent.isDefaultPrevented()) {
+ return // only register focus restorer if modal will actually get shown
+ }
+
+ $target.one(Modal._Event.HIDDEN, function () {
+ if ($(this).is(':visible')) {
+ this.focus()
+ }
+ }.bind(this))
+ }.bind(this))
+
+ Modal._jQueryInterface.call($(target), config, this)
+})
diff --git a/js/popover.js b/js/popover.js
index 02476ebac9..5663355516 100644
--- a/js/popover.js
+++ b/js/popover.js
@@ -1,113 +1,283 @@
-/* ========================================================================
- * Bootstrap: popover.js v3.3.2
+/** =======================================================================
+ * Bootstrap: popover.js v4.0.0
* http://getbootstrap.com/javascript/#popovers
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @fileoverview - Bootstrap's popover plugin - extends tooltip.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.popover
+ * + $.popover.noConflict
+ * + $.popover.Constructor
+ * + $.popover.Constructor.VERSION
+ * + $.popover.Constructor.Defaults
+ * + $.popover.Constructor.Defaults.container
+ * + $.popover.Constructor.Defaults.animation
+ * + $.popover.Constructor.Defaults.placement
+ * + $.popover.Constructor.Defaults.selector
+ * + $.popover.Constructor.Defaults.template
+ * + $.popover.Constructor.Defaults.trigger
+ * + $.popover.Constructor.Defaults.title
+ * + $.popover.Constructor.Defaults.content
+ * + $.popover.Constructor.Defaults.delay
+ * + $.popover.Constructor.Defaults.html
+ * + $.popover.Constructor.Defaults.viewport
+ * + $.popover.Constructor.Defaults.viewport.selector
+ * + $.popover.Constructor.Defaults.viewport.padding
+ * + $.popover.Constructor.prototype.enable
+ * + $.popover.Constructor.prototype.disable
+ * + $.popover.Constructor.prototype.destroy
+ * + $.popover.Constructor.prototype.toggleEnabled
+ * + $.popover.Constructor.prototype.toggle
+ * + $.popover.Constructor.prototype.show
+ * + $.popover.Constructor.prototype.hide
+ *
+ * ========================================================================
+ */
-+function ($) {
- 'use strict';
+'use strict';
- // POPOVER PUBLIC CLASS DEFINITION
- // ===============================
- var Popover = function (element, options) {
- this.init('popover', element, options)
- }
+if (!Tooltip) throw new Error('Popover requires tooltip.js')
+
+
+/**
+ * Our tooltip class.
+ * @param {Element!} element
+ * @param {Object=} opt_config
+ * @constructor
+ * @extends {Tooltip}
+ */
+var Popover = function (element, opt_config) {
+ Tooltip.apply(this, arguments)
+}
+Bootstrap.inherits(Popover, Tooltip)
+
+
+/**
+ * @const
+ * @type {string}
+ */
+Popover['VERSION'] = '4.0.0'
+
+
+/**
+ * @const
+ * @type {Object}
+ */
+Popover['Defaults'] = $.extend({}, $.fn['tooltip']['Constructor']['Defaults'], {
+ 'placement': 'right',
+ 'trigger': 'click',
+ 'content': '',
+ 'template': '<div class="popover" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
+})
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Popover._NAME = 'popover'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Popover._DATA_KEY = 'bs.popover'
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Popover._Event = {
+ HIDE : 'hide.bs.popover',
+ HIDDEN : 'hidden.bs.popover',
+ SHOW : 'show.bs.popover',
+ SHOWN : 'shown.bs.popover'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Popover._ClassName = {
+ FADE : 'fade',
+ IN : 'in'
+}
- if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
- Popover.VERSION = '3.3.2'
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Popover._Selector = {
+ TITLE : '.popover-title',
+ CONTENT : '.popover-content',
+ ARROW : '.popover-arrow'
+}
- Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
- placement: 'right',
- trigger: 'click',
- content: '',
- template: '<div class="popover" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
+
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+Popover._JQUERY_NO_CONFLICT = $.fn[Popover._NAME]
+
+
+/**
+ * @param {Object|string=} opt_config
+ * @this {jQuery}
+ * @return {jQuery}
+ * @private
+ */
+Popover._jQueryInterface = function (opt_config) {
+ return this.each(function () {
+ var data = $(this).data(Popover._DATA_KEY)
+ var config = typeof opt_config === 'object' ? opt_config : null
+
+ if (!data && opt_config === 'destroy') {
+ return
+ }
+
+ if (!data) {
+ data = new Popover(this, config)
+ $(this).data(Popover._DATA_KEY, data)
+ }
+
+ if (typeof opt_config === 'string') {
+ data[opt_config]()
+ }
})
+}
- // NOTE: POPOVER EXTENDS tooltip.js
- // ================================
+/**
+ * @return {string}
+ * @protected
+ */
+Popover.prototype.getName = function () {
+ return Popover._NAME
+}
- Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
- Popover.prototype.constructor = Popover
+/**
+ * @override
+ */
+Popover.prototype.getDataKey = function () {
+ return Popover._DATA_KEY
+}
- Popover.prototype.getDefaults = function () {
- return Popover.DEFAULTS
- }
- Popover.prototype.setContent = function () {
- var $tip = this.tip()
- var title = this.getTitle()
- var content = this.getContent()
+/**
+ * @override
+ */
+Popover.prototype.getEventObject = function () {
+ return Popover._Event
+}
- $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
- $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events
- this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
- ](content)
- $tip.removeClass('fade popover-top popover-bottom popover-left popover-right in')
+/**
+ * @override
+ */
+Popover.prototype.getArrowElement = function () {
+ return (this.arrow = this.arrow || $(this.getTipElement()).find(Popover._Selector.ARROW)[0])
+}
- // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
- // this manually by checking the contents.
- if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
- }
- Popover.prototype.hasContent = function () {
- return this.getTitle() || this.getContent()
+/**
+ * @override
+ */
+Popover.prototype.setContent = function () {
+ var tip = this.getTipElement()
+ var title = this.getTitle()
+ var content = this._getContent()
+ var titleElement = $(tip).find(Popover._Selector.TITLE)[0]
+
+ if (titleElement) {
+ titleElement[this.config['html'] ? 'innerHTML' : 'innerText'] = title
}
- Popover.prototype.getContent = function () {
- var $e = this.$element
- var o = this.options
+ // we use append for html objects to maintain js events
+ $(tip).find(Popover._Selector.CONTENT).children().detach().end()[
+ this.config['html'] ? (typeof content == 'string' ? 'html' : 'append') : 'text'
+ ](content)
- return $e.attr('data-content')
- || (typeof o.content == 'function' ?
- o.content.call($e[0]) :
- o.content)
- }
+ $(tip)
+ .removeClass(Popover._ClassName.FADE)
+ .removeClass(Popover._ClassName.IN)
- Popover.prototype.arrow = function () {
- return (this.$arrow = this.$arrow || this.tip().find('.popover-arrow'))
+ for (var direction in Tooltip.Direction) {
+ $(tip).removeClass(Popover._NAME + '-' + Tooltip.Direction[direction])
}
+}
- Popover.prototype.tip = function () {
- if (!this.$tip) this.$tip = $(this.options.template)
- return this.$tip
- }
+/**
+ * @override
+ */
+Popover.prototype.isWithContent = function () {
+ return this.getTitle() || this._getContent()
+}
- // POPOVER PLUGIN DEFINITION
- // =========================
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.popover')
- var options = typeof option == 'object' && option
+/**
+ * @override
+ */
+Popover.prototype.getTipElement = function () {
+ return (this.tip = this.tip || $(this.config['template'])[0])
+}
- if (!data && option == 'destroy') return
- if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
- var old = $.fn.popover
+/**
+ * @private
+ */
+Popover.prototype._getContent = function () {
+ return this.element.getAttribute('data-content')
+ || (typeof this.config['content'] == 'function' ?
+ this.config['content'].call(this.element) :
+ this.config['content'])
+}
- $.fn.popover = Plugin
- $.fn.popover.Constructor = Popover
- // POPOVER NO CONFLICT
- // ===================
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Popover._NAME] = Popover._jQueryInterface
+
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Popover._NAME]['Constructor'] = Popover
- $.fn.popover.noConflict = function () {
- $.fn.popover = old
- return this
- }
-}(jQuery);
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Popover._NAME]['noConflict'] = function () {
+ $.fn[Popover._NAME] = Popover._JQUERY_NO_CONFLICT
+ return this
+}
diff --git a/js/scrollspy.js b/js/scrollspy.js
index 0987177fd4..150241b540 100644
--- a/js/scrollspy.js
+++ b/js/scrollspy.js
@@ -1,175 +1,346 @@
-/* ========================================================================
- * Bootstrap: scrollspy.js v3.3.2
+/** =======================================================================
+ * Bootstrap: scrollspy.js v4.0.0
* http://getbootstrap.com/javascript/#scrollspy
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @fileoverview - Bootstrap's scrollspy plugin.
+ *
+ * Public Methods & Properties:
+ *
+ * + $.scrollspy
+ * + $.scrollspy.noConflict
+ * + $.scrollspy.Constructor
+ * + $.scrollspy.Constructor.VERSION
+ * + $.scrollspy.Constructor.Defaults
+ * + $.scrollspy.Constructor.Defaults.offset
+ * + $.scrollspy.Constructor.prototype.refresh
+ *
+ * ========================================================================
+ */
+'use strict';
-+function ($) {
- 'use strict';
- // SCROLLSPY CLASS DEFINITION
- // ==========================
+/**
+ * Our scrollspy class.
+ * @param {Element!} element
+ * @param {Object=} opt_config
+ * @constructor
+ */
+function ScrollSpy(element, opt_config) {
- function ScrollSpy(element, options) {
- var process = $.proxy(this.process, this)
+ /** @private {Element|Window} */
+ this._scrollElement = element.tagName == 'BODY' ? window : element
- this.$body = $('body')
- this.$scrollElement = $(element).is('body') ? $(window) : $(element)
- this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
- this.selector = (this.options.target || '') + ' .nav li > a'
- this.offsets = []
- this.targets = []
- this.activeTarget = null
- this.scrollHeight = 0
+ /** @private {Object} */
+ this._config = $.extend({}, ScrollSpy['Defaults'], opt_config)
- this.$scrollElement.on('scroll.bs.scrollspy', process)
- this.refresh()
- this.process()
- }
+ /** @private {string} */
+ this._selector = (this._config.target || '') + ' .nav li > a'
- ScrollSpy.VERSION = '3.3.2'
+ /** @private {Array} */
+ this._offsets = []
- ScrollSpy.DEFAULTS = {
- offset: 10
- }
+ /** @private {Array} */
+ this._targets = []
- ScrollSpy.prototype.getScrollHeight = function () {
- return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
- }
+ /** @private {Element} */
+ this._activeTarget = null
- ScrollSpy.prototype.refresh = function () {
- var offsetMethod = 'offset'
- var offsetBase = 0
+ /** @private {number} */
+ this._scrollHeight = 0
- if (!$.isWindow(this.$scrollElement[0])) {
- offsetMethod = 'position'
- offsetBase = this.$scrollElement.scrollTop()
- }
+ $(this._scrollElement).on('scroll.bs.scrollspy', this._process.bind(this))
- this.offsets = []
- this.targets = []
- this.scrollHeight = this.getScrollHeight()
-
- var self = this
-
- this.$body
- .find(this.selector)
- .map(function () {
- var $el = $(this)
- var href = $el.data('target') || $el.attr('href')
- var $href = /^#./.test(href) && $(href)
-
- return ($href
- && $href.length
- && $href.is(':visible')
- && [[$href[offsetMethod]().top + offsetBase, href]]) || null
- })
- .sort(function (a, b) { return a[0] - b[0] })
- .each(function () {
- self.offsets.push(this[0])
- self.targets.push(this[1])
- })
- }
+ this['refresh']()
- ScrollSpy.prototype.process = function () {
- var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
- var scrollHeight = this.getScrollHeight()
- var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height()
- var offsets = this.offsets
- var targets = this.targets
- var activeTarget = this.activeTarget
- var i
-
- if (this.scrollHeight != scrollHeight) {
- this.refresh()
- }
+ this._process()
+}
- if (scrollTop >= maxScroll) {
- return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
- }
- if (activeTarget && scrollTop < offsets[0]) {
- this.activeTarget = null
- return this.clear()
+/**
+ * @const
+ * @type {string}
+ */
+ScrollSpy['VERSION'] = '4.0.0'
+
+
+/**
+ * @const
+ * @type {Object}
+ */
+ScrollSpy['Defaults'] = {
+ 'offset': 10
+}
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+ScrollSpy._NAME = 'scrollspy'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+ScrollSpy._DATA_KEY = 'bs.scrollspy'
+
+
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+ScrollSpy._JQUERY_NO_CONFLICT = $.fn[ScrollSpy._NAME]
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+ScrollSpy._Event = {
+ ACTIVATE: 'activate.bs.scrollspy'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+ScrollSpy._ClassName = {
+ DROPDOWN_MENU : 'dropdown-menu',
+ ACTIVE : 'active'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+ScrollSpy._Selector = {
+ DATA_SPY : '[data-spy="scroll"]',
+ ACTIVE : '.active',
+ LI_DROPDOWN : 'li.dropdown',
+ LI : 'li'
+}
+
+
+/**
+ * @param {Object=} opt_config
+ * @this {jQuery}
+ * @return {jQuery}
+ * @private
+ */
+ScrollSpy._jQueryInterface = function (opt_config) {
+ return this.each(function () {
+ var data = $(this).data(ScrollSpy._DATA_KEY)
+ var config = typeof opt_config === 'object' && opt_config || null
+
+ if (!data) {
+ data = new ScrollSpy(this, config)
+ $(this).data(ScrollSpy._DATA_KEY, data)
}
- for (i = offsets.length; i--;) {
- activeTarget != targets[i]
- && scrollTop >= offsets[i]
- && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
- && this.activate(targets[i])
+ if (typeof opt_config === 'string') {
+ data[opt_config]()
}
+ })
+}
+
+
+/**
+ * Refresh the scrollspy target cache
+ */
+ScrollSpy.prototype['refresh'] = function () {
+ var offsetMethod = 'offset'
+ var offsetBase = 0
+
+ if (this._scrollElement !== this._scrollElement.window) {
+ offsetMethod = 'position'
+ offsetBase = this._getScrollTop()
}
- ScrollSpy.prototype.activate = function (target) {
- this.activeTarget = target
+ this._offsets = []
+ this._targets = []
- this.clear()
+ this._scrollHeight = this._getScrollHeight()
- var selector = this.selector +
- '[data-target="' + target + '"],' +
- this.selector + '[href="' + target + '"]'
+ var targets = /** @type {Array.<Element>} */ ($.makeArray($(this._selector)))
- var active = $(selector)
- .parents('li')
- .addClass('active')
+ targets
+ .map(function (element, index) {
+ var target
+ var targetSelector = Bootstrap.getSelectorFromElement(element)
- if (active.parent('.dropdown-menu').length) {
- active = active
- .closest('li.dropdown')
- .addClass('active')
+ if (targetSelector) {
+ target = $(targetSelector)[0]
+ }
+
+ if (target && (target.offsetWidth || target.offsetHeight)) {
+ // todo (fat): remove sketch reliance on jQuery position/offset
+ return [$(target)[offsetMethod]().top + offsetBase, targetSelector]
+ }
+ })
+ .filter(function (item) { return item })
+ .sort(function (a, b) { return a[0] - b[0] })
+ .forEach(function (item, index) {
+ this._offsets.push(item[0])
+ this._targets.push(item[1])
+ }.bind(this))
+}
+
+
+/**
+ * @private
+ */
+ScrollSpy.prototype._getScrollTop = function () {
+ return this._scrollElement === window ?
+ this._scrollElement.scrollY : this._scrollElement.scrollTop
+}
+
+
+/**
+ * @private
+ */
+ScrollSpy.prototype._getScrollHeight = function () {
+ return this._scrollElement.scrollHeight
+ || Math.max(document.body.scrollHeight, document.documentElement.scrollHeight)
+}
+
+
+/**
+ * @private
+ */
+ScrollSpy.prototype._process = function () {
+ var scrollTop = this._getScrollTop() + this._config.offset
+ var scrollHeight = this._getScrollHeight()
+ var maxScroll = this._config.offset + scrollHeight - this._scrollElement.offsetHeight
+
+ if (this._scrollHeight != scrollHeight) {
+ this['refresh']()
+ }
+
+ if (scrollTop >= maxScroll) {
+ var target = this._targets[this._targets.length - 1]
+
+ if (this._activeTarget != target) {
+ this._activate(target)
}
+ }
- active.trigger('activate.bs.scrollspy')
+ if (this._activeTarget && scrollTop < this._offsets[0]) {
+ this._activeTarget = null
+ this._clear()
+ return
}
- ScrollSpy.prototype.clear = function () {
- $(this.selector)
- .parentsUntil(this.options.target, '.active')
- .removeClass('active')
+ for (var i = this._offsets.length; i--;) {
+ var isActiveTarget = this._activeTarget != this._targets[i]
+ && scrollTop >= this._offsets[i]
+ && (!this._offsets[i + 1] || scrollTop < this._offsets[i + 1])
+
+ if (isActiveTarget) {
+ this._activate(this._targets[i])
+ }
}
+}
- // SCROLLSPY PLUGIN DEFINITION
- // ===========================
+/**
+ * @param {Element} target
+ * @private
+ */
+ScrollSpy.prototype._activate = function (target) {
+ this._activeTarget = target
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.scrollspy')
- var options = typeof option == 'object' && option
+ this._clear()
- if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
+ var selector = this._selector
+ + '[data-target="' + target + '"],'
+ + this._selector + '[href="' + target + '"]'
+
+ // todo (fat): this seems horribly wrong… getting all raw li elements up the tree ,_,
+ var parentListItems = $(selector).parents(ScrollSpy._Selector.LI)
+
+ for (var i = parentListItems.length; i--;) {
+ $(parentListItems[i]).addClass(ScrollSpy._ClassName.ACTIVE)
+
+ var itemParent = parentListItems[i].parentNode
- var old = $.fn.scrollspy
+ if (itemParent && $(itemParent).hasClass(ScrollSpy._ClassName.DROPDOWN_MENU)) {
+ var closestDropdown = $(itemParent).closest(ScrollSpy._Selector.LI_DROPDOWN)[0]
+ $(closestDropdown).addClass(ScrollSpy._ClassName.ACTIVE)
+ }
+ }
- $.fn.scrollspy = Plugin
- $.fn.scrollspy.Constructor = ScrollSpy
+ $(this._scrollElement).trigger(ScrollSpy._Event.ACTIVATE, {
+ relatedTarget: target
+ })
+}
- // SCROLLSPY NO CONFLICT
- // =====================
+/**
+ * @private
+ */
+ScrollSpy.prototype._clear = function () {
+ var activeParents = $(this._selector).parentsUntil(this._config.target, ScrollSpy._Selector.ACTIVE)
- $.fn.scrollspy.noConflict = function () {
- $.fn.scrollspy = old
- return this
+ for (var i = activeParents.length; i--;) {
+ $(activeParents[i]).removeClass(ScrollSpy._ClassName.ACTIVE)
}
+}
- // SCROLLSPY DATA-API
- // ==================
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
- $(window).on('load.bs.scrollspy.data-api', function () {
- $('[data-spy="scroll"]').each(function () {
- var $spy = $(this)
- Plugin.call($spy, $spy.data())
- })
- })
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[ScrollSpy._NAME] = ScrollSpy._jQueryInterface
+
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[ScrollSpy._NAME]['Constructor'] = ScrollSpy
-}(jQuery);
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[ScrollSpy._NAME]['noConflict'] = function () {
+ $.fn[ScrollSpy._NAME] = ScrollSpy._JQUERY_NO_CONFLICT
+ return this
+}
+
+
+/**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+$(window).on('load.bs.scrollspy.data-api', function () {
+ var scrollSpys = /** @type {Array.<Element>} */ ($.makeArray($(ScrollSpy._Selector.DATA_SPY)))
+
+ for (var i = scrollSpys.length; i--;) {
+ var $spy = $(scrollSpys[i])
+ ScrollSpy._jQueryInterface.call($spy, /** @type {Object|null} */ ($spy.data()))
+ }
+})
diff --git a/js/tab.js b/js/tab.js
index ddb7fc9c4f..07897294a3 100644
--- a/js/tab.js
+++ b/js/tab.js
@@ -1,153 +1,324 @@
-/* ========================================================================
- * Bootstrap: tab.js v3.3.2
+/** =======================================================================
+ * Bootstrap: tab.js v4.0.0
* http://getbootstrap.com/javascript/#tabs
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @fileoverview - Bootstrap's tab plugin. Tab O_O
+ *
+ * Public Methods & Properties:
+ *
+ * + $.tab
+ * + $.tab.noConflict
+ * + $.tab.Constructor
+ * + $.tab.Constructor.VERSION
+ * + $.tab.Constructor.prototype.show
+ *
+ * ========================================================================
+ */
+
+
+'use strict';
+
+/**
+ * Our Tab class.
+ * @param {Element!} element
+ * @constructor
+ */
+var Tab = function (element) {
+
+ /** @type {Element} */
+ this._element = element
+
+}
+
+
+/**
+ * @const
+ * @type {string}
+ */
+Tab['VERSION'] = '4.0.0'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Tab._NAME = 'tab'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Tab._DATA_KEY = 'bs.tab'
+
+
+/**
+ * @const
+ * @type {number}
+ * @private
+ */
+Tab._TRANSITION_DURATION = 150
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Tab._Event = {
+ HIDE : 'hide.bs.tab',
+ HIDDEN : 'hidden.bs.tab',
+ SHOW : 'show.bs.tab',
+ SHOWN : 'shown.bs.tab'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Tab._ClassName = {
+ DROPDOWN_MENU : 'dropdown-menu',
+ ACTIVE : 'active',
+ FADE : 'fade',
+ IN : 'in'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Tab._Selector = {
+ A : 'a',
+ LI : 'li',
+ LI_DROPDOWN : 'li.dropdown',
+ UL : 'ul:not(.dropdown-menu)',
+ FADE_CHILD : ':scope > .fade',
+ ACTIVE : '.active',
+ ACTIVE_CHILD : ':scope > .active',
+ DATA_TOGGLE : '[data-toggle="tab"], [data-toggle="pill"]',
+ DROPDOWN_ACTIVE_CHILD : ':scope > .dropdown-menu > .active'
+}
+
+
+/**
+ * @param {Object|string=} opt_config
+ * @this {jQuery}
+ * @return {jQuery}
+ * @private
+ */
+Tab._jQueryInterface = function (opt_config) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data(Tab._DATA_KEY)
+
+ if (!data) {
+ data = data = new Tab(this)
+ $this.data(Tab._DATA_KEY, data)
+ }
+ if (typeof opt_config === 'string') {
+ data[opt_config]()
+ }
+ })
+}
+
+
+/**
+ * Show the tab
+ */
+Tab.prototype['show'] = function () {
+ if ( this._element.parentNode
+ && this._element.parentNode.nodeType == Node.ELEMENT_NODE
+ && $(this._element).parent().hasClass(Tab._ClassName.ACTIVE)) {
+ return
+ }
-+function ($) {
- 'use strict';
+ var ulElement = $(this._element).closest(Tab._Selector.UL)[0]
+ var selector = Bootstrap.getSelectorFromElement(this._element)
- // TAB CLASS DEFINITION
- // ====================
+ if (ulElement) {
+ var previous = /** @type {Array.<Element>} */ ($.makeArray($(ulElement).find(Tab._Selector.ACTIVE)))
+ previous = previous[previous.length - 1]
- var Tab = function (element) {
- this.element = $(element)
+ if (previous) {
+ previous = $(previous).find('a')[0]
+ }
}
- Tab.VERSION = '3.3.2'
+ var hideEvent = $.Event(Tab._Event.HIDE, {
+ relatedTarget: this._element
+ })
+
+ var showEvent = $.Event(Tab._Event.SHOW, {
+ relatedTarget: previous
+ })
- Tab.TRANSITION_DURATION = 150
+ if (previous) {
+ $(previous).trigger(hideEvent)
+ }
- Tab.prototype.show = function () {
- var $this = this.element
- var $ul = $this.closest('ul:not(.dropdown-menu)')
- var selector = $this.data('target')
+ $(this._element).trigger(showEvent)
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
- }
+ if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
- if ($this.parent('li').hasClass('active')) return
+ if (selector) {
+ var target = $(selector)[0]
+ }
- var $previous = $ul.find('.active:last a')
- var hideEvent = $.Event('hide.bs.tab', {
- relatedTarget: $this[0]
- })
- var showEvent = $.Event('show.bs.tab', {
- relatedTarget: $previous[0]
+ this._activate($(this._element).closest(Tab._Selector.LI)[0], ulElement)
+
+ var complete = function () {
+ var hiddenEvent = $.Event(Tab._Event.HIDDEN, {
+ relatedTarget: this._element
})
- $previous.trigger(hideEvent)
- $this.trigger(showEvent)
+ var shownEvent = $.Event(Tab._Event.SHOWN, {
+ relatedTarget: previous
+ })
- if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
+ $(previous).trigger(hiddenEvent)
+ $(this._element).trigger(shownEvent)
+ }.bind(this)
- var $target = $(selector)
+ if (target) {
+ this._activate(target, /** @type {Element} */ (target.parentNode), complete)
+ } else {
+ complete()
+ }
+}
+
+
+/**
+ * @param {Element} element
+ * @param {Element} container
+ * @param {Function=} opt_callback
+ * @private
+ */
+Tab.prototype._activate = function (element, container, opt_callback) {
+ var active = $(container).find(Tab._Selector.ACTIVE_CHILD)[0]
+ var isTransitioning = opt_callback
+ && Bootstrap.transition
+ && ((active && $(active).hasClass(Tab._ClassName.FADE))
+ || !!$(container).find(Tab._Selector.FADE_CHILD)[0])
+
+ var complete = this._transitionComplete.bind(this, element, active, isTransitioning, opt_callback)
+
+ if (active && isTransitioning) {
+ $(active)
+ .one(Bootstrap.TRANSITION_END, complete)
+ .emulateTransitionEnd(Tab._TRANSITION_DURATION)
+
+ } else {
+ complete()
+ }
- this.activate($this.closest('li'), $ul)
- this.activate($target, $target.parent(), function () {
- $previous.trigger({
- type: 'hidden.bs.tab',
- relatedTarget: $this[0]
- })
- $this.trigger({
- type: 'shown.bs.tab',
- relatedTarget: $previous[0]
- })
- })
+ if (active) {
+ $(active).removeClass(Tab._ClassName.IN)
}
+}
+
+
+/**
+ * @param {Element} element
+ * @param {Element} active
+ * @param {boolean} isTransitioning
+ * @param {Function=} opt_callback
+ * @private
+ */
+Tab.prototype._transitionComplete = function (element, active, isTransitioning, opt_callback) {
+ if (active) {
+ $(active).removeClass(Tab._ClassName.ACTIVE)
+
+ var dropdownChild = $(active).find(Tab._Selector.DROPDOWN_ACTIVE_CHILD)[0]
+ if (dropdownChild) {
+ $(dropdownChild).removeClass(Tab._ClassName.ACTIVE)
+ }
- Tab.prototype.activate = function (element, container, callback) {
- var $active = container.find('> .active')
- var transition = callback
- && $.support.transition
- && (($active.length && $active.hasClass('fade')) || !!container.find('> .fade').length)
-
- function next() {
- $active
- .removeClass('active')
- .find('> .dropdown-menu > .active')
- .removeClass('active')
- .end()
- .find('[data-toggle="tab"]')
- .attr('aria-expanded', false)
-
- element
- .addClass('active')
- .find('[data-toggle="tab"]')
- .attr('aria-expanded', true)
-
- if (transition) {
- element[0].offsetWidth // reflow for transition
- element.addClass('in')
- } else {
- element.removeClass('fade')
- }
-
- if (element.parent('.dropdown-menu')) {
- element
- .closest('li.dropdown')
- .addClass('active')
- .end()
- .find('[data-toggle="tab"]')
- .attr('aria-expanded', true)
- }
-
- callback && callback()
+ var activeToggle = $(active).find(Tab._Selector.DATA_TOGGLE)[0]
+ if (activeToggle) {
+ activeToggle.setAttribute('aria-expanded', false)
}
+ }
- $active.length && transition ?
- $active
- .one('bsTransitionEnd', next)
- .emulateTransitionEnd(Tab.TRANSITION_DURATION) :
- next()
+ $(element).addClass(Tab._ClassName.ACTIVE)
- $active.removeClass('in')
+ var elementToggle = $(element).find(Tab._Selector.DATA_TOGGLE)[0]
+ if (elementToggle) {
+ elementToggle.setAttribute('aria-expanded', true)
}
+ if (isTransitioning) {
+ Bootstrap.reflow(element)
+ $(element).addClass(Tab._ClassName.IN)
+ } else {
+ $(element).removeClass(Tab._ClassName.FADE)
+ }
- // TAB PLUGIN DEFINITION
- // =====================
+ if (element.parentNode && $(element.parentNode).hasClass(Tab._ClassName.DROPDOWN_MENU)) {
+ var dropdownElement = $(element).closest(Tab._Selector.LI_DROPDOWN)[0]
+ if (dropdownElement) {
+ $(dropdownElement).addClass(Tab._ClassName.ACTIVE)
+ }
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.tab')
+ elementToggle = $(element).find(Tab._Selector.DATA_TOGGLE)[0]
+ if (elementToggle) {
+ elementToggle.setAttribute('aria-expanded', true)
+ }
+ }
- if (!data) $this.data('bs.tab', (data = new Tab(this)))
- if (typeof option == 'string') data[option]()
- })
+ if (opt_callback) {
+ opt_callback()
}
+}
- var old = $.fn.tab
- $.fn.tab = Plugin
- $.fn.tab.Constructor = Tab
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Tab._NAME] = Tab._jQueryInterface
- // TAB NO CONFLICT
- // ===============
- $.fn.tab.noConflict = function () {
- $.fn.tab = old
- return this
- }
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Tab._NAME]['Constructor'] = Tab
- // TAB DATA-API
- // ============
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Tab._NAME]['noConflict'] = function () {
+ $.fn[Tab._NAME] = Tab._JQUERY_NO_CONFLICT
+ return this
+}
- var clickHandler = function (e) {
- e.preventDefault()
- Plugin.call($(this), 'show')
- }
- $(document)
- .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
- .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
-}(jQuery);
+// TAB DATA-API
+// ============
+
+var clickHandler = function (e) {
+ e.preventDefault()
+ Tab._jQueryInterface.call($(this), 'show')
+}
+
+$(document)
+ .on('click.bs.tab.data-api', Tab._Selector.DATA_TOGGLE, clickHandler)
diff --git a/js/tests/closure.html b/js/tests/closure.html
new file mode 100644
index 0000000000..82c65f62b8
--- /dev/null
+++ b/js/tests/closure.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Bootstrap Plugin Test Suite</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+
+ <!-- jQuery -->
+ <script src="vendor/jquery.min.js"></script>
+
+ <!-- QUnit -->
+ <link rel="stylesheet" href="vendor/qunit.css" media="screen">
+ <script src="vendor/qunit.js"></script>
+ <style>
+ #qunit-tests > li.pass {
+ display: none;/* Make it easier to see failing tests in Sauce screencasts */
+ }
+
+ #qunit-fixture {
+ top: 0;
+ left: 0;
+ }
+ </style>
+ <script>
+ // See https://github.com/axemclion/grunt-saucelabs#test-result-details-with-qunit
+ var log = []
+ QUnit.done(function (testResults) {
+ var tests = []
+ for (var i = 0, len = log.length; i < len; i++) {
+ var details = log[i]
+ tests.push({
+ name: details.name,
+ result: details.result,
+ expected: details.expected,
+ actual: details.actual,
+ source: details.source
+ })
+ }
+ testResults.tests = tests
+
+ window.global_test_results = testResults
+ })
+
+ QUnit.testStart(function (testDetails) {
+ $(window).scrollTop(0)
+ QUnit.log = function (details) {
+ if (!details.result) {
+ details.name = testDetails.name
+ log.push(details)
+ }
+ }
+ })
+
+ // Cleanup
+ QUnit.testDone(function () {
+ $('#qunit-fixture').empty()
+ $('#modal-test, .modal-backdrop').remove()
+ })
+ </script>
+
+ <!-- Plugin sources -->
+ <script src="../../dist/js/bootstrap.min.js"></script>
+
+ <!-- Unit tests -->
+ <script src="unit/alert.js"></script>
+ <script src="unit/button.js"></script>
+ <script src="unit/carousel.js"></script>
+ <script src="unit/collapse.js"></script>
+ <script src="unit/dropdown.js"></script>
+ <script src="unit/modal.js"></script>
+ <script src="unit/scrollspy.js"></script>
+ <script src="unit/tab.js"></script>
+ <script src="unit/tooltip.js"></script>
+ <script src="unit/popover.js"></script>
+
+ </head>
+ <body>
+ <div id="qunit-container">
+ <div id="qunit"></div>
+ <div id="qunit-fixture"></div>
+ </div>
+ </body>
+</html>
diff --git a/js/tests/index.html b/js/tests/index.html
index b32f0adc22..a534da526d 100644
--- a/js/tests/index.html
+++ b/js/tests/index.html
@@ -59,7 +59,7 @@
</script>
<!-- Plugin sources -->
- <script>$.support.transition = false</script>
+ <script src="../../js/util.js"></script>
<script src="../../js/alert.js"></script>
<script src="../../js/button.js"></script>
<script src="../../js/carousel.js"></script>
@@ -70,7 +70,6 @@
<script src="../../js/tab.js"></script>
<script src="../../js/tooltip.js"></script>
<script src="../../js/popover.js"></script>
- <script src="../../js/affix.js"></script>
<!-- Unit tests -->
<script src="unit/alert.js"></script>
@@ -83,7 +82,6 @@
<script src="unit/tab.js"></script>
<script src="unit/tooltip.js"></script>
<script src="unit/popover.js"></script>
- <script src="unit/affix.js"></script>
</head>
<body>
diff --git a/js/tests/unit/button.js b/js/tests/unit/button.js
index cb51d40b95..08b071d655 100644
--- a/js/tests/unit/button.js
+++ b/js/tests/unit/button.js
@@ -29,61 +29,6 @@ $(function () {
strictEqual($button[0], $el[0], 'collection contains element')
})
- test('should return set state to loading', function (assert) {
- var $btn = $('<button class="btn" data-loading-text="fat">mdo</button>')
- equal($btn.html(), 'mdo', 'btn text equals mdo')
- $btn.bootstrapButton('loading')
- var done = assert.async()
- setTimeout(function () {
- equal($btn.html(), 'fat', 'btn text equals fat')
- ok($btn[0].hasAttribute('disabled'), 'btn is disabled')
- ok($btn.hasClass('disabled'), 'btn has disabled class')
- done()
- }, 0)
- })
-
- test('should return reset state', function (assert) {
- var $btn = $('<button class="btn" data-loading-text="fat">mdo</button>')
- equal($btn.html(), 'mdo', 'btn text equals mdo')
- $btn.bootstrapButton('loading')
- var doneOne = assert.async()
- setTimeout(function () {
- equal($btn.html(), 'fat', 'btn text equals fat')
- ok($btn[0].hasAttribute('disabled'), 'btn is disabled')
- ok($btn.hasClass('disabled'), 'btn has disabled class')
- doneOne()
- var doneTwo = assert.async()
- $btn.bootstrapButton('reset')
- setTimeout(function () {
- equal($btn.html(), 'mdo', 'btn text equals mdo')
- ok(!$btn[0].hasAttribute('disabled'), 'btn is not disabled')
- ok(!$btn.hasClass('disabled'), 'btn does not have disabled class')
- doneTwo()
- }, 0)
- }, 0)
- })
-
- test('should work with an empty string as reset state', function (assert) {
- var $btn = $('<button class="btn" data-loading-text="fat"/>')
- equal($btn.html(), '', 'btn text equals ""')
- $btn.bootstrapButton('loading')
- var doneOne = assert.async()
- setTimeout(function () {
- equal($btn.html(), 'fat', 'btn text equals fat')
- ok($btn[0].hasAttribute('disabled'), 'btn is disabled')
- ok($btn.hasClass('disabled'), 'btn has disabled class')
- doneOne()
- var doneTwo = assert.async()
- $btn.bootstrapButton('reset')
- setTimeout(function () {
- equal($btn.html(), '', 'btn text equals ""')
- ok(!$btn[0].hasAttribute('disabled'), 'btn is not disabled')
- ok(!$btn.hasClass('disabled'), 'btn does not have disabled class')
- doneTwo()
- }, 0)
- }, 0)
- })
-
test('should toggle active', function () {
var $btn = $('<button class="btn" data-toggle="button">mdo</button>')
ok(!$btn.hasClass('active'), 'btn does not have active class')
diff --git a/js/tests/unit/carousel.js b/js/tests/unit/carousel.js
index 6da932d2c2..2ad2c67347 100644
--- a/js/tests/unit/carousel.js
+++ b/js/tests/unit/carousel.js
@@ -322,30 +322,31 @@ $(function () {
+ '<a class="left carousel-control" href="#myCarousel" data-slide="prev">&lsaquo;</a>'
+ '<a class="right carousel-control" href="#myCarousel" data-slide="next">&rsaquo;</a>'
+ '</div>'
+
var $carousel = $(templateHTML)
$carousel.attr('data-interval', 1814)
$carousel.appendTo('body')
$('[data-slide]').first().click()
- equal($carousel.data('bs.carousel').options.interval, 1814)
+ equal($carousel.data('bs.carousel').getConfig().interval, 1814)
$carousel.remove()
$carousel.appendTo('body').attr('data-modal', 'foobar')
$('[data-slide]').first().click()
- equal($carousel.data('bs.carousel').options.interval, 1814, 'even if there is an data-modal attribute set')
+ equal($carousel.data('bs.carousel').getConfig().interval, 1814, 'even if there is an data-modal attribute set')
$carousel.remove()
$carousel.appendTo('body')
$('[data-slide]').first().click()
$carousel.attr('data-interval', 1860)
$('[data-slide]').first().click()
- equal($carousel.data('bs.carousel').options.interval, 1814, 'attributes should be read only on initialization')
+ equal($carousel.data('bs.carousel').getConfig().interval, 1814, 'attributes should be read only on initialization')
$carousel.remove()
$carousel.attr('data-interval', false)
$carousel.appendTo('body')
$carousel.bootstrapCarousel(1)
- strictEqual($carousel.data('bs.carousel').options.interval, false, 'data attribute has higher priority than default options')
+ strictEqual($carousel.data('bs.carousel').getConfig().interval, false, 'data attribute has higher priority than default options')
$carousel.remove()
})
diff --git a/js/tests/unit/collapse.js b/js/tests/unit/collapse.js
index ad59d87dbc..ac6e657b38 100644
--- a/js/tests/unit/collapse.js
+++ b/js/tests/unit/collapse.js
@@ -288,7 +288,7 @@ $(function () {
$body2
.toggleClass('in collapsing')
- .data('bs.collapse').transitioning = 1
+ .data('bs.collapse').setTransitioning(true)
$target1.click()
diff --git a/js/tests/unit/modal.js b/js/tests/unit/modal.js
index c1af15a2f9..7018b865f3 100644
--- a/js/tests/unit/modal.js
+++ b/js/tests/unit/modal.js
@@ -30,7 +30,7 @@ $(function () {
})
test('should expose defaults var for settings', function () {
- ok($.fn.bootstrapModal.Constructor.DEFAULTS, 'default object exposed')
+ ok($.fn.bootstrapModal.Constructor.Defaults, 'default object exposed')
})
test('should insert into dom when show method is called', function (assert) {
diff --git a/js/tests/unit/tooltip.js b/js/tests/unit/tooltip.js
index 20efff8cc0..2b63a3b48c 100644
--- a/js/tests/unit/tooltip.js
+++ b/js/tests/unit/tooltip.js
@@ -30,7 +30,7 @@ $(function () {
})
test('should expose default settings', function () {
- ok($.fn.bootstrapTooltip.Constructor.DEFAULTS, 'defaults is defined')
+ ok($.fn.bootstrapTooltip.Constructor.Defaults, 'defaults is defined')
})
test('should empty title attribute', function () {
@@ -879,17 +879,17 @@ $(function () {
.bootstrapTooltip({ delay: { show: 0, hide: 150 }})
setTimeout(function () {
- ok($tooltip.data('bs.tooltip').$tip.is('.fade.in'), '1ms: tooltip faded in')
+ ok($('.tooltip').is('.fade.in'), '1ms: tooltip faded in')
$tooltip.trigger('mouseout')
setTimeout(function () {
- ok($tooltip.data('bs.tooltip').$tip.is('.fade.in'), '100ms: tooltip still faded in')
+ ok($('.tooltip').is('.fade.in'), '100ms: tooltip still faded in')
}, 100)
setTimeout(function () {
- ok(!$tooltip.data('bs.tooltip').$tip.is('.in'), '200ms: tooltip removed')
- done()
+ ok(!$('.tooltip').is('.in'), '200ms: tooltip removed')
+ start()
}, 200)
}, 0)
@@ -978,7 +978,7 @@ $(function () {
test('should not reload the tooltip on subsequent mouseenter events', function () {
var titleHtml = function () {
- var uid = $.fn.bootstrapTooltip.Constructor.prototype.getUID('tooltip')
+ var uid = 'fatTooltip'
return '<p id="tt-content">' + uid + '</p><p>' + uid + '</p><p>' + uid + '</p>'
}
@@ -1004,7 +1004,7 @@ $(function () {
test('should not reload the tooltip if the mouse leaves and re-enters before hiding', function () {
var titleHtml = function () {
- var uid = $.fn.bootstrapTooltip.Constructor.prototype.getUID('tooltip')
+ var uid = 'fatTooltip'
return '<p id="tt-content">' + uid + '</p><p>' + uid + '</p><p>' + uid + '</p>'
}
@@ -1029,10 +1029,12 @@ $(function () {
$('#tt-outer').trigger('mouseleave')
equal(currentUid, $('#tt-content').text())
- ok(obj.hoverState == 'out', 'the tooltip hoverState should be set to "out"')
+ debugger
+
+ ok(obj.getHoverState() == 'out', 'the tooltip hoverState should be set to "out"')
$('#tt-content').trigger('mouseenter')
- ok(obj.hoverState == 'in', 'the tooltip hoverState should be set to "in"')
+ ok(obj.getHoverState() == 'in', 'the tooltip hoverState should be set to "in"')
equal(currentUid, $('#tt-content').text())
})
@@ -1051,7 +1053,7 @@ $(function () {
$('<a href="#" title="tooltip title" style="position: absolute; bottom: 0; right: 0;">Foobar</a>')
.appendTo('body')
.on('shown.bs.tooltip', function () {
- var arrowStyles = $(this).data('bs.tooltip').$tip.find('.tooltip-arrow').attr('style')
+ var arrowStyles = $('.tooltip').find('.tooltip-arrow').attr('style')
ok(/left/i.test(arrowStyles) && !/top/i.test(arrowStyles), 'arrow positioned correctly')
$(this).bootstrapTooltip('hide')
})
diff --git a/js/tests/visual/affix.html b/js/tests/visual/affix.html
deleted file mode 100644
index 782fcb32c7..0000000000
--- a/js/tests/visual/affix.html
+++ /dev/null
@@ -1,299 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
- <meta charset="utf-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <title>Affix</title>
- <link rel="stylesheet" href="../../../dist/css/bootstrap.min.css">
-
- <style>
- /* Test Styles */
- .affixed-element-top.affix {
- top: 10px;
- }
- .affixed-element-top.affix-bottom {
- position: absolute;
- }
- .affixed-element-bottom {
- margin-bottom: 0;
- }
- .affixed-element-bottom.affix {
- bottom: 10px;
- }
- .affixed-element-bottom.affix-bottom {
- position: relative;
- }
- .grow-btn, .shrink-btn {
- color: #FFF;
- }
- .grow-btn {
- background-color: #2ECC40;
- }
- .grow-btn:hover {
- background-color: #3D9970;
- }
- .shrink-btn {
- background-color: #FF4136;
- }
- .shrink-btn:hover {
- background-color: #85144B;
- }
- </style>
-</head>
-<body>
-
-<div class="container">
-
- <div class="page-header js-page-header">
- <h1>Affix <small>Bootstrap Visual Test</small></h1>
- </div>
-
- <div class="col-md-3">
- <ul class="list-group affixed-element-top js-affixed-element-top">
- <li class="list-group-item">Cras justo odio</li>
- <li class="list-group-item">Dapibus ac facilisis in</li>
- <li class="list-group-item">Morbi leo risus</li>
- <li class="list-group-item">Porta ac consectetur ac</li>
- <li class="list-group-item">Vestibulum at eros</li>
- <li class="list-group-item">Cras justo odio</li>
- <li class="list-group-item">Dapibus ac facilisis in</li>
- <li class="list-group-item">Morbi leo risus</li>
- <li class="list-group-item">Porta ac consectetur ac</li>
- <li class="list-group-item">Vestibulum at eros</li>
- <li class="list-group-item">Cras justo odio</li>
- <li class="list-group-item">Dapibus ac facilisis in</li>
- <li class="list-group-item">Morbi leo risus</li>
- <li class="list-group-item">Porta ac consectetur ac</li>
- <li class="list-group-item">Vestibulum at eros</li>
- <li class="list-group-item">Porta ac consectetur ac</li>
- </ul>
- </div>
-
- <div class="col-md-6 js-content">
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
-
- </div>
-
- <div class="col-md-3">
- <ul class="list-group affixed-element-bottom js-affixed-element-bottom">
- <li class="list-group-item">Sit necessitatibus aspernatur.</li>
- <li class="list-group-item">Adipisicing alias dolor!</li>
- <li class="list-group-item">Ipsum molestiae impedit.</li>
- <li class="list-group-item">Amet quis iste?</li>
- <li class="list-group-item">Ipsum quaerat porro.</li>
- <li class="list-group-item">Elit lorem libero.</li>
- <li class="list-group-item">Ipsum dolore facilis.</li>
- <li class="list-group-item">Elit ad atque.</li>
- <li class="list-group-item">Dolor amet sequi!</li>
- <li class="list-group-item">Consectetur voluptatum facilis!</li>
- <li class="list-group-item">Sit neque eligendi?</li>
- <li class="list-group-item">Amet fuga consectetur!</li>
- <li class="list-group-item">Amet molestias repellat!</li>
- <li class="list-group-item">Consectetur minima repellendus.</li>
- <li class="list-group-item grow-btn js-grow-btn">Grow content</li>
- <li class="list-group-item shrink-btn js-shrink-btn">Shrink content</li>
- </ul>
- </div>
-
- <div class="col-md-12 js-footer">
- <hr>
-
- <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
-
- <p>Nullam id dolor id nibh ultricies vehicula ut id elit. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
-
- <p>Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue.</p>
-
- <p>Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
- </div>
-
-</div>
-
-<!-- JavaScript Includes -->
-<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
-<script src="../../affix.js"></script>
-
-
-<!-- JavaScript Test -->
-<script>
-$(function () {
- $('.js-affixed-element-top').affix({
- offset: {
- top: $('.js-page-header').outerHeight(true) - 10
- , bottom: $('.js-footer').outerHeight(true) + 10
- }
- })
- // todo(fat): sux you have to do this.
- .on('affix.bs.affix', function (e) {
- $(e.target).width(e.target.offsetWidth)
- })
-
- $('.js-affixed-element-bottom').affix({
- offset: {
- bottom: $('.js-footer').outerHeight(true) + 10
- }
- })
-
- $('.js-grow-btn').on('click', function() {
- $('.js-content').append('<p>Ipsum corrupti ipsam est temporibus.</p>')
- })
- $('.js-shrink-btn').on('click', function() {
- $('.js-content p').last().remove()
- })
-})
-</script>
-</body>
-</html>
diff --git a/js/tests/visual/alert.html b/js/tests/visual/alert.html
index 2f824ace16..978f9ac209 100644
--- a/js/tests/visual/alert.html
+++ b/js/tests/visual/alert.html
@@ -34,7 +34,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
+<script src="../../util.js"></script>
<script src="../../alert.js"></script>
</body>
diff --git a/js/tests/visual/button.html b/js/tests/visual/button.html
index e9b06dfa26..c3be921415 100644
--- a/js/tests/visual/button.html
+++ b/js/tests/visual/button.html
@@ -15,10 +15,6 @@
<h1>Button <small>Bootstrap Visual Test</small></h1>
</div>
- <button type="button" data-loading-text="Loading for 3 seconds..." class="btn btn-primary js-loading-button">
- Loading state
- </button>
-
<button type="button" class="btn btn-primary" data-toggle="button">Single toggle</button>
<div class="btn-group" data-toggle="buttons">
@@ -49,7 +45,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
+<script src="../../util.js"></script>
<script src="../../button.js"></script>
<!-- JavaScript Test -->
diff --git a/js/tests/visual/carousel.html b/js/tests/visual/carousel.html
index 5b071679fc..45424a5d15 100644
--- a/js/tests/visual/carousel.html
+++ b/js/tests/visual/carousel.html
@@ -44,7 +44,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
+<script src="../../util.js"></script>
<script src="../../carousel.js"></script>
</body>
diff --git a/js/tests/visual/collapse.html b/js/tests/visual/collapse.html
index 89b36d7a62..1b81cf5ca8 100644
--- a/js/tests/visual/collapse.html
+++ b/js/tests/visual/collapse.html
@@ -64,7 +64,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
+<script src="../../util.js"></script>
<script src="../../collapse.js"></script>
</body>
diff --git a/js/tests/visual/dropdown.html b/js/tests/visual/dropdown.html
index 38f28bca14..6cf18df8ee 100644
--- a/js/tests/visual/dropdown.html
+++ b/js/tests/visual/dropdown.html
@@ -93,7 +93,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
+<script src="../../util.js"></script>
<script src="../../dropdown.js"></script>
<script src="../../collapse.js"></script>
diff --git a/js/tests/visual/modal.html b/js/tests/visual/modal.html
index 86d1427f99..0d6e50ea2c 100644
--- a/js/tests/visual/modal.html
+++ b/js/tests/visual/modal.html
@@ -63,7 +63,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
+<script src="../../util.js"></script>
<script src="../../modal.js"></script>
<script src="../../tooltip.js"></script>
<script src="../../popover.js"></script>
diff --git a/js/tests/visual/popover.html b/js/tests/visual/popover.html
index e71ebca8c6..7446a67412 100644
--- a/js/tests/visual/popover.html
+++ b/js/tests/visual/popover.html
@@ -32,7 +32,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
+<script src="../../util.js"></script>
<script src="../../tooltip.js"></script>
<script src="../../popover.js"></script>
diff --git a/js/tests/visual/scrollspy.html b/js/tests/visual/scrollspy.html
index 1edfbc383d..13bc4383cc 100644
--- a/js/tests/visual/scrollspy.html
+++ b/js/tests/visual/scrollspy.html
@@ -93,7 +93,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
+<script src="../../util.js"></script>
<script src="../../scrollspy.js"></script>
<script src="../../dropdown.js"></script>
<script src="../../collapse.js"></script>
diff --git a/js/tests/visual/tab.html b/js/tests/visual/tab.html
index a1dd8214c0..2480ce08ca 100644
--- a/js/tests/visual/tab.html
+++ b/js/tests/visual/tab.html
@@ -157,7 +157,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
+<script src="../../util.js"></script>
<script src="../../tab.js"></script>
<script src="../../dropdown.js"></script>
diff --git a/js/tests/visual/tooltip.html b/js/tests/visual/tooltip.html
index 7c8361e4dc..c61b088d0e 100644
--- a/js/tests/visual/tooltip.html
+++ b/js/tests/visual/tooltip.html
@@ -29,7 +29,7 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
+<script src="../../util.js"></script>
<script src="../../tooltip.js"></script>
<!-- JavaScript Test -->
diff --git a/js/tooltip.js b/js/tooltip.js
index 8663bb7675..ced0e9282f 100644
--- a/js/tooltip.js
+++ b/js/tooltip.js
@@ -1,472 +1,868 @@
-/* ========================================================================
- * Bootstrap: tooltip.js v3.3.2
+/** =======================================================================
+ * Bootstrap: tooltip.js v4.0.0
* http://getbootstrap.com/javascript/#tooltip
- * Inspired by the original jQuery.tipsy by Jason Frame
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
+ * ========================================================================
+ * @fileoverview - Bootstrap's tooltip plugin.
+ * (Inspired by jQuery.tipsy by Jason Frame)
+ *
+ * Public Methods & Properties:
+ *
+ * + $.tooltip
+ * + $.tooltip.noConflict
+ * + $.tooltip.Constructor
+ * + $.tooltip.Constructor.VERSION
+ * + $.tooltip.Constructor.Defaults
+ * + $.tooltip.Constructor.Defaults.container
+ * + $.tooltip.Constructor.Defaults.animation
+ * + $.tooltip.Constructor.Defaults.placement
+ * + $.tooltip.Constructor.Defaults.selector
+ * + $.tooltip.Constructor.Defaults.template
+ * + $.tooltip.Constructor.Defaults.trigger
+ * + $.tooltip.Constructor.Defaults.title
+ * + $.tooltip.Constructor.Defaults.delay
+ * + $.tooltip.Constructor.Defaults.html
+ * + $.tooltip.Constructor.Defaults.viewport
+ * + $.tooltip.Constructor.Defaults.viewport.selector
+ * + $.tooltip.Constructor.Defaults.viewport.padding
+ * + $.tooltip.Constructor.prototype.enable
+ * + $.tooltip.Constructor.prototype.disable
+ * + $.tooltip.Constructor.prototype.destroy
+ * + $.tooltip.Constructor.prototype.toggleEnabled
+ * + $.tooltip.Constructor.prototype.toggle
+ * + $.tooltip.Constructor.prototype.show
+ * + $.tooltip.Constructor.prototype.hide
+ *
+ * ========================================================================
+ */
+'use strict';
-+function ($) {
- 'use strict';
- // TOOLTIP PUBLIC CLASS DEFINITION
- // ===============================
+/**
+ * Our tooltip class.
+ * @param {Element!} element
+ * @param {Object=} opt_config
+ * @constructor
+ */
+var Tooltip = function (element, opt_config) {
- var Tooltip = function (element, options) {
- this.type =
- this.options =
- this.enabled =
- this.timeout =
- this.hoverState =
- this.$element = null
+ /** @private {boolean} */
+ this._isEnabled = true
- this.init('tooltip', element, options)
- }
+ /** @private {number} */
+ this._timeout = 0
- Tooltip.VERSION = '3.3.2'
-
- Tooltip.TRANSITION_DURATION = 150
-
- Tooltip.DEFAULTS = {
- animation: true,
- placement: 'top',
- selector: false,
- template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
- trigger: 'hover focus',
- title: '',
- delay: 0,
- html: false,
- container: false,
- viewport: {
- selector: 'body',
- padding: 0
- }
- }
+ /** @private {string} */
+ this._hoverState = ''
- Tooltip.prototype.init = function (type, element, options) {
- this.enabled = true
- this.type = type
- this.$element = $(element)
- this.options = this.getOptions(options)
- this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)
+ /** @protected {Element} */
+ this.element = element
- var triggers = this.options.trigger.split(' ')
+ /** @protected {Object} */
+ this.config = this._getConfig(opt_config)
- for (var i = triggers.length; i--;) {
- var trigger = triggers[i]
+ /** @protected {Element} */
+ this.tip = null
- if (trigger == 'click') {
- this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
- } else if (trigger != 'manual') {
- var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
- var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
+ /** @protected {Element} */
+ this.arrow = null
- this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
- this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
- }
- }
+ if (this.config['viewport']) {
- this.options.selector ?
- (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
- this.fixTitle()
- }
+ /** @private {Element} */
+ this._viewport = $(this.config['viewport']['selector'] || this.config['viewport'])[0]
- Tooltip.prototype.getDefaults = function () {
- return Tooltip.DEFAULTS
}
- Tooltip.prototype.getOptions = function (options) {
- options = $.extend({}, this.getDefaults(), this.$element.data(), options)
+ this._setListeners()
+}
+
+
+/**
+ * @const
+ * @type {string}
+ */
+Tooltip['VERSION'] = '4.0.0'
+
+
+/**
+ * @const
+ * @type {Object}
+ */
+Tooltip['Defaults'] = {
+ 'container' : false,
+ 'animation' : true,
+ 'placement' : 'top',
+ 'selector' : false,
+ 'template' : '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
+ 'trigger' : 'hover focus',
+ 'title' : '',
+ 'delay' : 0,
+ 'html' : false,
+ 'viewport': {
+ 'selector': 'body',
+ 'padding' : 0
+ }
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @protected
+ */
+Tooltip.Direction = {
+ TOP: 'top',
+ LEFT: 'left',
+ RIGHT: 'right',
+ BOTTOM: 'bottom'
+}
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Tooltip._NAME = 'tooltip'
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+Tooltip._DATA_KEY = 'bs.tooltip'
+
+
+/**
+ * @const
+ * @type {number}
+ * @private
+ */
+Tooltip._TRANSITION_DURATION = 150
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Tooltip._HoverState = {
+ IN: 'in',
+ OUT: 'out'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Tooltip._Event = {
+ HIDE : 'hide.bs.tooltip',
+ HIDDEN : 'hidden.bs.tooltip',
+ SHOW : 'show.bs.tooltip',
+ SHOWN : 'shown.bs.tooltip'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Tooltip._ClassName = {
+ FADE : 'fade',
+ IN : 'in'
+}
+
+
+/**
+ * @const
+ * @enum {string}
+ * @private
+ */
+Tooltip._Selector = {
+ TOOLTIP : '.tooltip',
+ TOOLTIP_INNER : '.tooltip-inner',
+ TOOLTIP_ARROW : '.tooltip-arrow'
+}
+
+
+/**
+ * @const
+ * @type {Function}
+ * @private
+ */
+Tooltip._JQUERY_NO_CONFLICT = $.fn[Tooltip._NAME]
+
+
+/**
+ * @param {Object=} opt_config
+ * @this {jQuery}
+ * @return {jQuery}
+ * @private
+ */
+Tooltip._jQueryInterface = function (opt_config) {
+ return this.each(function () {
+ var data = $(this).data(Tooltip._DATA_KEY)
+ var config = typeof opt_config == 'object' ? opt_config : null
+
+ if (!data && opt_config == 'destroy') {
+ return
+ }
- if (options.delay && typeof options.delay == 'number') {
- options.delay = {
- show: options.delay,
- hide: options.delay
- }
+ if (!data) {
+ data = new Tooltip(this, config)
+ $(this).data(Tooltip._DATA_KEY, data)
}
- return options
+ if (typeof opt_config === 'string') {
+ data[opt_config]()
+ }
+ })
+}
+
+
+/**
+ * Enable tooltip
+ */
+Tooltip.prototype['enable'] = function () {
+ this._isEnabled = true
+}
+
+
+/**
+ * Disable tooltip
+ */
+Tooltip.prototype['disable'] = function () {
+ this._isEnabled = false
+}
+
+
+/**
+ * Toggle the tooltip enable state
+ */
+Tooltip.prototype['toggleEnabled'] = function () {
+ this._isEnabled = !this._isEnabled
+}
+
+/**
+ * Toggle the tooltips display
+ * @param {Event} opt_event
+ */
+Tooltip.prototype['toggle'] = function (opt_event) {
+ var context = this
+ var dataKey = this.getDataKey()
+
+ if (opt_event) {
+ context = $(opt_event.currentTarget).data(dataKey)
+
+ if (!context) {
+ context = new this.constructor(opt_event.currentTarget, this._getDelegateConfig())
+ $(opt_event.currentTarget).data(dataKey, context)
+ }
}
- Tooltip.prototype.getDelegateOptions = function () {
- var options = {}
- var defaults = this.getDefaults()
+ $(context.getTipElement()).hasClass(Tooltip._ClassName.IN) ?
+ context._leave(null, context) :
+ context._enter(null, context)
+}
- this._options && $.each(this._options, function (key, value) {
- if (defaults[key] != value) options[key] = value
- })
- return options
- }
+/**
+ * Remove tooltip functionality
+ */
+Tooltip.prototype['destroy'] = function () {
+ clearTimeout(this._timeout)
+ this['hide'](function () {
+ $(this.element)
+ .off(Tooltip._Selector.TOOLTIP)
+ .removeData(this.getDataKey())
+ }.bind(this))
+}
+
+
+/**
+ * Show the tooltip
+ * todo (fat): ~fuck~ this is a big function - refactor out all of positioning logic
+ * and replace with external lib
+ */
+Tooltip.prototype['show'] = function () {
+ var showEvent = $.Event(this.getEventObject().SHOW)
+
+ if (this.isWithContent() && this._isEnabled) {
+ $(this.element).trigger(showEvent)
- Tooltip.prototype.enter = function (obj) {
- var self = obj instanceof this.constructor ?
- obj : $(obj.currentTarget).data('bs.' + this.type)
+ var isInTheDom = $.contains(this.element.ownerDocument.documentElement, this.element)
- if (self && self.$tip && self.$tip.is(':visible')) {
- self.hoverState = 'in'
+ if (showEvent.isDefaultPrevented() || !isInTheDom) {
return
}
- if (!self) {
- self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
- $(obj.currentTarget).data('bs.' + this.type, self)
+ var tip = this.getTipElement()
+ var tipId = Bootstrap.getUID(this.getName())
+
+ tip.setAttribute('id', tipId)
+ this.element.setAttribute('aria-describedby', tipId)
+
+ this.setContent()
+
+ if (this.config['animation']) {
+ $(tip).addClass(Tooltip._ClassName.FADE)
}
- clearTimeout(self.timeout)
+ var placement = typeof this.config['placement'] == 'function' ?
+ this.config['placement'].call(this, tip, this.element) :
+ this.config['placement']
- self.hoverState = 'in'
+ var autoToken = /\s?auto?\s?/i
+ var isWithAutoPlacement = autoToken.test(placement)
- if (!self.options.delay || !self.options.delay.show) return self.show()
+ if (isWithAutoPlacement) {
+ placement = placement.replace(autoToken, '') || Tooltip.Direction.TOP
+ }
- self.timeout = setTimeout(function () {
- if (self.hoverState == 'in') self.show()
- }, self.options.delay.show)
- }
+ if (tip.parentNode && tip.parentNode.nodeType == Node.ELEMENT_NODE) {
+ tip.parentNode.removeChild(tip)
+ }
+
+ tip.style.top = 0
+ tip.style.left = 0
+ tip.style.display = 'block'
+
+ $(tip).addClass(Tooltip._NAME + '-' + placement)
- Tooltip.prototype.leave = function (obj) {
- var self = obj instanceof this.constructor ?
- obj : $(obj.currentTarget).data('bs.' + this.type)
+ $(tip).data(this.getDataKey(), this)
- if (!self) {
- self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
- $(obj.currentTarget).data('bs.' + this.type, self)
+ if (this.config['container']) {
+ $(this.config['container'])[0].appendChild(tip)
+ } else {
+ this.element.parentNode.insertBefore(tip, this.element.nextSibling)
}
- clearTimeout(self.timeout)
+ var position = this._getPosition()
+ var actualWidth = tip.offsetWidth
+ var actualHeight = tip.offsetHeight
+
+ var calculatedPlacement = this._getCalculatedAutoPlacement(isWithAutoPlacement, placement, position, actualWidth, actualHeight)
+ var calculatedOffset = this._getCalculatedOffset(calculatedPlacement, position, actualWidth, actualHeight)
- self.hoverState = 'out'
+ this._applyCalculatedPlacement(calculatedOffset, calculatedPlacement)
- if (!self.options.delay || !self.options.delay.hide) return self.hide()
+ var complete = function () {
+ var prevHoverState = this.hoverState
+ $(this.element).trigger(this.getEventObject().SHOWN)
+ this.hoverState = null
- self.timeout = setTimeout(function () {
- if (self.hoverState == 'out') self.hide()
- }, self.options.delay.hide)
+ if (prevHoverState == 'out') this._leave(null, this)
+ }.bind(this)
+
+ Bootstrap.transition && $(this._tip).hasClass(Tooltip._ClassName.FADE) ?
+ $(this._tip)
+ .one(Bootstrap.TRANSITION_END, complete)
+ .emulateTransitionEnd(Tooltip._TRANSITION_DURATION) :
+ complete()
}
+}
- Tooltip.prototype.show = function () {
- var e = $.Event('show.bs.' + this.type)
- if (this.hasContent() && this.enabled) {
- this.$element.trigger(e)
+/**
+ * Hide the tooltip breh
+ */
+Tooltip.prototype['hide'] = function (callback) {
+ var tip = this.getTipElement()
+ var hideEvent = $.Event(this.getEventObject().HIDE)
- var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
- if (e.isDefaultPrevented() || !inDom) return
- var that = this
+ var complete = function () {
+ if (this._hoverState != Tooltip._HoverState.IN) {
+ tip.parentNode.removeChild(tip)
+ }
- var $tip = this.tip()
+ this.element.removeAttribute('aria-describedby')
+ $(this.element).trigger(this.getEventObject().HIDDEN)
+
+ if (callback) {
+ callback()
+ }
+ }.bind(this)
- var tipId = this.getUID(this.type)
+ $(this.element).trigger(hideEvent)
- this.setContent()
- $tip.attr('id', tipId)
- this.$element.attr('aria-describedby', tipId)
+ if (hideEvent.isDefaultPrevented()) return
- if (this.options.animation) $tip.addClass('fade')
+ $(tip).removeClass(Tooltip._ClassName.IN)
- var placement = typeof this.options.placement == 'function' ?
- this.options.placement.call(this, $tip[0], this.$element[0]) :
- this.options.placement
+ if (Bootstrap.transition && $(this._tip).hasClass(Tooltip._ClassName.FADE)) {
+ $(tip)
+ .one(Bootstrap.TRANSITION_END, complete)
+ .emulateTransitionEnd(Tooltip._TRANSITION_DURATION)
+ } else {
+ complete()
+ }
- var autoToken = /\s?auto?\s?/i
- var autoPlace = autoToken.test(placement)
- if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
+ this._hoverState = ''
+}
- $tip
- .detach()
- .css({ top: 0, left: 0, display: 'block' })
- .addClass(this.type + '-' + placement)
- .data('bs.' + this.type, this)
- this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
+/**
+ * @return {string}
+ */
+Tooltip.prototype['getHoverState'] = function (callback) {
+ return this._hoverState
+}
- var pos = this.getPosition()
- var actualWidth = $tip[0].offsetWidth
- var actualHeight = $tip[0].offsetHeight
- if (autoPlace) {
- var origPlacement = placement
- var $container = this.options.container ? $(this.options.container) : this.$element.parent()
- var containerDim = this.getPosition($container)
+/**
+ * @return {string}
+ * @protected
+ */
+Tooltip.prototype.getName = function () {
+ return Tooltip._NAME
+}
- placement = placement == 'bottom' && pos.bottom + actualHeight > containerDim.bottom ? 'top' :
- placement == 'top' && pos.top - actualHeight < containerDim.top ? 'bottom' :
- placement == 'right' && pos.right + actualWidth > containerDim.width ? 'left' :
- placement == 'left' && pos.left - actualWidth < containerDim.left ? 'right' :
- placement
- $tip
- .removeClass(this.type + '-' + origPlacement)
- .addClass(this.type + '-' + placement)
- }
+/**
+ * @return {string}
+ * @protected
+ */
+Tooltip.prototype.getDataKey = function () {
+ return Tooltip._DATA_KEY
+}
- var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
- this.applyPlacement(calculatedOffset, placement)
+/**
+ * @return {Object}
+ * @protected
+ */
+Tooltip.prototype.getEventObject = function () {
+ return Tooltip._Event
+}
- var complete = function () {
- var prevHoverState = that.hoverState
- that.$element.trigger('shown.bs.' + that.type)
- that.hoverState = null
- if (prevHoverState == 'out') that.leave(that)
- }
+/**
+ * @return {string}
+ * @protected
+ */
+Tooltip.prototype.getTitle = function () {
+ var title = this.element.getAttribute('data-original-title')
- $.support.transition && this.$tip.hasClass('fade') ?
- $tip
- .one('bsTransitionEnd', complete)
- .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
- complete()
- }
+ if (!title) {
+ title = typeof this.config['title'] === 'function' ?
+ this.config['title'].call(this.element) :
+ this.config['title']
}
- Tooltip.prototype.applyPlacement = function (offset, placement) {
- var $tip = this.tip()
- var width = $tip[0].offsetWidth
- var height = $tip[0].offsetHeight
-
- // manually read margins because getBoundingClientRect includes difference
- var marginTop = parseInt($tip.css('margin-top'), 10)
- var marginLeft = parseInt($tip.css('margin-left'), 10)
-
- // we must check for NaN for IE9
- if (isNaN(marginTop)) marginTop = 0
- if (isNaN(marginLeft)) marginLeft = 0
-
- offset.top = offset.top + marginTop
- offset.left = offset.left + marginLeft
-
- // $.fn.offset doesn't round pixel values
- // so we use setOffset directly with our own function B-0
- $.offset.setOffset($tip[0], $.extend({
- using: function (props) {
- $tip.css({
- top: Math.round(props.top),
- left: Math.round(props.left)
- })
- }
- }, offset), 0)
-
- $tip.addClass('in')
-
- // check to see if placing tip in new offset caused the tip to resize itself
- var actualWidth = $tip[0].offsetWidth
- var actualHeight = $tip[0].offsetHeight
-
- if (placement == 'top' && actualHeight != height) {
- offset.top = offset.top + height - actualHeight
- }
+ return /** @type {string} */ (title)
+}
- var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
- if (delta.left) offset.left += delta.left
- else offset.top += delta.top
+/**
+ * @return {Element}
+ * @protected
+ */
+Tooltip.prototype.getTipElement = function () {
+ return (this._tip = this._tip || $(this.config['template'])[0])
+}
- var isVertical = /top|bottom/.test(placement)
- var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
- var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
- $tip.offset(offset)
- this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
- }
+/**
+ * @return {Element}
+ * @protected
+ */
+Tooltip.prototype.getArrowElement = function () {
+ return (this.arrow = this.arrow || $(this.getTipElement()).find(Tooltip._Selector.TOOLTIP_ARROW)[0])
+}
- Tooltip.prototype.replaceArrow = function (delta, dimension, isHorizontal) {
- this.arrow()
- .css(isHorizontal ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
- .css(isHorizontal ? 'top' : 'left', '')
- }
- Tooltip.prototype.setContent = function () {
- var $tip = this.tip()
- var title = this.getTitle()
+/**
+ * @return {boolean}
+ * @protected
+ */
+Tooltip.prototype.isWithContent = function () {
+ return !!this.getTitle()
+}
- $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
- $tip.removeClass('fade in tooltip-top tooltip-bottom tooltip-left tooltip-right')
- }
- Tooltip.prototype.hide = function (callback) {
- var that = this
- var $tip = this.tip()
- var e = $.Event('hide.bs.' + this.type)
-
- function complete() {
- if (that.hoverState != 'in') $tip.detach()
- that.$element
- .removeAttr('aria-describedby')
- .trigger('hidden.bs.' + that.type)
- callback && callback()
- }
+/**
+ * @protected
+ */
+Tooltip.prototype.setContent = function () {
+ var tip = this.getTipElement()
+ var title = this.getTitle()
- this.$element.trigger(e)
+ $(tip).find(Tooltip._Selector.TOOLTIP_INNER)[0][this.config['html'] ? 'innerHTML' : 'innerText'] = title
- if (e.isDefaultPrevented()) return
+ $(tip)
+ .removeClass(Tooltip._ClassName.FADE)
+ .removeClass(Tooltip._ClassName.IN)
- $tip.removeClass('in')
+ for (var direction in Tooltip.Direction) {
+ $(tip).removeClass(Tooltip._NAME + '-' + direction)
+ }
+}
- $.support.transition && this.$tip.hasClass('fade') ?
- $tip
- .one('bsTransitionEnd', complete)
- .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
- complete()
- this.hoverState = null
+/**
+ * @private
+ */
+Tooltip.prototype._setListeners = function () {
+ var triggers = this.config['trigger'].split(' ')
- return this
- }
+ triggers.forEach(function (trigger) {
+ if (trigger == 'click') {
+ $(this.element).on('click.bs.tooltip', this.config['selector'], this['toggle'].bind(this))
- Tooltip.prototype.fixTitle = function () {
- var $e = this.$element
- if ($e.attr('title') || typeof ($e.attr('data-original-title')) != 'string') {
- $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
+ } else if (trigger != 'manual') {
+ var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
+ var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
+
+ $(this.element)
+ .on(eventIn + '.bs.tooltip', this.config['selector'], this._enter.bind(this))
+ .on(eventOut + '.bs.tooltip', this.config['selector'], this._leave.bind(this))
}
- }
+ }.bind(this))
- Tooltip.prototype.hasContent = function () {
- return this.getTitle()
+ if (this.config['selector']) {
+ this.config = $.extend({}, this.config, { 'trigger': 'manual', 'selector': '' })
+ } else {
+ this._fixTitle()
}
+}
- Tooltip.prototype.getPosition = function ($element) {
- $element = $element || this.$element
- var el = $element[0]
- var isBody = el.tagName == 'BODY'
+/**
+ * @param {Object=} opt_config
+ * @return {Object}
+ * @private
+ */
+Tooltip.prototype._getConfig = function (opt_config) {
+ var config = $.extend({}, this.constructor['Defaults'], $(this.element).data(), opt_config)
- var elRect = el.getBoundingClientRect()
- if (elRect.width == null) {
- // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
- elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
+ if (config['delay'] && typeof config['delay'] == 'number') {
+ config['delay'] = {
+ 'show': config['delay'],
+ 'hide': config['delay']
}
- var elOffset = isBody ? { top: 0, left: 0 } : $element.offset()
- var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
- var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
-
- return $.extend({}, elRect, scroll, outerDims, elOffset)
}
- Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
- return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
- placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
- placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
- /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
+ return config
+}
- }
- Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
- var delta = { top: 0, left: 0 }
- if (!this.$viewport) return delta
-
- var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
- var viewportDimensions = this.getPosition(this.$viewport)
-
- if (/right|left/.test(placement)) {
- var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll
- var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
- if (topEdgeOffset < viewportDimensions.top) { // top overflow
- delta.top = viewportDimensions.top - topEdgeOffset
- } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
- delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
- }
- } else {
- var leftEdgeOffset = pos.left - viewportPadding
- var rightEdgeOffset = pos.left + viewportPadding + actualWidth
- if (leftEdgeOffset < viewportDimensions.left) { // left overflow
- delta.left = viewportDimensions.left - leftEdgeOffset
- } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
- delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
- }
+/**
+ * @return {Object}
+ * @private
+ */
+Tooltip.prototype._getDelegateConfig = function () {
+ var config = {}
+ var defaults = this.constructor['Defaults']
+
+ if (this.config) {
+ for (var key in this.config) {
+ var value = this.config[key]
+ if (defaults[key] != value) config[key] = value
}
+ }
+
+ return config
+}
+
+
+
+/**
+ * @param {boolean} isWithAutoPlacement
+ * @param {string} placement
+ * @param {Object} position
+ * @param {number} actualWidth
+ * @param {number} actualHeight
+ * @return {string}
+ * @private
+ */
+Tooltip.prototype._getCalculatedAutoPlacement = function (isWithAutoPlacement, placement, position, actualWidth, actualHeight) {
+ if (isWithAutoPlacement) {
+ var originalPlacement = placement
+ var container = this.config['container'] ? $(this.config['container'])[0] : this.element.parentNode
+ var containerDim = this._getPosition(/** @type {Element} */ (container))
+
+ placement = placement == Tooltip.Direction.BOTTOM && position.bottom + actualHeight > containerDim.bottom ? Tooltip.Direction.TOP :
+ placement == Tooltip.Direction.TOP && position.top - actualHeight < containerDim.top ? Tooltip.Direction.BOTTOM :
+ placement == Tooltip.Direction.RIGHT && position.right + actualWidth > containerDim.width ? Tooltip.Direction.LEFT :
+ placement == Tooltip.Direction.LEFT && position.left - actualWidth < containerDim.left ? Tooltip.Direction.RIGHT :
+ placement
+
+ $(this._tip)
+ .removeClass(Tooltip._NAME + '-' + originalPlacement)
+ .addClass(Tooltip._NAME + '-' + placement)
+ }
+ return placement
+}
+
+
+/**
+ * @param {string} placement
+ * @param {Object} position
+ * @param {number} actualWidth
+ * @param {number} actualHeight
+ * @return {{left: number, top: number}}
+ * @private
+ */
+Tooltip.prototype._getCalculatedOffset = function (placement, position, actualWidth, actualHeight) {
+ return placement == Tooltip.Direction.BOTTOM ? { top: position.top + position.height, left: position.left + position.width / 2 - actualWidth / 2 } :
+ placement == Tooltip.Direction.TOP ? { top: position.top - actualHeight, left: position.left + position.width / 2 - actualWidth / 2 } :
+ placement == Tooltip.Direction.LEFT ? { top: position.top + position.height / 2 - actualHeight / 2, left: position.left - actualWidth } :
+ /* placement == Tooltip.Direction.RIGHT */ { top: position.top + position.height / 2 - actualHeight / 2, left: position.left + position.width }
+}
+
+
+/**
+ * @param {string} placement
+ * @param {Object} position
+ * @param {number} actualWidth
+ * @param {number} actualHeight
+ * @return {Object}
+ * @private
+ */
+Tooltip.prototype._getViewportAdjustedDelta = function (placement, position, actualWidth, actualHeight) {
+ var delta = { top: 0, left: 0 }
+
+ if (!this._viewport) {
return delta
}
- Tooltip.prototype.getTitle = function () {
- var title
- var $e = this.$element
- var o = this.options
+ var viewportPadding = this.config['viewport'] && this.config['viewport']['padding'] || 0
+ var viewportDimensions = this._getPosition(this._viewport)
- title = $e.attr('data-original-title')
- || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
+ if (placement === Tooltip.Direction.RIGHT || placement === Tooltip.Direction.LEFT) {
+ var topEdgeOffset = position.top - viewportPadding - viewportDimensions.scroll
+ var bottomEdgeOffset = position.top + viewportPadding - viewportDimensions.scroll + actualHeight
- return title
+ if (topEdgeOffset < viewportDimensions.top) { // top overflow
+ delta.top = viewportDimensions.top - topEdgeOffset
+
+ } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
+ delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
+ }
+
+ } else {
+ var leftEdgeOffset = position.left - viewportPadding
+ var rightEdgeOffset = position.left + viewportPadding + actualWidth
+
+ if (leftEdgeOffset < viewportDimensions.left) { // left overflow
+ delta.left = viewportDimensions.left - leftEdgeOffset
+
+ } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
+ delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
+ }
}
- Tooltip.prototype.getUID = function (prefix) {
- do prefix += ~~(Math.random() * 1000000)
- while (document.getElementById(prefix))
- return prefix
+ return delta
+}
+
+
+/**
+ * @param {Element=} opt_element
+ * @return {Object}
+ * @private
+ */
+Tooltip.prototype._getPosition = function (opt_element) {
+ var element = opt_element || this.element
+ var isBody = element.tagName == 'BODY'
+ var rect = element.getBoundingClientRect()
+ var offset = isBody ? { top: 0, left: 0 } : $(element).offset()
+ var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : this.element.scrollTop }
+ var outerDims = isBody ? { width: window.innerWidth, height: window.innerHeight } : null
+
+ return $.extend({}, rect, scroll, outerDims, offset)
+}
+
+
+/**
+ * @param {{left: number, top: number}} offset
+ * @param {string} placement
+ * @private
+ */
+Tooltip.prototype._applyCalculatedPlacement = function (offset, placement) {
+ var tip = this.getTipElement()
+ var width = tip.offsetWidth
+ var height = tip.offsetHeight
+
+ // manually read margins because getBoundingClientRect includes difference
+ var marginTop = parseInt(tip.style.marginTop, 10)
+ var marginLeft = parseInt(tip.style.marginLeft, 10)
+
+ // we must check for NaN for ie 8/9
+ if (isNaN(marginTop)) {
+ marginTop = 0
+ }
+ if (isNaN(marginLeft)) {
+ marginLeft = 0
}
- Tooltip.prototype.tip = function () {
- return (this.$tip = this.$tip || $(this.options.template))
+ offset.top = offset.top + marginTop
+ offset.left = offset.left + marginLeft
+
+ // $.fn.offset doesn't round pixel values
+ // so we use setOffset directly with our own function B-0
+ $.offset.setOffset(tip, $.extend({
+ using: function (props) {
+ tip.style.top = Math.round(props.top) + 'px'
+ tip.style.left = Math.round(props.left) + 'px'
+ }
+ }, offset), 0)
+
+ $(tip).addClass(Tooltip._ClassName.IN)
+
+ // check to see if placing tip in new offset caused the tip to resize itself
+ var actualWidth = tip.offsetWidth
+ var actualHeight = tip.offsetHeight
+
+ if (placement == Tooltip.Direction.TOP && actualHeight != height) {
+ offset.top = offset.top + height - actualHeight
}
- Tooltip.prototype.arrow = function () {
- return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
+ var delta = this._getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
+
+ if (delta.left) {
+ offset.left += delta.left
+ } else {
+ offset.top += delta.top
}
- Tooltip.prototype.enable = function () {
- this.enabled = true
+ var isVertical = placement === Tooltip.Direction.TOP || placement === Tooltip.Direction.BOTTOM
+ var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
+ var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
+
+ $(tip).offset(offset)
+
+ this._replaceArrow(arrowDelta, tip[arrowOffsetPosition], isVertical)
+}
+
+
+/**
+ * @param {number} delta
+ * @param {number} dimension
+ * @param {boolean} isHorizontal
+ * @private
+ */
+Tooltip.prototype._replaceArrow = function (delta, dimension, isHorizontal) {
+ var arrow = this.getArrowElement()
+
+ arrow.style[isHorizontal ? 'left' : 'top'] = 50 * (1 - delta / dimension) + '%'
+ arrow.style[isHorizontal ? 'top' : 'left'] = ''
+}
+
+
+
+/**
+ * @private
+ */
+Tooltip.prototype._fixTitle = function () {
+ if (this.element.getAttribute('title') || typeof this.element.getAttribute('data-original-title') != 'string') {
+ this.element.setAttribute('data-original-title', this.element.getAttribute('title') || '')
+ this.element.setAttribute('title', '')
}
+}
+
- Tooltip.prototype.disable = function () {
- this.enabled = false
+/**
+ * @param {Event=} opt_event
+ * @param {Object=} opt_context
+ * @private
+ */
+Tooltip.prototype._enter = function (opt_event, opt_context) {
+ var dataKey = this.getDataKey()
+ var context = opt_context || $(opt_event.currentTarget).data(dataKey)
+
+ if (context && context._tip && context._tip.offsetWidth) {
+ context._hoverState = Tooltip._HoverState.IN
+ return
}
- Tooltip.prototype.toggleEnabled = function () {
- this.enabled = !this.enabled
+ if (!context) {
+ context = new this.constructor(opt_event.currentTarget, this._getDelegateConfig())
+ $(opt_event.currentTarget).data(dataKey, context)
}
- Tooltip.prototype.toggle = function (e) {
- var self = this
- if (e) {
- self = $(e.currentTarget).data('bs.' + this.type)
- if (!self) {
- self = new this.constructor(e.currentTarget, this.getDelegateOptions())
- $(e.currentTarget).data('bs.' + this.type, self)
- }
- }
+ clearTimeout(context._timeout)
- self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
- }
+ context._hoverState = Tooltip._HoverState.IN
- Tooltip.prototype.destroy = function () {
- var that = this
- clearTimeout(this.timeout)
- this.hide(function () {
- that.$element.off('.' + that.type).removeData('bs.' + that.type)
- })
+ if (!context.config['delay'] || !context.config['delay']['show']) {
+ context['show']()
+ return
}
+ context._timeout = setTimeout(function () {
+ if (context._hoverState == Tooltip._HoverState.IN) {
+ context['show']()
+ }
+ }, context.config['delay']['show'])
+}
+
+
+/**
+ * @param {Event=} opt_event
+ * @param {Object=} opt_context
+ * @private
+ */
+Tooltip.prototype._leave = function (opt_event, opt_context) {
+ var dataKey = this.getDataKey()
+ var context = opt_context || $(opt_event.currentTarget).data(dataKey)
+
+ if (!context) {
+ context = new this.constructor(opt_event.currentTarget, this._getDelegateConfig())
+ $(opt_event.currentTarget).data(dataKey, context)
+ }
- // TOOLTIP PLUGIN DEFINITION
- // =========================
+ clearTimeout(context._timeout)
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.tooltip')
- var options = typeof option == 'object' && option
+ context._hoverState = Tooltip._HoverState.OUT
- if (!data && option == 'destroy') return
- if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
- if (typeof option == 'string') data[option]()
- })
+ if (!context.config['delay'] || !context.config['delay']['hide']) {
+ context['hide']()
+ return
}
- var old = $.fn.tooltip
+ context._timeout = setTimeout(function () {
+ if (context._hoverState == Tooltip._HoverState.OUT) {
+ context['hide']()
+ }
+ }, context.config['delay']['hide'])
+}
- $.fn.tooltip = Plugin
- $.fn.tooltip.Constructor = Tooltip
- // TOOLTIP NO CONFLICT
- // ===================
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface + noConflict implementaiton
+ * ------------------------------------------------------------------------
+ */
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Tooltip._NAME] = Tooltip._jQueryInterface
+
+
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Tooltip._NAME]['Constructor'] = Tooltip
- $.fn.tooltip.noConflict = function () {
- $.fn.tooltip = old
- return this
- }
-}(jQuery);
+/**
+ * @const
+ * @type {Function}
+ */
+$.fn[Tooltip._NAME]['noConflict'] = function () {
+ $.fn[Tooltip._NAME] = Tooltip._JQUERY_NO_CONFLICT
+ return this
+}
diff --git a/js/util.js b/js/util.js
new file mode 100644
index 0000000000..294a5a9601
--- /dev/null
+++ b/js/util.js
@@ -0,0 +1,165 @@
+/** =======================================================================
+ * Bootstrap: util.js v4.0.0
+ * http://getbootstrap.com/javascript/#alerts
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ========================================================================
+ * @fileoverview - Bootstrap's private util helper. Adds private util
+ * helpers for things like accesibility and transitions. These methods are
+ * shared across all bootstrap plugins.
+ * ========================================================================
+ */
+
+'use strict';
+
+
+/**
+ * @type {Object}
+ */
+var Bootstrap = {}
+
+
+/**
+ * @const
+ * @type {string}
+ */
+Bootstrap.TRANSITION_END = 'bsTransitionEnd'
+
+
+/**
+ * @const
+ * @type {Object}
+ */
+Bootstrap.TransitionEndEvent = {
+ 'WebkitTransition' : 'webkitTransitionEnd',
+ 'MozTransition' : 'transitionend',
+ 'OTransition' : 'oTransitionEnd otransitionend',
+ 'transition' : 'transitionend'
+}
+
+
+/**
+ * @param {Function} childConstructor
+ * @param {Function} parentConstructor
+ */
+Bootstrap.inherits = function(childConstructor, parentConstructor) {
+ /** @constructor */
+ function tempConstructor() {}
+ tempConstructor.prototype = parentConstructor.prototype
+ childConstructor.prototype = new tempConstructor()
+ /** @override */
+ childConstructor.prototype.constructor = childConstructor
+}
+
+
+/**
+ * @param {Element} element
+ * @return {string|null}
+ */
+Bootstrap.getSelectorFromElement = function (element) {
+ var selector = element.getAttribute('data-target')
+
+ if (!selector) {
+ selector = element.getAttribute('href') || ''
+ selector = /^#[a-z]/i.test(selector) ? selector : null
+ }
+
+ return selector
+}
+
+
+/**
+ * @param {string} prefix
+ * @return {string}
+ */
+Bootstrap.getUID = function (prefix) {
+ do prefix += ~~(Math.random() * 1000000)
+ while (document.getElementById(prefix))
+ return prefix
+}
+
+
+/**
+ * @return {Object}
+ */
+Bootstrap.getSpecialTransitionEndEvent = function () {
+ return {
+ bindType: Bootstrap.transition.end,
+ delegateType: Bootstrap.transition.end,
+ handle: /** @param {jQuery.Event} event */ (function (event) {
+ if ($(event.target).is(this)) {
+ return event.handleObj.handler.apply(this, arguments)
+ }
+ })
+ }
+}
+
+
+/**
+ * @param {Element} element
+ */
+Bootstrap.reflow = function (element) {
+ new Function('bs',"return bs")(element.offsetHeight)
+}
+
+
+/**
+ * @return {Object|boolean}
+ */
+Bootstrap.transitionEndTest = function () {
+ if (window['QUnit']) {
+ return false
+ }
+
+ var el = document.createElement('bootstrap')
+ for (var name in Bootstrap.TransitionEndEvent) {
+ if (el.style[name] !== undefined) {
+ return { end: Bootstrap.TransitionEndEvent[name] }
+ }
+ }
+ return false
+}
+
+
+/**
+ * @param {number} duration
+ * @this {Element}
+ * @return {Object}
+ */
+Bootstrap.transitionEndEmulator = function (duration) {
+ var called = false
+
+ $(this).one(Bootstrap.TRANSITION_END, function () {
+ called = true
+ })
+
+ var callback = function () {
+ if (!called) {
+ $(this).trigger(Bootstrap.transition.end)
+ }
+ }.bind(this)
+
+ setTimeout(callback, duration)
+
+ return this
+}
+
+
+/**
+ * ------------------------------------------------------------------------
+ * jQuery Interface
+ * ------------------------------------------------------------------------
+ */
+
+$.fn.emulateTransitionEnd = Bootstrap.transitionEndEmulator
+
+$(function () {
+ Bootstrap.transition = Bootstrap.transitionEndTest()
+
+ if (!Bootstrap.transition) {
+ return
+ }
+
+ $.event.special[Bootstrap.TRANSITION_END] = Bootstrap.getSpecialTransitionEndEvent()
+})
diff --git a/package.json b/package.json
index 67bf080a83..95886665f8 100644
--- a/package.json
+++ b/package.json
@@ -36,8 +36,9 @@
"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-compress": "^0.13.0",
"grunt-contrib-concat": "~0.5.0",
"grunt-contrib-connect": "~0.9.0",
"grunt-contrib-copy": "~0.7.0",
@@ -63,6 +64,8 @@
"markdown-it": "^3.0.4",
"mq4-hover-shim": "^0.1.0",
"npm-shrinkwrap": "^200.0.0",
+ "remarkable": "~1.6.0",
+ "superstartup-closure-compiler": "^0.1.6",
"time-grunt": "~1.0.0"
},
"engines": {