/*!
 * Bootstrap v4.0.0-alpha (http://getbootstrap.com)
 * Copyright 2011-2015 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 */

if (typeof jQuery === 'undefined') {
  throw new Error('Bootstrap\'s JavaScript requires jQuery')
}

+function ($) {
  var version = $.fn.jquery.split(' ')[0].split('.')
  if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1)) {
    throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher')
  }
}(jQuery);

/** =======================================================================
 * 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()
})

/** =======================================================================
 * 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
 *
 * ========================================================================
 */

'use strict';


/**
 * Our Alert class.
 * @param {Element=} opt_element
 * @constructor
 */
var Alert = function (opt_element) {
  if (opt_element) {
    $(opt_element).on('click', Alert._DISMISS_SELECTOR, Alert._handleDismiss(this))
  }
}


/**
 * @const
 * @type {string}
 */
Alert['VERSION'] = '4.0.0'


/**
 * @const
 * @type {string}
 * @private
 */
Alert._NAME = 'alert'


/**
 * @const
 * @type {string}
 * @private
 */
Alert._DATA_KEY = 'bs.alert'


/**
 * @const
 * @type {string}
 * @private
 */
Alert._DISMISS_SELECTOR = '[data-dismiss="alert"]'


/**
 * @const
 * @type {number}
 * @private
 */
Alert._TRANSITION_DURATION = 150


/**
 * @const
 * @type {Function}
 * @private
 */
Alert._JQUERY_NO_CONFLICT = $.fn[Alert._NAME]


/**
 * @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 (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)
}


/**
 * 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]
  }

  if (!parent) {
    parent = $(element).closest('.' + Alert._ClassName.ALERT)[0]
  }

  return parent
}


/**
 * Trigger close event on element
 * @return {$.Event}
 * @private
 */
Alert.prototype._triggerCloseEvent = function (element) {
  var closeEvent = $.Event(Alert._Event.CLOSE)
  $(element).trigger(closeEvent)
  return closeEvent
}


/**
 * 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
}


/**
 * ------------------------------------------------------------------------
 * 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';


/**
 * Our Button class.
 * @param {Element!} element
 * @constructor
 */
var Button = function (element) {

  /** @private {Element} */
  this._element = element

}


/**
 * @const
 * @type {string}
 */
Button['VERSION']  = '4.0.0'


/**
 * @const
 * @type {string}
 * @private
 */
Button._NAME  = 'button'


/**
 * @const
 * @type {string}
 * @private
 */
Button._DATA_KEY = 'bs.button'


/**
 * @const
 * @type {Function}
 * @private
 */
Button._JQUERY_NO_CONFLICT = $.fn[Button._NAME]


/**
 * @const
 * @enum {string}
 * @private
 */
Button._ClassName = {
  ACTIVE : 'active',
  BUTTON : 'btn',
  FOCUS  : 'focus'
}


/**
 * @const
 * @enum {string}
 * @private
 */
Button._Selector = {
  DATA_TOGGLE_CARROT : '[data-toggle^="button"]',
  DATA_TOGGLE        : '[data-toggle="buttons"]',
  INPUT              : 'input',
  ACTIVE             : '.active',
  BUTTON             : '.btn'
}


/**
 * Provides the jQuery Interface for the Button component.
 * @param {string=} opt_config
 * @this {jQuery}
 * @return {jQuery}
 * @private
 */
Button._jQueryInterface = function (opt_config) {
  return this.each(function () {
    var data  = $(this).data(Button._DATA_KEY)

    if (!data) {
      data = new Button(this)
      $(this).data(Button._DATA_KEY, data)
    }

    if (opt_config === 'toggle') {
      data[opt_config]()
    }
  })
}


/**
 * Toggle's the button active state
 */
Button.prototype['toggle'] = function () {
  var triggerChangeEvent = true
  var rootElement = $(this._element).closest(Button._Selector.DATA_TOGGLE)[0]

  if (rootElement) {
    var input = $(this._element).find(Button._Selector.INPUT)[0]
    if (input) {
      if (input.type == 'radio') {
        if (input.checked && $(this._element).hasClass(Button._ClassName.ACTIVE)) {
          triggerChangeEvent = false
        } else {
          var activeElement = $(rootElement).find(Button._Selector.ACTIVE)[0]
          if (activeElement) {
            $(activeElement).removeClass(Button._ClassName.ACTIVE)
          }
        }
      }

      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)
  }
}


/**
 * ------------------------------------------------------------------------
 * jQuery Interface + noConflict implementaiton
 * ------------------------------------------------------------------------
 */

/**
 * @const
 * @type {Function}
 */
$.fn[Button._NAME] = Button._jQueryInterface


/**
 * @const
 * @type {Function}
 */
$.fn[Button._NAME]['Constructor'] = Button


/**
 * @const
 * @type {Function}
 */
$.fn[Button._NAME]['noConflict'] = function () {
  $.fn[Button._NAME] = Button._JQUERY_NO_CONFLICT
  return this
}


/**
 * ------------------------------------------------------------------------
 * Data Api implementation
 * ------------------------------------------------------------------------
 */

$(document)
  .on('click.bs.button.data-api', Button._Selector.DATA_TOGGLE_CARROT, function (event) {
    event.preventDefault()

    var button = event.target

    if (!$(button).hasClass(Button._ClassName.BUTTON)) {
      button = $(button).closest(Button._Selector.BUTTON)
    }

    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 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

  /** @private {boolean} */
  this._isSliding = false

  /** @private {?number} */
  this._interval = null

  /** @private {?Element} */
  this._activeElement = null

  /** @private {?Array} */
  this._items = null

  this._addEventListeners()

}


/**
 * @const
 * @type {string}
 */
Carousel['VERSION'] = '4.0.0'


/**
 * @const
 * @type {Object}
 */
Carousel['Defaults'] = {
  'interval' : 5000,
  'pause'    : 'hover',
  'wrap'     : true,
  'keyboard' : true,
  'slide'    : false
}


/**
 * @const
 * @type {string}
 * @private
 */
Carousel._NAME  = 'carousel'


/**
 * @const
 * @type {string}
 * @private
 */
Carousel._DATA_KEY = 'bs.carousel'


/**
 * @const
 * @type {number}
 * @private
 */
Carousel._TRANSITION_DURATION = 600


/**
 * @const
 * @enum {string}
 * @private
 */
Carousel._Direction = {
  NEXT     : 'next',
  PREVIOUS : 'prev'
}


/**
 * @const
 * @enum {string}
 * @private
 */
Carousel._Event = {
  SLIDE : 'slide.bs.carousel',
  SLID  : 'slid.bs.carousel'
}


/**
 * @const
 * @enum {string}
 * @private
 */
Carousel._ClassName = {
  CAROUSEL : 'carousel',
  ACTIVE   : 'active',
  SLIDE    : 'slide',
  RIGHT    : 'right',
  LEFT     : 'left',
  ITEM     : 'carousel-item'
}


/**
 * @const
 * @enum {string}
 * @private
 */
Carousel._Selector = {
  ACTIVE      : '.active',
  ACTIVE_ITEM : '.active.carousel-item',
  ITEM        : '.carousel-item',
  NEXT_PREV   : '.next, .prev',
  INDICATORS  : '.carousel-indicators'
}


/**
 * @const
 * @type {Function}
 * @private
 */
Carousel._JQUERY_NO_CONFLICT = $.fn[Carousel._NAME]


/**
 * @param {Object=} opt_config
 * @this {jQuery}
 * @return {jQuery}
 * @private
 */
Carousel._jQueryInterface = function (opt_config) {
  return this.each(function () {
    var data   = $(this).data(Carousel._DATA_KEY)
    var config = $.extend({}, Carousel['Defaults'], $(this).data(), typeof opt_config == 'object' && opt_config)
    var action = typeof opt_config == 'string' ? opt_config : config.slide

    if (!data) {
      data = new Carousel(this, config)
      $(this).data(Carousel._DATA_KEY, data)
    }

    if (typeof opt_config == 'number') {
      data.to(opt_config)

    } else if (action) {
      data[action]()

    } else if (config.interval) {
      data['pause']()
      data['cycle']()
    }
  })
}


/**
 * Click handler for data api
 * @param {Event} event
 * @this {Element}
 * @private
 */
Carousel._dataApiClickHandler = function (event) {
  var selector = Bootstrap.getSelectorFromElement(this)

  if (!selector) {
    return
  }

  var target = $(selector)[0]

  if (!target || !$(target).hasClass(Carousel._ClassName.CAROUSEL)) {
    return
  }

  var config = $.extend({}, $(target).data(), $(this).data())

  var slideIndex = this.getAttribute('data-slide-to')
  if (slideIndex) {
    config.interval = false
  }

  Carousel._jQueryInterface.call($(target), config)

  if (slideIndex) {
    $(target).data(Carousel._DATA_KEY).to(slideIndex)
  }

  event.preventDefault()
}


/**
 * Advance the carousel to the next slide
 */
Carousel.prototype['next'] = function () {
  if (!this._isSliding) {
    this._slide(Carousel._Direction.NEXT)
  }
}


/**
 * Return the carousel to the previous slide
 */
Carousel.prototype['prev'] = function () {
  if (!this._isSliding) {
    this._slide(Carousel._Direction.PREVIOUS)
  }
}


/**
 * Pause the carousel cycle
 * @param {Event=} opt_event
 */
Carousel.prototype['pause'] = function (opt_event) {
  if (!opt_event) {
    this._isPaused = true
  }

  if ($(this._element).find(Carousel._Selector.NEXT_PREV)[0] && Bootstrap.transition) {
    $(this._element).trigger(Bootstrap.transition.end)
    this['cycle'](true)
  }

  clearInterval(this._interval)
  this._interval = null
}


/**
 * Cycle to the next carousel item
 * @param {Event|boolean=} opt_event
 */
Carousel.prototype['cycle'] = function (opt_event) {
  if (!opt_event) {
    this._isPaused = false
  }

  if (this._interval) {
    clearInterval(this._interval)
    this._interval = null
  }

  if (this._config['interval'] && !this._isPaused) {
    this._interval = setInterval(this['next'].bind(this), this._config['interval'])
  }
}


/**
 * @return {Object}
 */
Carousel.prototype['getConfig'] = function () {
  return this._config
}


/**
 * 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]

  var activeIndex = this._getItemIndex(this._activeElement)

  if (index > (this._items.length - 1) || index < 0) {
    return
  }

  if (this._isSliding) {
    $(this._element).one(Carousel._Event.SLID, function () { this.to(index) }.bind(this))
    return
  }

  if (activeIndex == index) {
    this['pause']()
    this['cycle']()
    return
  }

  var direction = index > activeIndex ?
    Carousel._Direction.NEXT :
    Carousel._Direction.PREVIOUS

  this._slide(direction, this._items[index])
}


/**
 * 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))
  }

  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))
  }
}


/**
 * Keydown handler
 * @param {Event} event
 * @private
 */
Carousel.prototype._keydown = function (event) {
  event.preventDefault()

  if (/input|textarea/i.test(event.target.tagName)) return

  switch (event.which) {
    case 37: this['prev'](); break
    case 39: this['next'](); break
    default: return
  }
}


/**
 * Get item index
 * @param {Element} element
 * @return {number}
 * @private
 */
Carousel.prototype._getItemIndex = function (element) {
  this._items = $.makeArray($(element).parent().find(Carousel._Selector.ITEM))

  return this._items.indexOf(element)
}


/**
 * Get next displayed item based on direction
 * @param {Carousel._Direction} direction
 * @param {Element} activeElement
 * @return {Element}
 * @private
 */
Carousel.prototype._getItemByDirection = function (direction, activeElement) {
  var activeIndex   = this._getItemIndex(activeElement)
  var isGoingToWrap = (direction === Carousel._Direction.PREVIOUS && activeIndex === 0) ||
                      (direction === Carousel._Direction.NEXT && activeIndex == (this._items.length - 1))

  if (isGoingToWrap && !this._config['wrap']) {
    return activeElement
  }

  var delta     = direction == Carousel._Direction.PREVIOUS ? -1 : 1
  var itemIndex = (activeIndex + delta) % this._items.length

  return itemIndex === -1 ? this._items[this._items.length - 1] : this._items[itemIndex]
}


/**
 * Trigger slide event on element
 * @param {Element} relatedTarget
 * @param {Carousel._ClassName} directionalClassname
 * @return {$.Event}
 * @private
 */
Carousel.prototype._triggerSlideEvent = function (relatedTarget, directionalClassname) {
  var slideEvent = $.Event(Carousel._Event.SLIDE, {
    relatedTarget: relatedTarget,
    direction: directionalClassname
  })

  $(this._element).trigger(slideEvent)

  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 directionalClassName = direction == Carousel._Direction.NEXT ?
    Carousel._ClassName.LEFT :
    Carousel._ClassName.RIGHT

  if (nextElement && $(nextElement).hasClass(Carousel._ClassName.ACTIVE)) {
    this._isSliding = false
    return
  }

  var slideEvent = this._triggerSlideEvent(nextElement, directionalClassName)
  if (slideEvent.isDefaultPrevented()) {
    return
  }

  if (!activeElement || !nextElement) {
    // some weirdness is happening, so we bail (maybe throw exception here alerting user that they're dom is off
    return
  }

  this._isSliding = true

  if (isCycling) {
    this['pause']()
  }

  this._setActiveIndicatorElement(nextElement)

  var slidEvent = $.Event(Carousel._Event.SLID, { relatedTarget: nextElement, direction: directionalClassName })

  if (Bootstrap.transition && $(this._element).hasClass(Carousel._ClassName.SLIDE)) {
    $(nextElement).addClass(direction)

    Bootstrap.reflow(nextElement)

    $(activeElement).addClass(directionalClassName)
    $(nextElement).addClass(directionalClassName)

    $(activeElement)
      .one(Bootstrap.TRANSITION_END, function () {
        $(nextElement)
          .removeClass(directionalClassName)
          .removeClass(direction)

        $(nextElement).addClass(Carousel._ClassName.ACTIVE)

        $(activeElement)
          .removeClass(Carousel._ClassName.ACTIVE)
          .removeClass(direction)
          .removeClass(directionalClassName)

        this._isSliding = false

        setTimeout(function () {
          $(this._element).trigger(slidEvent)
        }.bind(this), 0)
      }.bind(this))
      .emulateTransitionEnd(Carousel._TRANSITION_DURATION)

  } else {
    $(activeElement).removeClass(Carousel._ClassName.ACTIVE)
    $(nextElement).addClass(Carousel._ClassName.ACTIVE)

    this._isSliding = false
    $(this._element).trigger(slidEvent)
  }

  if (isCycling) {
    this['cycle']()
  }
}


/**
 * ------------------------------------------------------------------------
 * jQuery Interface + noConflict implementaiton
 * ------------------------------------------------------------------------
 */

/**
 * @const
 * @type {Function}
 */
$.fn[Carousel._NAME] = Carousel._jQueryInterface


/**
 * @const
 * @type {Function}
 */
$.fn[Carousel._NAME]['Constructor'] = Carousel


/**
 * @const
 * @type {Function}
 */
$.fn[Carousel._NAME]['noConflict'] = function () {
  $.fn[Carousel._NAME] = Carousel._JQUERY_NO_CONFLICT
  return this
}


/**
 * ------------------------------------------------------------------------
 * Data Api implementation
 * ------------------------------------------------------------------------
 */

$(document)
  .on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', Carousel._dataApiClickHandler)

$(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';


/**
 * Our collapse class.
 * @param {Element!} element
 * @param {Object=} opt_config
 * @constructor
 */
var Collapse = function (element, opt_config) {

  /** @private {Element} */
  this._element  = element

  /** @private {Object} */
  this._config = $.extend({}, Collapse['Defaults'], opt_config)

  /** @private {Element} */
  this._trigger = typeof this._config['trigger'] == 'string' ?
    $(this._config['trigger'])[0] : this._config['trigger']

  /** @private {boolean} */
  this._isTransitioning = false

  /** @private {?Element} */
  this._parent = this._config['parent'] ? this._getParent() : null

  if (!this._config['parent']) {
    this._addAriaAndCollapsedClass(this._element, this._trigger)
  }

  if (this._config['toggle']) {
    this['toggle']()
  }

}


/**
 * @const
 * @type {string}
 */
Collapse['VERSION'] = '4.0.0'


/**
 * @const
 * @type {Object}
 */
Collapse['Defaults'] = {
  'toggle'  : true,
  'trigger' : '[data-toggle="collapse"]',
  'parent'  : null
}


/**
 * @const
 * @type {string}
 * @private
 */
Collapse._NAME = 'collapse'


/**
 * @const
 * @type {string}
 * @private
 */
Collapse._DATA_KEY = 'bs.collapse'


/**
 * @const
 * @type {number}
 * @private
 */
Collapse._TRANSITION_DURATION = 600


/**
 * @const
 * @type {Function}
 * @private
 */
Collapse._JQUERY_NO_CONFLICT = $.fn[Collapse._NAME]


/**
 * @const
 * @enum {string}
 * @private
 */
Collapse._Event = {
  SHOW   : 'show.bs.collapse',
  SHOWN  : 'shown.bs.collapse',
  HIDE   : 'hide.bs.collapse',
  HIDDEN : 'hidden.bs.collapse'
}


/**
 * @const
 * @enum {string}
 * @private
 */
Collapse._ClassName = {
  IN         : 'in',
  COLLAPSE   : 'collapse',
  COLLAPSING : 'collapsing',
  COLLAPSED  : 'collapsed'
}


/**
 * @const
 * @enum {string}
 * @private
 */
Collapse._Dimension = {
  WIDTH  : 'width',
  HEIGHT : 'height'
}


/**
 * @const
 * @enum {string}
 * @private
 */
Collapse._Selector = {
  ACTIVES : '.panel > .in, .panel > .collapsing'
}


/**
 * Provides the jQuery Interface for the alert component.
 * @param {Object|string=} opt_config
 * @this {jQuery}
 * @return {jQuery}
 * @private
 */
Collapse._jQueryInterface = function (opt_config) {
  return this.each(function () {
    var $this   = $(this)
    var data    = $this.data(Collapse._DATA_KEY)
    var config = $.extend({}, Collapse['Defaults'], $this.data(), typeof opt_config == 'object' && opt_config)

    if (!data && config['toggle'] && opt_config == 'show') {
      config['toggle'] = false
    }

    if (!data) {
      data = new Collapse(this, config)
      $this.data(Collapse._DATA_KEY, data)
    }

    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
}


/**
 * 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']()
  }
}


/**
 * Show's the collapsing element
 */
Collapse.prototype['show'] = function () {
  if (this._isTransitioning || $(this._element).hasClass(Collapse._ClassName.IN)) {
    return
  }

  var activesData, actives

  if (this._parent) {
    actives = $.makeArray($(Collapse._Selector.ACTIVES))
    if (!actives.length) {
      actives = null
    }
  }

  if (actives) {
    activesData = $(actives).data(Collapse._DATA_KEY)
    if (activesData && activesData._isTransitioning) {
      return
    }
  }

  var startEvent = $.Event(Collapse._Event.SHOW)
  $(this._element).trigger(startEvent)
  if (startEvent.isDefaultPrevented()) {
    return
  }

  if (actives) {
    Collapse._jQueryInterface.call($(actives), 'hide')
    if (!activesData) {
      $(actives).data(Collapse._DATA_KEY, null)
    }
  }

  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)
  }

  this['setTransitioning'](true)

  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
  }

  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
  }

  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)
  }

  this['setTransitioning'](true)

  var complete = function () {
    this['setTransitioning'](false)
    $(this._element)
      .removeClass(Collapse._ClassName.COLLAPSING)
      .addClass(Collapse._ClassName.COLLAPSE)
      .trigger(Collapse._Event.HIDDEN)

  }.bind(this)

  this._element.style[dimension] = 0

  if (!Bootstrap.transition) {
    return complete()
  }

  $(this._element)
    .one(Bootstrap.TRANSITION_END, complete)
    .emulateTransitionEnd(Collapse._TRANSITION_DURATION)
}



/**
 * @param {boolean} isTransitioning
 */
Collapse.prototype['setTransitioning'] = function (isTransitioning) {
  this._isTransitioning = isTransitioning
}


/**
 * Returns the collapsing dimension
 * @return {string}
 * @private
 */
Collapse.prototype._getDimension = function () {
  var hasWidth = $(this._element).hasClass(Collapse._Dimension.WIDTH)
  return hasWidth ? Collapse._Dimension.WIDTH : Collapse._Dimension.HEIGHT
}


/**
 * Returns the parent element
 * @return {Element}
 * @private
 */
Collapse.prototype._getParent = function () {
  var selector = '[data-toggle="collapse"][data-parent="' + this._config['parent'] + '"]'
  var parent = $(this._config['parent'])[0]
  var elements = /** @type {Array.<Element>} */ ($.makeArray($(parent).find(selector)))

  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';


/**
 * Our dropdown class.
 * @param {Element!} element
 * @constructor
 */
var Dropdown = function (element) {
  $(element).on('click.bs.dropdown', this['toggle'])
}


/**
 * @const
 * @type {string}
 */
Dropdown['VERSION'] = '4.0.0'


/**
 * @const
 * @type {string}
 * @private
 */
Dropdown._NAME = 'dropdown'


/**
 * @const
 * @type {string}
 * @private
 */
Dropdown._DATA_KEY = 'bs.dropdown'


/**
 * @const
 * @type {Function}
 * @private
 */
Dropdown._JQUERY_NO_CONFLICT = $.fn[Dropdown._NAME]


/**
 * @const
 * @enum {string}
 * @private
 */
Dropdown._Event = {
  HIDE   : 'hide.bs.dropdown',
  HIDDEN : 'hidden.bs.dropdown',
  SHOW   : 'show.bs.dropdown',
  SHOWN  : 'shown.bs.dropdown'
}


/**
 * @const
 * @enum {string}
 * @private
 */
Dropdown._ClassName = {
  BACKDROP : 'dropdown-backdrop',
  DISABLED : 'disabled',
  OPEN     : 'open'
}


/**
 * @const
 * @enum {string}
 * @private
 */
Dropdown._Selector = {
  BACKDROP      : '.dropdown-backdrop',
  DATA_TOGGLE   : '[data-toggle="dropdown"]',
  FORM_CHILD    : '.dropdown form',
  ROLE_MENU     : '[role="menu"]',
  ROLE_LISTBOX  : '[role="listbox"]',
  NAVBAR_NAV    : '.navbar-nav',
  VISIBLE_ITEMS : '[role="menu"] li:not(.divider) a, [role="listbox"] li:not(.divider) a'
}


/**
 * Provides the jQuery Interface for the alert component.
 * @param {string=} opt_config
 * @this {jQuery}
 * @return {jQuery}
 * @private
 */
Dropdown._jQueryInterface = function (opt_config) {
  return this.each(function () {
    var data  = $(this).data(Dropdown._DATA_KEY)

    if (!data) {
      $(this).data(Dropdown._DATA_KEY, (data = new Dropdown(this)))
    }

    if (typeof opt_config === 'string') {
      data[opt_config].call(this)
    }
  })
}


/**
 * @param {Event=} opt_event
 * @private
 */
Dropdown._clearMenus = function (opt_event) {
  if (opt_event && opt_event.which == 3) {
    return
  }

  var backdrop = $(Dropdown._Selector.BACKDROP)[0]
  if (backdrop) {
    backdrop.parentNode.removeChild(backdrop)
  }

  var toggles = /** @type {Array.<Element>} */ ($.makeArray($(Dropdown._Selector.DATA_TOGGLE)))

  for (var i = 0; i < toggles.length; i++) {
    var parent = Dropdown._getParentFromElement(toggles[i])
    var relatedTarget = { 'relatedTarget': toggles[i] }

    if (!$(parent).hasClass(Dropdown._ClassName.OPEN)) {
      continue
    }

    var hideEvent = $.Event(Dropdown._Event.HIDE, relatedTarget)
    $(parent).trigger(hideEvent)
    if (hideEvent.isDefaultPrevented()) {
      continue
    }

    toggles[i].setAttribute('aria-expanded', 'false')

    $(parent)
      .removeClass(Dropdown._ClassName.OPEN)
      .trigger(Dropdown._Event.HIDDEN, relatedTarget)
  }
}


/**
 * @param {Element} element
 * @return {Element}
 * @private
 */
Dropdown._getParentFromElement = function (element) {
  var selector = Bootstrap.getSelectorFromElement(element)

  if (selector) {
    var parent = $(selector)[0]
  }

  return /** @type {Element} */ (parent || element.parentNode)
}


/**
 * @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
  }

  event.preventDefault()
  event.stopPropagation()

  if (this.disabled || $(this).hasClass(Dropdown._ClassName.DISABLED)) {
    return
  }

  var parent  = Dropdown._getParentFromElement(this)
  var isActive = $(parent).hasClass(Dropdown._ClassName.OPEN)

  if ((!isActive && event.which != 27) || (isActive && event.which == 27)) {
    if (event.which == 27) {
      var toggle = $(parent).find(Dropdown._Selector.DATA_TOGGLE)[0]
      $(toggle).trigger('focus')
    }
    $(this).trigger('click')
    return
  }

  var items = $.makeArray($(Dropdown._Selector.VISIBLE_ITEMS))

  items = items.filter(function (item) {
    return item.offsetWidth || item.offsetHeight
  })

  if (!items.length) {
    return
  }

  var index = items.indexOf(event.target)

  if (event.which == 38 && index > 0)                index--                        // up
  if (event.which == 40 && index < items.length - 1) index++                        // down
  if (!~index)                                       index = 0

  items[index].focus()
}


/**
 * Toggles the dropdown
 * @this {Element}
 * @return {boolean|undefined}
 */
Dropdown.prototype['toggle'] = function () {
  if (this.disabled || $(this).hasClass(Dropdown._ClassName.DISABLED)) {
    return
  }

  var parent   = Dropdown._getParentFromElement(this)
  var isActive = $(parent).hasClass(Dropdown._ClassName.OPEN)

  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)
  }

  var relatedTarget = { 'relatedTarget': this }
  var showEvent     = $.Event(Dropdown._Event.SHOW, relatedTarget)

  $(parent).trigger(showEvent)

  if (showEvent.isDefaultPrevented()) {
    return
  }

  this.focus()
  this.setAttribute('aria-expanded', 'true')

  $(parent).toggleClass(Dropdown._ClassName.OPEN)

  $(parent).trigger(Dropdown._Event.SHOWN, relatedTarget)

  return false
}


/**
 * ------------------------------------------------------------------------
 * 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';


/**
 * Our modal class.
 * @param {Element} element
 * @param {Object} config
 * @constructor
 */
var Modal = function (element, config) {

  /** @private {Object} */
  this._config = config

  /** @private {Element} */
  this._element = element

  /** @private {Element} */
  this._backdrop = null

  /** @private {boolean} */
  this._isShown = false

  /** @private {boolean} */
  this._isBodyOverflowing = false

  /** @private {number} */
  this._scrollbarWidth = 0

}


/**
 * @const
 * @type {string}
 */
Modal['VERSION']  = '4.0.0'


/**
 * @const
 * @type {Object}
 */
Modal['Defaults'] = {
  'backdrop' : true,
  'keyboard' : true,
  'show'     : true
}


/**
 * @const
 * @type {string}
 * @private
 */
Modal._NAME = 'modal'


/**
 * @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)
    }

    if (typeof opt_config == 'string') {
      data[opt_config](opt_relatedTarget)

    } else if (config['show']) {
      data['show'](opt_relatedTarget)
    }
  })
}


/**
 * @param {Element} relatedTarget
 */
Modal.prototype['toggle'] = function (relatedTarget) {
  return this._isShown ? this['hide']() : this['show'](relatedTarget)
}


/**
 * @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
  }

  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()
  }

  var hideEvent = $.Event(Modal._Event.HIDE)

  $(this._element).trigger(hideEvent)

  if (!this._isShown || hideEvent.isDefaultPrevented()) {
    return
  }

  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()
  }
}


/**
 * @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
  }

  this._element.style.display = 'block'
  this._element.scrollTop = 0

  if (this._config['backdrop']) {
    this._adjustBackdrop()
  }

  if (transition) {
    Bootstrap.reflow(this._element)
  }

  $(this._element).addClass(Modal._ClassName.IN)
  this._element.setAttribute('aria-hidden', false)

  this._enforceFocus()

  var shownEvent = $.Event(Modal._Event.SHOWN, { relatedTarget: relatedTarget })

  var transitionComplete = function () {
    this._element.focus()
    $(this._element).trigger(shownEvent)
  }.bind(this)

  if (transition) {
    var dialog = $(this._element).find(Modal._Selector.DIALOG)[0]
    $(dialog)
      .one(Bootstrap.TRANSITION_END, transitionComplete)
      .emulateTransitionEnd(Modal._TRANSITION_DURATION)
  } else {
    transitionComplete()
  }
}



/**
 * @private
 */
Modal.prototype._enforceFocus = function () {
  $(document)
    .off('focusin.bs.modal') // guard against infinite focus loop
    .on('focusin.bs.modal', function (e) {
      if (this._element !== e.target && !$(this._element).has(e.target).length) {
        this._element.focus()
      }
    }.bind(this))
}


/**
 * @private
 */
Modal.prototype._escape = function () {
  if (this._isShown && this._config['keyboard']) {
    $(this._element).on('keydown.dismiss.bs.modal', function (event) {
      if (event.which === 27) {
        this['hide']()
      }
    }.bind(this))

  } else if (!this._isShown) {
    $(this._element).off('keydown.dismiss.bs.modal')
  }
}


/**
 * @private
 */
Modal.prototype._resize = function () {
  if (this._isShown) {
    $(window).on('resize.bs.modal', this._handleUpdate.bind(this))
  } else {
    $(window).off('resize.bs.modal')
  }
}


/**
 * @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
  }
}


/**
 * @param {Function} callback
 * @private
 */
Modal.prototype._showBackdrop = function (callback) {
  var animate = $(this._element).hasClass(Modal._ClassName.FADE) ? Modal._ClassName.FADE : ''

  if (this._isShown && this._config['backdrop']) {
    var doAnimate = Bootstrap.transition && animate

    this._backdrop = document.createElement('div')
    this._backdrop.className = Modal._ClassName.BACKDROP

    if (animate) {
      $(this._backdrop).addClass(animate)
    }

    $(this._element).prepend(this._backdrop)

    $(this._backdrop).on('click.dismiss.bs.modal', function (event) {
      if (event.target !== event.currentTarget) return
      this._config['backdrop'] === 'static'
        ? this._element.focus()
        : this['hide']()
    }.bind(this))

    if (doAnimate) {
      Bootstrap.reflow(this._backdrop)
    }

    $(this._backdrop).addClass(Modal._ClassName.IN)

    if (!callback) {
      return
    }

    if (!doAnimate) {
      callback()
      return
    }

    $(this._backdrop)
      .one(Bootstrap.TRANSITION_END, callback)
      .emulateTransitionEnd(Modal._BACKDROP_TRANSITION_DURATION)

  } else if (!this._isShown && this._backdrop) {
    $(this._backdrop).removeClass(Modal._ClassName.IN)

    var callbackRemove = function () {
      this._removeBackdrop()
      if (callback) {
        callback()
      }
    }.bind(this)

    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

  if (!this._isBodyOverflowing && isModalOverflowing) {
    this._element.style.paddingLeft = this._scrollbarWidth + 'px'
  }

  if (this._isBodyOverflowing && !isModalOverflowing) {
    this._element.style.paddingRight = this._scrollbarWidth + 'px'
  }
}


/**
 * @private
 */
Modal.prototype._resetAdjustments = function () {
  this._element.style.paddingLeft = ''
  this._element.style.paddingRight = ''
}


/**
 * @private
 */
Modal.prototype._checkScrollbar = function () {
  this._isBodyOverflowing = document.body.scrollHeight > document.documentElement.clientHeight
  this._scrollbarWidth = this._getScrollbarWidth()
}


/**
 * @private
 */
Modal.prototype._setScrollbar = function () {
  var bodyPadding = parseInt(($(document.body).css('padding-right') || 0), 10)

  if (this._isBodyOverflowing) {
    document.body.style.paddingRight = bodyPadding + this._scrollbarWidth + 'px'
  }
}


/**
 * @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)
})

/** =======================================================================
 * 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';


/**
 * Our scrollspy class.
 * @param {Element!} element
 * @param {Object=} opt_config
 * @constructor
 */
function ScrollSpy(element, opt_config) {

  /** @private {Element|Window} */
  this._scrollElement = element.tagName == 'BODY' ? window : element

  /** @private {Object} */
  this._config = $.extend({}, ScrollSpy['Defaults'], opt_config)

  /** @private {string} */
  this._selector = (this._config.target || '') + ' .nav li > a'

  /** @private {Array} */
  this._offsets = []

  /** @private {Array} */
  this._targets = []

  /** @private {Element} */
  this._activeTarget = null

  /** @private {number} */
  this._scrollHeight = 0

  $(this._scrollElement).on('scroll.bs.scrollspy', this._process.bind(this))

  this['refresh']()

  this._process()
}


/**
 * @const
 * @type {string}
 */
ScrollSpy['VERSION'] = '4.0.0'


/**
 * @const
 * @type {Object}
 */
ScrollSpy['Defaults'] = {
  'offset': 10
}


/**
 * @const
 * @type {string}
 * @private
 */
ScrollSpy._NAME = 'scrollspy'


/**
 * @const
 * @type {string}
 * @private
 */
ScrollSpy._DATA_KEY = 'bs.scrollspy'


/**
 * @const
 * @type {Function}
 * @private
 */
ScrollSpy._JQUERY_NO_CONFLICT = $.fn[ScrollSpy._NAME]


/**
 * @const
 * @enum {string}
 * @private
 */
ScrollSpy._Event = {
  ACTIVATE: 'activate.bs.scrollspy'
}


/**
 * @const
 * @enum {string}
 * @private
 */
ScrollSpy._ClassName = {
  DROPDOWN_MENU : 'dropdown-menu',
  ACTIVE        : 'active'
}


/**
 * @const
 * @enum {string}
 * @private
 */
ScrollSpy._Selector = {
  DATA_SPY    : '[data-spy="scroll"]',
  ACTIVE      : '.active',
  LI_DROPDOWN : 'li.dropdown',
  LI          : 'li'
}


/**
 * @param {Object=} opt_config
 * @this {jQuery}
 * @return {jQuery}
 * @private
 */
ScrollSpy._jQueryInterface = function (opt_config) {
  return this.each(function () {
    var data   = $(this).data(ScrollSpy._DATA_KEY)
    var config = typeof opt_config === 'object' && opt_config || null

    if (!data) {
      data = new ScrollSpy(this, config)
      $(this).data(ScrollSpy._DATA_KEY, data)
    }

    if (typeof opt_config === 'string') {
      data[opt_config]()
    }
  })
}


/**
 * Refresh the scrollspy target cache
 */
ScrollSpy.prototype['refresh'] = function () {
  var offsetMethod = 'offset'
  var offsetBase   = 0

  if (this._scrollElement !== this._scrollElement.window) {
    offsetMethod = 'position'
    offsetBase   = this._getScrollTop()
  }

  this._offsets = []
  this._targets = []

  this._scrollHeight = this._getScrollHeight()

  var targets = /** @type {Array.<Element>} */ ($.makeArray($(this._selector)))

  targets
    .map(function (element, index) {
      var target
      var targetSelector = Bootstrap.getSelectorFromElement(element)

      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)
    }
  }

  if (this._activeTarget && scrollTop < this._offsets[0]) {
    this._activeTarget = null
    this._clear()
    return
  }

  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)

  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)
    }
  }

  $(this._scrollElement).trigger(ScrollSpy._Event.ACTIVATE, {
    relatedTarget: target
  })
}


/**
 * @private
 */
ScrollSpy.prototype._clear = function () {
  var activeParents = $(this._selector).parentsUntil(this._config.target, ScrollSpy._Selector.ACTIVE)

  for (var i = activeParents.length; i--;) {
    $(activeParents[i]).removeClass(ScrollSpy._ClassName.ACTIVE)
  }
}


/**
 * ------------------------------------------------------------------------
 * jQuery Interface + noConflict implementaiton
 * ------------------------------------------------------------------------
 */

/**
 * @const
 * @type {Function}
 */
$.fn[ScrollSpy._NAME] = ScrollSpy._jQueryInterface


/**
 * @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
}


/**
 * ------------------------------------------------------------------------
 * 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()))
  }
})

/** =======================================================================
 * Bootstrap: tooltip.js v4.0.0
 * http://getbootstrap.com/javascript/#tooltip
 * ========================================================================
 * Copyright 2011-2015 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * ========================================================================
 * @fileoverview - Bootstrap's tooltip plugin.
 * (Inspired by jQuery.tipsy by Jason Frame)
 *
 * Public Methods & Properties:
 *
 *   + $.tooltip
 *   + $.tooltip.noConflict
 *   + $.tooltip.Constructor
 *   + $.tooltip.Constructor.VERSION
 *   + $.tooltip.Constructor.Defaults
 *   + $.tooltip.Constructor.Defaults.container
 *   + $.tooltip.Constructor.Defaults.animation
 *   + $.tooltip.Constructor.Defaults.placement
 *   + $.tooltip.Constructor.Defaults.selector
 *   + $.tooltip.Constructor.Defaults.template
 *   + $.tooltip.Constructor.Defaults.trigger
 *   + $.tooltip.Constructor.Defaults.title
 *   + $.tooltip.Constructor.Defaults.delay
 *   + $.tooltip.Constructor.Defaults.html
 *   + $.tooltip.Constructor.Defaults.viewport
 *   + $.tooltip.Constructor.Defaults.viewport.selector
 *   + $.tooltip.Constructor.Defaults.viewport.padding
 *   + $.tooltip.Constructor.prototype.enable
 *   + $.tooltip.Constructor.prototype.disable
 *   + $.tooltip.Constructor.prototype.destroy
 *   + $.tooltip.Constructor.prototype.toggleEnabled
 *   + $.tooltip.Constructor.prototype.toggle
 *   + $.tooltip.Constructor.prototype.show
 *   + $.tooltip.Constructor.prototype.hide
 *
 * ========================================================================
 */

'use strict';


/**
 * Our tooltip class.
 * @param {Element!} element
 * @param {Object=} opt_config
 * @constructor
 */
var Tooltip = function (element, opt_config) {

  /** @private {boolean} */
  this._isEnabled = true

  /** @private {number} */
  this._timeout = 0

  /** @private {string} */
  this._hoverState = ''

  /** @protected {Element} */
  this.element = element

  /** @protected {Object} */
  this.config = this._getConfig(opt_config)

  /** @protected {Element} */
  this.tip = null

  /** @protected {Element} */
  this.arrow = null

  if (this.config['viewport']) {

    /** @private {Element} */
    this._viewport = $(this.config['viewport']['selector'] || this.config['viewport'])[0]

  }

  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 (!data) {
      data = new Tooltip(this, config)
      $(this).data(Tooltip._DATA_KEY, data)
    }

    if (typeof opt_config === 'string') {
      data[opt_config]()
    }
  })
}


/**
 * Enable tooltip
 */
Tooltip.prototype['enable'] = function () {
  this._isEnabled = true
}


/**
 * Disable tooltip
 */
Tooltip.prototype['disable'] = function () {
  this._isEnabled = false
}


/**
 * Toggle the tooltip enable state
 */
Tooltip.prototype['toggleEnabled'] = function () {
  this._isEnabled = !this._isEnabled
}

/**
 * Toggle the tooltips display
 * @param {Event} opt_event
 */
Tooltip.prototype['toggle'] = function (opt_event) {
  var context = this
  var dataKey = this.getDataKey()

  if (opt_event) {
    context = $(opt_event.currentTarget).data(dataKey)

    if (!context) {
      context = new this.constructor(opt_event.currentTarget, this._getDelegateConfig())
      $(opt_event.currentTarget).data(dataKey, context)
    }
  }

  $(context.getTipElement()).hasClass(Tooltip._ClassName.IN) ?
    context._leave(null, context) :
    context._enter(null, context)
}


/**
 * Remove tooltip functionality
 */
Tooltip.prototype['destroy'] = function () {
  clearTimeout(this._timeout)
  this['hide'](function () {
    $(this.element)
      .off(Tooltip._Selector.TOOLTIP)
      .removeData(this.getDataKey())
  }.bind(this))
}


/**
 * Show the tooltip
 * todo (fat): ~fuck~ this is a big function - refactor out all of positioning logic
 * and replace with external lib
 */
Tooltip.prototype['show'] = function () {
  var showEvent = $.Event(this.getEventObject().SHOW)

  if (this.isWithContent() && this._isEnabled) {
    $(this.element).trigger(showEvent)

    var isInTheDom = $.contains(this.element.ownerDocument.documentElement, this.element)

    if (showEvent.isDefaultPrevented() || !isInTheDom) {
      return
    }

    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)
    }

    var placement = typeof this.config['placement'] == 'function' ?
      this.config['placement'].call(this, tip, this.element) :
      this.config['placement']

    var autoToken = /\s?auto?\s?/i
    var isWithAutoPlacement = autoToken.test(placement)

    if (isWithAutoPlacement) {
      placement = placement.replace(autoToken, '') || Tooltip.Direction.TOP
    }

    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)

    $(tip).data(this.getDataKey(), this)

    if (this.config['container']) {
      $(this.config['container'])[0].appendChild(tip)
    } else {
      this.element.parentNode.insertBefore(tip, this.element.nextSibling)
    }

    var position            = this._getPosition()
    var actualWidth         = tip.offsetWidth
    var actualHeight        = tip.offsetHeight

    var calculatedPlacement = this._getCalculatedAutoPlacement(isWithAutoPlacement, placement, position, actualWidth, actualHeight)
    var calculatedOffset    = this._getCalculatedOffset(calculatedPlacement, position, actualWidth, actualHeight)

    this._applyCalculatedPlacement(calculatedOffset, calculatedPlacement)

    var complete = function () {
      var prevHoverState = this.hoverState
      $(this.element).trigger(this.getEventObject().SHOWN)
      this.hoverState = null

      if (prevHoverState == 'out') this._leave(null, this)
    }.bind(this)

    Bootstrap.transition && $(this._tip).hasClass(Tooltip._ClassName.FADE) ?
      $(this._tip)
        .one(Bootstrap.TRANSITION_END, complete)
        .emulateTransitionEnd(Tooltip._TRANSITION_DURATION) :
      complete()
  }
}


/**
 * Hide the tooltip breh
 */
Tooltip.prototype['hide'] = function (callback) {
  var tip       = this.getTipElement()
  var hideEvent = $.Event(this.getEventObject().HIDE)

  var complete  = function () {
    if (this._hoverState != Tooltip._HoverState.IN) {
      tip.parentNode.removeChild(tip)
    }

    this.element.removeAttribute('aria-describedby')
    $(this.element).trigger(this.getEventObject().HIDDEN)

    if (callback) {
      callback()
    }
  }.bind(this)

  $(this.element).trigger(hideEvent)

  if (hideEvent.isDefaultPrevented()) return

  $(tip).removeClass(Tooltip._ClassName.IN)

  if (Bootstrap.transition && $(this._tip).hasClass(Tooltip._ClassName.FADE)) {
    $(tip)
      .one(Bootstrap.TRANSITION_END, complete)
      .emulateTransitionEnd(Tooltip._TRANSITION_DURATION)
  } else {
    complete()
  }

  this._hoverState = ''
}


/**
 * @return {string}
 */
Tooltip.prototype['getHoverState'] = function (callback) {
  return this._hoverState
}


/**
 * @return {string}
 * @protected
 */
Tooltip.prototype.getName = function () {
  return Tooltip._NAME
}


/**
 * @return {string}
 * @protected
 */
Tooltip.prototype.getDataKey = function () {
  return Tooltip._DATA_KEY
}


/**
 * @return {Object}
 * @protected
 */
Tooltip.prototype.getEventObject = function () {
  return Tooltip._Event
}


/**
 * @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']
  }

  return /** @type {string} */ (title)
}


/**
 * @return {Element}
 * @protected
 */
Tooltip.prototype.getTipElement = function () {
  return (this._tip = this._tip || $(this.config['template'])[0])
}


/**
 * @return {Element}
 * @protected
 */
Tooltip.prototype.getArrowElement = function () {
  return (this.arrow = this.arrow || $(this.getTipElement()).find(Tooltip._Selector.TOOLTIP_ARROW)[0])
}


/**
 * @return {boolean}
 * @protected
 */
Tooltip.prototype.isWithContent = function () {
  return !!this.getTitle()
}


/**
 * @protected
 */
Tooltip.prototype.setContent = function () {
  var tip   = this.getTipElement()
  var title = this.getTitle()

  $(tip).find(Tooltip._Selector.TOOLTIP_INNER)[0][this.config['html'] ? 'innerHTML' : 'innerText'] = title

  $(tip)
    .removeClass(Tooltip._ClassName.FADE)
    .removeClass(Tooltip._ClassName.IN)

  for (var direction in Tooltip.Direction) {
    $(tip).removeClass(Tooltip._NAME + '-' + direction)
  }
}


/**
 * @private
 */
Tooltip.prototype._setListeners = function () {
  var triggers = this.config['trigger'].split(' ')

  triggers.forEach(function (trigger) {
    if (trigger == 'click') {
      $(this.element).on('click.bs.tooltip', this.config['selector'], this['toggle'].bind(this))

    } 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))

  if (this.config['selector']) {
    this.config = $.extend({}, this.config, { 'trigger': 'manual', 'selector': '' })
  } else {
    this._fixTitle()
  }
}


/**
 * @param {Object=} opt_config
 * @return {Object}
 * @private
 */
Tooltip.prototype._getConfig = function (opt_config) {
  var config = $.extend({}, this.constructor['Defaults'], $(this.element).data(), opt_config)

  if (config['delay'] && typeof config['delay'] == 'number') {
    config['delay'] = {
      'show': config['delay'],
      'hide': config['delay']
    }
  }

  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
}



/**
 * @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
  }

  var viewportPadding    = this.config['viewport'] && this.config['viewport']['padding'] || 0
  var viewportDimensions = this._getPosition(this._viewport)

  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

    } 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
    }
  }

  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
  }

  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
  }

  var delta = this._getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)

  if (delta.left) {
    offset.left += delta.left
  } else {
    offset.top  += delta.top
  }

  var isVertical          = placement === Tooltip.Direction.TOP || placement === Tooltip.Direction.BOTTOM
  var arrowDelta          = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
  var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'

  $(tip).offset(offset)

  this._replaceArrow(arrowDelta, tip[arrowOffsetPosition], isVertical)
}


/**
 * @param {number} delta
 * @param {number} dimension
 * @param {boolean} isHorizontal
 * @private
 */
Tooltip.prototype._replaceArrow = function (delta, dimension, isHorizontal) {
  var arrow = this.getArrowElement()

  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', '')
  }
}


/**
 * @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)
  }

  clearTimeout(context._timeout)

  context._hoverState = Tooltip._HoverState.IN

  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)
  }

  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'])
}



/**
 * ------------------------------------------------------------------------
 * jQuery Interface + noConflict implementaiton
 * ------------------------------------------------------------------------
 */

/**
 * @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
 *
 * ========================================================================
 */


'use strict';


if (!Tooltip) throw new Error('Popover requires tooltip.js')


/**
 * Our tooltip class.
 * @param {Element!} element
 * @param {Object=} opt_config
 * @constructor
 * @extends {Tooltip}
 */
var Popover = function (element, opt_config) {
  Tooltip.apply(this, arguments)
}
Bootstrap.inherits(Popover, Tooltip)


/**
 * @const
 * @type {string}
 */
Popover['VERSION'] = '4.0.0'


/**
 * @const
 * @type {Object}
 */
Popover['Defaults'] = $.extend({}, $.fn['tooltip']['Constructor']['Defaults'], {
  'placement': 'right',
  'trigger': 'click',
  'content': '',
  'template': '<div class="popover" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
})


/**
 * @const
 * @type {string}
 * @private
 */
Popover._NAME = 'popover'


/**
 * @const
 * @type {string}
 * @private
 */
Popover._DATA_KEY = 'bs.popover'


/**
 * @const
 * @enum {string}
 * @private
 */
Popover._Event = {
  HIDE   : 'hide.bs.popover',
  HIDDEN : 'hidden.bs.popover',
  SHOW   : 'show.bs.popover',
  SHOWN  : 'shown.bs.popover'
}


/**
 * @const
 * @enum {string}
 * @private
 */
Popover._ClassName = {
  FADE : 'fade',
  IN  : 'in'
}


/**
 * @const
 * @enum {string}
 * @private
 */
Popover._Selector = {
  TITLE   : '.popover-title',
  CONTENT : '.popover-content',
  ARROW   : '.popover-arrow'
}


/**
 * @const
 * @type {Function}
 * @private
 */
Popover._JQUERY_NO_CONFLICT = $.fn[Popover._NAME]


/**
 * @param {Object|string=} opt_config
 * @this {jQuery}
 * @return {jQuery}
 * @private
 */
Popover._jQueryInterface = function (opt_config) {
  return this.each(function () {
    var data   = $(this).data(Popover._DATA_KEY)
    var config = typeof opt_config === 'object' ? opt_config : null

    if (!data && opt_config === 'destroy') {
      return
    }

    if (!data) {
      data = new Popover(this, config)
      $(this).data(Popover._DATA_KEY, data)
    }

    if (typeof opt_config === 'string') {
      data[opt_config]()
    }
  })
}


/**
 * @return {string}
 * @protected
 */
Popover.prototype.getName = function () {
  return Popover._NAME
}


/**
 * @override
 */
Popover.prototype.getDataKey = function () {
  return Popover._DATA_KEY
}


/**
 * @override
 */
Popover.prototype.getEventObject = function () {
  return Popover._Event
}


/**
 * @override
 */
Popover.prototype.getArrowElement = function () {
  return (this.arrow = this.arrow || $(this.getTipElement()).find(Popover._Selector.ARROW)[0])
}


/**
 * @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)

  $(tip)
    .removeClass(Popover._ClassName.FADE)
    .removeClass(Popover._ClassName.IN)

  for (var direction in Tooltip.Direction) {
    $(tip).removeClass(Popover._NAME + '-' + Tooltip.Direction[direction])
  }
}


/**
 * @override
 */
Popover.prototype.isWithContent = function () {
  return this.getTitle() || this._getContent()
}


/**
 * @override
 */
Popover.prototype.getTipElement = function () {
  return (this.tip = this.tip || $(this.config['template'])[0])
}


/**
 * @private
 */
Popover.prototype._getContent = function () {
  return this.element.getAttribute('data-content')
    || (typeof this.config['content'] == 'function' ?
          this.config['content'].call(this.element) :
          this.config['content'])
}



/**
 * ------------------------------------------------------------------------
 * jQuery Interface + noConflict implementaiton
 * ------------------------------------------------------------------------
 */

/**
 * @const
 * @type {Function}
 */
$.fn[Popover._NAME] = Popover._jQueryInterface


/**
 * @const
 * @type {Function}
 */
$.fn[Popover._NAME]['Constructor'] = Popover


/**
 * @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
 *
 * ========================================================================
 */


'use strict';

/**
 * Our Tab class.
 * @param {Element!} element
 * @constructor
 */
var Tab = function (element) {

  /** @type {Element} */
  this._element = element

}


/**
 * @const
 * @type {string}
 */
Tab['VERSION'] = '4.0.0'


/**
 * @const
 * @type {string}
 * @private
 */
Tab._NAME = 'tab'


/**
 * @const
 * @type {string}
 * @private
 */
Tab._DATA_KEY = 'bs.tab'


/**
 * @const
 * @type {number}
 * @private
 */
Tab._TRANSITION_DURATION = 150


/**
 * @const
 * @enum {string}
 * @private
 */
Tab._Event = {
  HIDE   : 'hide.bs.tab',
  HIDDEN : 'hidden.bs.tab',
  SHOW   : 'show.bs.tab',
  SHOWN  : 'shown.bs.tab'
}


/**
 * @const
 * @enum {string}
 * @private
 */
Tab._ClassName = {
  DROPDOWN_MENU : 'dropdown-menu',
  ACTIVE        : 'active',
  FADE          : 'fade',
  IN            : 'in'
}


/**
 * @const
 * @enum {string}
 * @private
 */
Tab._Selector = {
  A                     : 'a',
  LI                    : 'li',
  LI_DROPDOWN           : 'li.dropdown',
  UL                    : 'ul:not(.dropdown-menu)',
  FADE_CHILD            : ':scope > .fade',
  ACTIVE                : '.active',
  ACTIVE_CHILD          : ':scope > .active',
  DATA_TOGGLE           : '[data-toggle="tab"], [data-toggle="pill"]',
  DROPDOWN_ACTIVE_CHILD : ':scope > .dropdown-menu > .active'
}


/**
 * @param {Object|string=} opt_config
 * @this {jQuery}
 * @return {jQuery}
 * @private
 */
Tab._jQueryInterface = function (opt_config) {
  return this.each(function () {
    var $this = $(this)
    var data  = $this.data(Tab._DATA_KEY)

    if (!data) {
      data = data = new Tab(this)
      $this.data(Tab._DATA_KEY, data)
    }

    if (typeof opt_config === 'string') {
      data[opt_config]()
    }
  })
}


/**
 * Show the tab
 */
Tab.prototype['show'] = function () {
  if ( this._element.parentNode
    && this._element.parentNode.nodeType == Node.ELEMENT_NODE
    && $(this._element).parent().hasClass(Tab._ClassName.ACTIVE)) {
    return
  }

  var ulElement = $(this._element).closest(Tab._Selector.UL)[0]
  var selector  = Bootstrap.getSelectorFromElement(this._element)

  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]
    }
  }

  var hideEvent = $.Event(Tab._Event.HIDE, {
    relatedTarget: this._element
  })

  var showEvent = $.Event(Tab._Event.SHOW, {
    relatedTarget: previous
  })

  if (previous) {
    $(previous).trigger(hideEvent)
  }

  $(this._element).trigger(showEvent)

  if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return

  if (selector) {
    var target = $(selector)[0]
  }

  this._activate($(this._element).closest(Tab._Selector.LI)[0], ulElement)

  var complete = function () {
    var hiddenEvent = $.Event(Tab._Event.HIDDEN, {
      relatedTarget: this._element
    })

    var shownEvent  = $.Event(Tab._Event.SHOWN, {
      relatedTarget: previous
    })

    $(previous).trigger(hiddenEvent)
    $(this._element).trigger(shownEvent)
  }.bind(this)

  if (target) {
    this._activate(target, /** @type {Element} */ (target.parentNode), complete)
  } else {
    complete()
  }
}


/**
 * @param {Element} element
 * @param {Element} container
 * @param {Function=} opt_callback
 * @private
 */
Tab.prototype._activate = function (element, container, opt_callback) {
  var active          = $(container).find(Tab._Selector.ACTIVE_CHILD)[0]
  var isTransitioning = opt_callback
    && Bootstrap.transition
    && ((active && $(active).hasClass(Tab._ClassName.FADE))
       || !!$(container).find(Tab._Selector.FADE_CHILD)[0])

  var complete = this._transitionComplete.bind(this, element, active, isTransitioning, opt_callback)

  if (active && isTransitioning) {
    $(active)
      .one(Bootstrap.TRANSITION_END, complete)
      .emulateTransitionEnd(Tab._TRANSITION_DURATION)

  } else {
    complete()
  }

  if (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)
    }

    var activeToggle = $(active).find(Tab._Selector.DATA_TOGGLE)[0]
    if (activeToggle) {
      activeToggle.setAttribute('aria-expanded', false)
    }
  }

  $(element).addClass(Tab._ClassName.ACTIVE)

  var elementToggle = $(element).find(Tab._Selector.DATA_TOGGLE)[0]
  if (elementToggle) {
    elementToggle.setAttribute('aria-expanded', true)
  }

  if (isTransitioning) {
    Bootstrap.reflow(element)
    $(element).addClass(Tab._ClassName.IN)
  } else {
    $(element).removeClass(Tab._ClassName.FADE)
  }

  if (element.parentNode && $(element.parentNode).hasClass(Tab._ClassName.DROPDOWN_MENU)) {
    var dropdownElement = $(element).closest(Tab._Selector.LI_DROPDOWN)[0]
    if (dropdownElement) {
      $(dropdownElement).addClass(Tab._ClassName.ACTIVE)
    }

    elementToggle = $(element).find(Tab._Selector.DATA_TOGGLE)[0]
    if (elementToggle) {
      elementToggle.setAttribute('aria-expanded', true)
    }
  }

  if (opt_callback) {
    opt_callback()
  }
}


/**
 * ------------------------------------------------------------------------
 * jQuery Interface + noConflict implementaiton
 * ------------------------------------------------------------------------
 */

/**
 * @const
 * @type {Function}
 */
$.fn[Tab._NAME] = Tab._jQueryInterface


/**
 * @const
 * @type {Function}
 */
$.fn[Tab._NAME]['Constructor'] = Tab


/**
 * @const
 * @type {Function}
 */
$.fn[Tab._NAME]['noConflict'] = function () {
  $.fn[Tab._NAME] = Tab._JQUERY_NO_CONFLICT
  return this
}



// 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)
