From 8eee78ca15f51dc7e7d514497078bfd7c012ac21 Mon Sep 17 00:00:00 2001
From: fat <fat@folders.local>
Date: Mon, 11 May 2015 12:29:06 -0700
Subject: [PATCH] tab es6

---
 Gruntfile.js             |   3 +-
 js/dist/scrollspy.js     |   1 -
 js/dist/scrollspy.js.map | Bin 13403 -> 13346 bytes
 js/dist/tab.js           | 270 +++++++++++++++++++++++++++++++++++++
 js/dist/tab.js.map       | Bin 0 -> 13219 bytes
 js/scrollspy.js          | 172 ------------------------
 js/src/scrollspy.js      |   9 +-
 js/src/tab.js            | 278 +++++++++++++++++++++++++++++++++++++++
 js/tests/index.html      |   2 +-
 js/tests/visual/tab.html |  38 +++---
 10 files changed, 574 insertions(+), 199 deletions(-)
 create mode 100644 js/dist/tab.js
 create mode 100644 js/dist/tab.js.map
 delete mode 100644 js/scrollspy.js
 create mode 100644 js/src/tab.js

diff --git a/Gruntfile.js b/Gruntfile.js
index b3c29c3533..84e3f9bb24 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -70,7 +70,8 @@ module.exports = function (grunt) {
           'js/dist/collapse.js'  : 'js/src/collapse.js',
           'js/dist/dropdown.js'  : 'js/src/dropdown.js',
           'js/dist/modal.js'     : 'js/src/modal.js',
-          'js/dist/scrollspy.js' : 'js/src/scrollspy.js'
+          'js/dist/scrollspy.js' : 'js/src/scrollspy.js',
+          'js/dist/tab.js'       : 'js/src/tab.js'
         }
       }
     },
diff --git a/js/dist/scrollspy.js b/js/dist/scrollspy.js
index a0d774f438..7cacbab8b9 100644
--- a/js/dist/scrollspy.js
+++ b/js/dist/scrollspy.js
@@ -23,7 +23,6 @@ var ScrollSpy = (function ($) {
   var VERSION = '4.0.0';
   var DATA_KEY = 'bs.scrollspy';
   var JQUERY_NO_CONFLICT = $.fn[NAME];
-  var TRANSITION_DURATION = 150;
 
   var Defaults = {
     offset: 10
diff --git a/js/dist/scrollspy.js.map b/js/dist/scrollspy.js.map
index ba686262fc673f2f9f97afea8a501eb0480ca028..dcd3cf1b02136d5e850296e9d0ccef9ec6024b89 100644
GIT binary patch
delta 243
zcmcbeu_$B0)Wl3Dop49Ta2;nyM@MT%N9Ph}9bZRBUmbr(M}Hl6N5^Cc3nrZdl@4@t
z3<OCh>Li2Iz@)37(%DXqj@eEi1@4mzSwtrv5a;3ss{vZ!TI9Uhk?{>1ud|^uOt*8|
zW-AV9ZYE35$#udildFYw7|k}X5I)2xQsm>V6YS_1Yz+gBj+vY5L{(WQFOkxkd|paz
l@;5169$N(!y|lb&KSy8J*v-Mx(>VBJ@)VH4=9kJv0swKdMGF7`

delta 296
zcmZ3KaXVwe)WmEjop49Ta2;nyM@MT%N9QtU9bZRBUmbr(M}Hl6N5@nM3nrZhl@4@t
z3<OCh>m-8+n6x2OI@`(75oj_{f#>7^7SYM`#kt^WN}y_TfogKWYTQ8z-60BqZgACi
z+N{9%hK<+J6=u4#vCC#j4ry*CbI-}K!YY%agmoBAHn$5OViYOxan}iUbPTqJ0Y}I5
z&9S1Ytdkq1v?s5YQlETLO3y&SRzXECEic;7(bqLLCQm^jIX|zsL?I-|(J$CD#M9p|
a-X%215ySwhG&D8X%q=~QWAg^(A^`w3-BU#X

diff --git a/js/dist/tab.js b/js/dist/tab.js
new file mode 100644
index 0000000000..8c71d01714
--- /dev/null
+++ b/js/dist/tab.js
@@ -0,0 +1,270 @@
+'use strict';
+
+var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
+
+/**
+ * --------------------------------------------------------------------------
+ * Bootstrap (v4.0.0): tab.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+
+var Tab = (function ($) {
+
+  /**
+   * ------------------------------------------------------------------------
+   * Constants
+   * ------------------------------------------------------------------------
+   */
+
+  var NAME = 'tab';
+  var VERSION = '4.0.0';
+  var DATA_KEY = 'bs.tab';
+  var JQUERY_NO_CONFLICT = $.fn[NAME];
+  var TRANSITION_DURATION = 150;
+
+  var Event = {
+    HIDE: 'hide.bs.tab',
+    HIDDEN: 'hidden.bs.tab',
+    SHOW: 'show.bs.tab',
+    SHOWN: 'shown.bs.tab',
+    CLICK: 'click.bs.tab.data-api'
+  };
+
+  var ClassName = {
+    DROPDOWN_MENU: 'dropdown-menu',
+    ACTIVE: 'active',
+    FADE: 'fade',
+    IN: 'in'
+  };
+
+  var Selector = {
+    A: 'a',
+    LI: 'li',
+    LI_DROPDOWN: 'li.dropdown',
+    UL: 'ul:not(.dropdown-menu)',
+    FADE_CHILD: '> .fade',
+    ACTIVE: '.active',
+    ACTIVE_CHILD: '> .active',
+    DATA_TOGGLE: '[data-toggle="tab"], [data-toggle="pill"]',
+    DROPDOWN_ACTIVE_CHILD: '> .dropdown-menu > .active'
+  };
+
+  /**
+   * ------------------------------------------------------------------------
+   * Class Definition
+   * ------------------------------------------------------------------------
+   */
+
+  var Tab = (function () {
+    function Tab(element) {
+      _classCallCheck(this, Tab);
+
+      this._element = element;
+    }
+
+    _createClass(Tab, [{
+      key: 'show',
+
+      // public
+
+      value: function show() {
+        var _this = this;
+
+        if (this._element.parentNode && this._element.parentNode.nodeType == Node.ELEMENT_NODE && $(this._element).parent().hasClass(ClassName.ACTIVE)) {
+          return;
+        }
+
+        var target = undefined;
+        var previous = undefined;
+        var ulElement = $(this._element).closest(Selector.UL)[0];
+        var selector = Util.getSelectorFromElement(this._element);
+
+        if (ulElement) {
+          previous = $.makeArray($(ulElement).find(Selector.ACTIVE));
+          previous = previous[previous.length - 1];
+
+          if (previous) {
+            previous = $(previous).find(Selector.A)[0];
+          }
+        }
+
+        var hideEvent = $.Event(Event.HIDE, {
+          relatedTarget: this._element
+        });
+
+        var showEvent = $.Event(Event.SHOW, {
+          relatedTarget: previous
+        });
+
+        if (previous) {
+          $(previous).trigger(hideEvent);
+        }
+
+        $(this._element).trigger(showEvent);
+
+        if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) {
+          return;
+        }
+
+        if (selector) {
+          target = $(selector)[0];
+        }
+
+        this._activate($(this._element).closest(Selector.LI)[0], ulElement);
+
+        var complete = function complete() {
+          var hiddenEvent = $.Event(Event.HIDDEN, {
+            relatedTarget: _this._element
+          });
+
+          var shownEvent = $.Event(Event.SHOWN, {
+            relatedTarget: previous
+          });
+
+          $(previous).trigger(hiddenEvent);
+          $(_this._element).trigger(shownEvent);
+        };
+
+        if (target) {
+          this._activate(target, target.parentNode, complete);
+        } else {
+          complete();
+        }
+      }
+    }, {
+      key: '_activate',
+
+      // private
+
+      value: function _activate(element, container, callback) {
+        var active = $(container).find(Selector.ACTIVE_CHILD)[0];
+        var isTransitioning = callback && Util.supportsTransitionEnd() && (active && $(active).hasClass(ClassName.FADE) || !!$(container).find(Selector.FADE_CHILD)[0]);
+
+        var complete = this._transitionComplete.bind(this, element, active, isTransitioning, callback);
+
+        if (active && isTransitioning) {
+          $(active).one(Util.TRANSITION_END, complete).emulateTransitionEnd(TRANSITION_DURATION);
+        } else {
+          complete();
+        }
+
+        if (active) {
+          $(active).removeClass(ClassName.IN);
+        }
+      }
+    }, {
+      key: '_transitionComplete',
+      value: function _transitionComplete(element, active, isTransitioning, callback) {
+        if (active) {
+          $(active).removeClass(ClassName.ACTIVE);
+
+          var dropdownChild = $(active).find(Selector.DROPDOWN_ACTIVE_CHILD)[0];
+          if (dropdownChild) {
+            $(dropdownChild).removeClass(ClassName.ACTIVE);
+          }
+
+          var activeToggle = $(active).find(Selector.DATA_TOGGLE)[0];
+          if (activeToggle) {
+            activeToggle.setAttribute('aria-expanded', false);
+          }
+        }
+
+        $(element).addClass(ClassName.ACTIVE);
+
+        var elementToggle = $(element).find(Selector.DATA_TOGGLE)[0];
+        if (elementToggle) {
+          elementToggle.setAttribute('aria-expanded', true);
+        }
+
+        if (isTransitioning) {
+          Util.reflow(element);
+          $(element).addClass(ClassName.IN);
+        } else {
+          $(element).removeClass(ClassName.FADE);
+        }
+
+        if (element.parentNode && $(element.parentNode).hasClass(ClassName.DROPDOWN_MENU)) {
+
+          var dropdownElement = $(element).closest(Selector.LI_DROPDOWN)[0];
+          if (dropdownElement) {
+            $(dropdownElement).addClass(ClassName.ACTIVE);
+          }
+
+          elementToggle = $(element).find(Selector.DATA_TOGGLE)[0];
+          if (elementToggle) {
+            elementToggle.setAttribute('aria-expanded', true);
+          }
+        }
+
+        if (callback) {
+          callback();
+        }
+      }
+    }], [{
+      key: 'VERSION',
+
+      // getters
+
+      get: function () {
+        return VERSION;
+      }
+    }, {
+      key: 'Default',
+      get: function () {
+        return Default;
+      }
+    }, {
+      key: '_jQueryInterface',
+
+      // static
+
+      value: function _jQueryInterface(config) {
+        return this.each(function () {
+          var $this = $(this);
+          var data = $this.data(DATA_KEY);
+
+          if (!data) {
+            data = data = new Tab(this);
+            $this.data(DATA_KEY, data);
+          }
+
+          if (typeof config === 'string') {
+            data[config]();
+          }
+        });
+      }
+    }]);
+
+    return Tab;
+  })();
+
+  /**
+   * ------------------------------------------------------------------------
+   * Data Api implementation
+   * ------------------------------------------------------------------------
+   */
+
+  $(document).on(Event.CLICK, Selector.DATA_TOGGLE, function (event) {
+    event.preventDefault();
+    Tab._jQueryInterface.call($(this), 'show');
+  });
+
+  /**
+   * ------------------------------------------------------------------------
+   * jQuery
+   * ------------------------------------------------------------------------
+   */
+
+  $.fn[NAME] = Tab._jQueryInterface;
+  $.fn[NAME].Constructor = Tab;
+  $.fn[NAME].noConflict = function () {
+    $.fn[NAME] = JQUERY_NO_CONFLICT;
+    return Tab._jQueryInterface;
+  };
+
+  return Tab;
+})(jQuery);
+//# sourceMappingURL=tab.js.map
\ No newline at end of file
diff --git a/js/dist/tab.js.map b/js/dist/tab.js.map
new file mode 100644
index 0000000000000000000000000000000000000000..6981e217a58667076fb68a9be96fcb40bd3fd545
GIT binary patch
literal 13219
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
Us~jT*{mM2m_kafI+UrmM1%HkcrvLx|

literal 0
HcmV?d00001

diff --git a/js/scrollspy.js b/js/scrollspy.js
deleted file mode 100644
index f860c1abad..0000000000
--- a/js/scrollspy.js
+++ /dev/null
@@ -1,172 +0,0 @@
-/* ========================================================================
- * Bootstrap: scrollspy.js v3.3.4
- * http://getbootstrap.com/javascript/#scrollspy
- * ========================================================================
- * Copyright 2011-2015 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
-
-+function ($) {
-  'use strict';
-
-  // SCROLLSPY CLASS DEFINITION
-  // ==========================
-
-  function ScrollSpy(element, options) {
-    this.$body          = $(document.body)
-    this.$scrollElement = $(element).is(document.body) ? $(window) : $(element)
-    this.options        = $.extend({}, ScrollSpy.DEFAULTS, options)
-    this.selector       = (this.options.target || '') + ' .nav li > a'
-    this.offsets        = []
-    this.targets        = []
-    this.activeTarget   = null
-    this.scrollHeight   = 0
-
-    this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this))
-    this.refresh()
-    this.process()
-  }
-
-  ScrollSpy.VERSION  = '3.3.4'
-
-  ScrollSpy.DEFAULTS = {
-    offset: 10
-  }
-
-  ScrollSpy.prototype.getScrollHeight = function () {
-    return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
-  }
-
-  ScrollSpy.prototype.refresh = function () {
-    var that          = this
-    var offsetMethod  = 'offset'
-    var offsetBase    = 0
-
-    this.offsets      = []
-    this.targets      = []
-    this.scrollHeight = this.getScrollHeight()
-
-    if (!$.isWindow(this.$scrollElement[0])) {
-      offsetMethod = 'position'
-      offsetBase   = this.$scrollElement.scrollTop()
-    }
-
-    this.$body
-      .find(this.selector)
-      .map(function () {
-        var $el   = $(this)
-        var href  = $el.data('target') || $el.attr('href')
-        var $href = /^#./.test(href) && $(href)
-
-        return ($href
-          && $href.length
-          && $href.is(':visible')
-          && [[$href[offsetMethod]().top + offsetBase, href]]) || null
-      })
-      .sort(function (a, b) { return a[0] - b[0] })
-      .each(function () {
-        that.offsets.push(this[0])
-        that.targets.push(this[1])
-      })
-  }
-
-  ScrollSpy.prototype.process = function () {
-    var scrollTop    = this.$scrollElement.scrollTop() + this.options.offset
-    var scrollHeight = this.getScrollHeight()
-    var maxScroll    = this.options.offset + scrollHeight - this.$scrollElement.height()
-    var offsets      = this.offsets
-    var targets      = this.targets
-    var activeTarget = this.activeTarget
-    var i
-
-    if (this.scrollHeight != scrollHeight) {
-      this.refresh()
-    }
-
-    if (scrollTop >= maxScroll) {
-      return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
-    }
-
-    if (activeTarget && scrollTop < offsets[0]) {
-      this.activeTarget = null
-      return this.clear()
-    }
-
-    for (i = offsets.length; i--;) {
-      activeTarget != targets[i]
-        && scrollTop >= offsets[i]
-        && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1])
-        && this.activate(targets[i])
-    }
-  }
-
-  ScrollSpy.prototype.activate = function (target) {
-    this.activeTarget = target
-
-    this.clear()
-
-    var selector = this.selector +
-      '[data-target="' + target + '"],' +
-      this.selector + '[href="' + target + '"]'
-
-    var active = $(selector)
-      .parents('li')
-      .addClass('active')
-
-    if (active.parent('.dropdown-menu').length) {
-      active = active
-        .closest('li.dropdown')
-        .addClass('active')
-    }
-
-    active.trigger('activate.bs.scrollspy')
-  }
-
-  ScrollSpy.prototype.clear = function () {
-    $(this.selector)
-      .parentsUntil(this.options.target, '.active')
-      .removeClass('active')
-  }
-
-
-  // SCROLLSPY PLUGIN DEFINITION
-  // ===========================
-
-  function Plugin(option) {
-    return this.each(function () {
-      var $this   = $(this)
-      var data    = $this.data('bs.scrollspy')
-      var options = typeof option == 'object' && option
-
-      if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
-      if (typeof option == 'string') data[option]()
-    })
-  }
-
-  var old = $.fn.scrollspy
-
-  $.fn.scrollspy             = Plugin
-  $.fn.scrollspy.Constructor = ScrollSpy
-
-
-  // SCROLLSPY NO CONFLICT
-  // =====================
-
-  $.fn.scrollspy.noConflict = function () {
-    $.fn.scrollspy = old
-    return this
-  }
-
-
-  // SCROLLSPY DATA-API
-  // ==================
-
-  $(window).on('load.bs.scrollspy.data-api', function () {
-    $('[data-spy="scroll"]').each(function () {
-      var $spy = $(this)
-      Plugin.call($spy, $spy.data())
-    })
-  })
-
-}(jQuery);
diff --git a/js/src/scrollspy.js b/js/src/scrollspy.js
index 985da708d0..b66f7bb886 100644
--- a/js/src/scrollspy.js
+++ b/js/src/scrollspy.js
@@ -17,11 +17,10 @@ const ScrollSpy = (($) => {
    * ------------------------------------------------------------------------
    */
 
-  const NAME                = 'scrollspy'
-  const VERSION             = '4.0.0'
-  const DATA_KEY            = 'bs.scrollspy'
-  const JQUERY_NO_CONFLICT  = $.fn[NAME]
-  const TRANSITION_DURATION = 150
+  const NAME               = 'scrollspy'
+  const VERSION            = '4.0.0'
+  const DATA_KEY           = 'bs.scrollspy'
+  const JQUERY_NO_CONFLICT = $.fn[NAME]
 
   const Defaults = {
     offset : 10
diff --git a/js/src/tab.js b/js/src/tab.js
new file mode 100644
index 0000000000..4668ff9e69
--- /dev/null
+++ b/js/src/tab.js
@@ -0,0 +1,278 @@
+import Util from './util'
+
+
+/**
+ * --------------------------------------------------------------------------
+ * Bootstrap (v4.0.0): tab.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+
+const Tab = (($) => {
+
+
+  /**
+   * ------------------------------------------------------------------------
+   * Constants
+   * ------------------------------------------------------------------------
+   */
+
+  const NAME                = 'tab'
+  const VERSION             = '4.0.0'
+  const DATA_KEY            = 'bs.tab'
+  const JQUERY_NO_CONFLICT  = $.fn[NAME]
+  const TRANSITION_DURATION = 150
+
+  const Event = {
+    HIDE   : 'hide.bs.tab',
+    HIDDEN : 'hidden.bs.tab',
+    SHOW   : 'show.bs.tab',
+    SHOWN  : 'shown.bs.tab',
+    CLICK  : 'click.bs.tab.data-api'
+  }
+
+  const ClassName = {
+    DROPDOWN_MENU : 'dropdown-menu',
+    ACTIVE        : 'active',
+    FADE          : 'fade',
+    IN            : 'in'
+  }
+
+  const Selector = {
+    A                     : 'a',
+    LI                    : 'li',
+    LI_DROPDOWN           : 'li.dropdown',
+    UL                    : 'ul:not(.dropdown-menu)',
+    FADE_CHILD            : '> .fade',
+    ACTIVE                : '.active',
+    ACTIVE_CHILD          : '> .active',
+    DATA_TOGGLE           : '[data-toggle="tab"], [data-toggle="pill"]',
+    DROPDOWN_ACTIVE_CHILD : '> .dropdown-menu > .active'
+  }
+
+
+  /**
+   * ------------------------------------------------------------------------
+   * Class Definition
+   * ------------------------------------------------------------------------
+   */
+
+  class Tab {
+
+    constructor(element) {
+      this._element = element
+    }
+
+
+    // getters
+
+    static get VERSION() {
+      return VERSION
+    }
+
+    static get Default() {
+      return Default
+    }
+
+
+    // public
+
+    show() {
+      if (this._element.parentNode &&
+         (this._element.parentNode.nodeType == Node.ELEMENT_NODE) &&
+         ($(this._element).parent().hasClass(ClassName.ACTIVE))) {
+        return
+      }
+
+      let target
+      let previous
+      let ulElement = $(this._element).closest(Selector.UL)[0]
+      let selector  = Util.getSelectorFromElement(this._element)
+
+      if (ulElement) {
+        previous = $.makeArray($(ulElement).find(Selector.ACTIVE))
+        previous = previous[previous.length - 1]
+
+        if (previous) {
+          previous = $(previous).find(Selector.A)[0]
+        }
+      }
+
+      let hideEvent = $.Event(Event.HIDE, {
+        relatedTarget: this._element
+      })
+
+      let showEvent = $.Event(Event.SHOW, {
+        relatedTarget: previous
+      })
+
+      if (previous) {
+        $(previous).trigger(hideEvent)
+      }
+
+      $(this._element).trigger(showEvent)
+
+      if (showEvent.isDefaultPrevented() ||
+         (hideEvent.isDefaultPrevented())) {
+        return
+      }
+
+      if (selector) {
+        target = $(selector)[0]
+      }
+
+      this._activate(
+        $(this._element).closest(Selector.LI)[0],
+        ulElement
+      )
+
+      let complete = () => {
+        let hiddenEvent = $.Event(Event.HIDDEN, {
+          relatedTarget: this._element
+        })
+
+        let shownEvent  = $.Event(Event.SHOWN, {
+          relatedTarget: previous
+        })
+
+        $(previous).trigger(hiddenEvent)
+        $(this._element).trigger(shownEvent)
+      }
+
+      if (target) {
+        this._activate(target, target.parentNode, complete)
+      } else {
+        complete()
+      }
+    }
+
+
+    // private
+
+    _activate(element, container, callback) {
+      let active          = $(container).find(Selector.ACTIVE_CHILD)[0]
+      let isTransitioning = callback
+        && Util.supportsTransitionEnd()
+        && ((active && $(active).hasClass(ClassName.FADE))
+           || !!$(container).find(Selector.FADE_CHILD)[0])
+
+      let complete = this._transitionComplete.bind(
+        this, element, active, isTransitioning, callback)
+
+      if (active && isTransitioning) {
+        $(active)
+          .one(Util.TRANSITION_END, complete)
+          .emulateTransitionEnd(TRANSITION_DURATION)
+
+      } else {
+        complete()
+      }
+
+      if (active) {
+        $(active).removeClass(ClassName.IN)
+      }
+    }
+
+    _transitionComplete(element, active, isTransitioning, callback) {
+      if (active) {
+        $(active).removeClass(ClassName.ACTIVE)
+
+        let dropdownChild = $(active).find(
+          Selector.DROPDOWN_ACTIVE_CHILD
+        )[0]
+        if (dropdownChild) {
+          $(dropdownChild).removeClass(ClassName.ACTIVE)
+        }
+
+        let activeToggle = $(active).find(Selector.DATA_TOGGLE)[0]
+        if (activeToggle) {
+          activeToggle.setAttribute('aria-expanded', false)
+        }
+      }
+
+      $(element).addClass(ClassName.ACTIVE)
+
+      let elementToggle = $(element).find(Selector.DATA_TOGGLE)[0]
+      if (elementToggle) {
+        elementToggle.setAttribute('aria-expanded', true)
+      }
+
+      if (isTransitioning) {
+        Util.reflow(element)
+        $(element).addClass(ClassName.IN)
+      } else {
+        $(element).removeClass(ClassName.FADE)
+      }
+
+      if (element.parentNode &&
+         ($(element.parentNode).hasClass(ClassName.DROPDOWN_MENU))) {
+
+        let dropdownElement = $(element).closest(Selector.LI_DROPDOWN)[0]
+        if (dropdownElement) {
+          $(dropdownElement).addClass(ClassName.ACTIVE)
+        }
+
+        elementToggle = $(element).find(Selector.DATA_TOGGLE)[0]
+        if (elementToggle) {
+          elementToggle.setAttribute('aria-expanded', true)
+        }
+      }
+
+      if (callback) {
+        callback()
+      }
+    }
+
+
+    // static
+
+    static _jQueryInterface(config) {
+      return this.each(function () {
+        let $this = $(this)
+        let data  = $this.data(DATA_KEY)
+
+        if (!data) {
+          data = data = new Tab(this)
+          $this.data(DATA_KEY, data)
+        }
+
+        if (typeof config === 'string') {
+          data[config]()
+        }
+      })
+    }
+
+  }
+
+
+  /**
+   * ------------------------------------------------------------------------
+   * Data Api implementation
+   * ------------------------------------------------------------------------
+   */
+
+  $(document)
+    .on(Event.CLICK, Selector.DATA_TOGGLE, function (event) {
+    event.preventDefault()
+    Tab._jQueryInterface.call($(this), 'show')
+  })
+
+
+  /**
+   * ------------------------------------------------------------------------
+   * jQuery
+   * ------------------------------------------------------------------------
+   */
+
+  $.fn[NAME]             = Tab._jQueryInterface
+  $.fn[NAME].Constructor = Tab
+  $.fn[NAME].noConflict  = function () {
+    $.fn[NAME] = JQUERY_NO_CONFLICT
+    return Tab._jQueryInterface
+  }
+
+  return Tab
+
+})(jQuery)
+
+export default Tab
diff --git a/js/tests/index.html b/js/tests/index.html
index 0262eec2a9..6f325115bb 100644
--- a/js/tests/index.html
+++ b/js/tests/index.html
@@ -138,9 +138,9 @@
     <script src="../../js/dist/dropdown.js"></script>
     <script src="../../js/dist/modal.js"></script>
     <script src="../../js/dist/scrollspy.js"></script>
+    <script src="../../js/dist/tab.js"></script>
 
     <!-- Old Plugin sources -->
-    <script src="../../js/tab.js"></script>
     <script src="../../js/tooltip.js"></script>
     <script src="../../js/popover.js"></script>
 
diff --git a/js/tests/visual/tab.html b/js/tests/visual/tab.html
index a3fed1e9fa..ff6685fede 100644
--- a/js/tests/visual/tab.html
+++ b/js/tests/visual/tab.html
@@ -35,10 +35,10 @@
   <h4>Tabs without fade</h4>
 
   <ul id="myTab" class="nav nav-tabs">
-    <li class="active"><a href="#home" data-toggle="tab">Home</a></li>
-    <li><a href="#profile" data-toggle="tab">Profile</a></li>
-    <li class="dropdown">
-      <a href="#" id="myTabDrop1" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
+    <li class="active nav-item"><a href="#home" class="nav-link" data-toggle="tab">Home</a></li>
+    <li class="nav-item"><a href="#profile" data-toggle="tab" class="nav-link">Profile</a></li>
+    <li class="dropdown nav-item">
+      <a href="#" id="myTabDrop1" class="dropdown-toggle nav-link" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
       <ul class="dropdown-menu" role="menu" aria-labelledby="myTabDrop1">
         <li><a href="#dropdown1" tabindex="-1" data-toggle="tab">@fat</a></li>
         <li><a href="#dropdown2" tabindex="-1" data-toggle="tab">@mdo</a></li>
@@ -67,10 +67,10 @@
   <h4>Tabs with fade</h4>
 
   <ul id="myTab1" class="nav nav-tabs">
-    <li class="active"><a href="#home1" data-toggle="tab">Home</a></li>
-    <li><a href="#profile1" data-toggle="tab">Profile</a></li>
-    <li class="dropdown">
-      <a href="#" id="myTabDrop2" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
+    <li class="active nav-item"><a class="nav-link" href="#home1" data-toggle="tab">Home</a></li>
+    <li class="nav-item"><a class="nav-link" href="#profile1" data-toggle="tab">Profile</a></li>
+    <li class="dropdown nav-item">
+      <a href="#" id="myTabDrop2" class="dropdown-toggle nav-link" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
       <ul class="dropdown-menu" role="menu" aria-labelledby="myTabDrop2">
         <li><a href="#dropdown1-1" tabindex="-1" data-toggle="tab">@fat</a></li>
         <li><a href="#dropdown1-2" tabindex="-1" data-toggle="tab">@mdo</a></li>
@@ -99,10 +99,10 @@
   <h4>Tabs without fade (no initially active pane)</h4>
 
   <ul id="myTab2" class="nav nav-tabs">
-    <li><a href="#home2" data-toggle="tab">Home</a></li>
-    <li><a href="#profile2" data-toggle="tab">Profile</a></li>
-    <li class="dropdown">
-      <a href="#" id="myTabDrop3" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
+    <li class="nav-item"><a class="nav-link" href="#home2" data-toggle="tab">Home</a></li>
+    <li class="nav-item"><a class="nav-link" href="#profile2" data-toggle="tab">Profile</a></li>
+    <li class="dropdown nav-item">
+      <a href="#" id="myTabDrop3" class="dropdown-toggle nav-link" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
       <ul class="dropdown-menu" role="menu" aria-labelledby="myTabDrop3">
         <li><a href="#dropdown2-1" tabindex="-1" data-toggle="tab">@fat</a></li>
         <li><a href="#dropdown2-2" tabindex="-1" data-toggle="tab">@mdo</a></li>
@@ -131,10 +131,10 @@
   <h4>Tabs with fade (no initially active pane)</h4>
 
   <ul id="myTab3" class="nav nav-tabs">
-    <li><a href="#home3" data-toggle="tab">Home</a></li>
-    <li><a href="#profile3" data-toggle="tab">Profile</a></li>
-    <li class="dropdown">
-      <a href="#" id="myTabDrop4" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
+    <li class="nav-item"><a class="nav-link" href="#home3" data-toggle="tab">Home</a></li>
+    <li class="nav-item"><a class="nav-link" href="#profile3" data-toggle="tab">Profile</a></li>
+    <li class="dropdown nav-item">
+      <a href="#" id="myTabDrop4" class="dropdown-toggle nav-link" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
       <ul class="dropdown-menu" role="menu" aria-labelledby="myTabDrop4">
         <li><a href="#dropdown3-1" tabindex="-1" data-toggle="tab">@fat</a></li>
         <li><a href="#dropdown3-2" tabindex="-1" data-toggle="tab">@mdo</a></li>
@@ -164,9 +164,9 @@
 
 <!-- JavaScript Includes -->
 <script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
-<script src="../../tab.js"></script>
-<script src="../../dropdown.js"></script>
+<script src="../../dist/util.js"></script>
+<script src="../../dist/tab.js"></script>
+<script src="../../dist/dropdown.js"></script>
 
 </body>
 </html>
-- 
GitLab