component-tabs.js

/**
 * Component Tabs
 * @module Tabs
 * @param  {jQuery} $ Instance of jQuery
 * @return {Object} List of tabs methods
 */
XA.component.tabs = (function($) {
    /**
     * This object stores all public api methods
     * @type {Object.<Methods>}
     * @memberOf module:Tabs
     * @
     * */
    var api = {},
        instance;
    /**
     * isFlipInside chech if inside Tab component present Flip component
     * @memberOf module:Tab
     * @method
     * @param {jQuery} $instance Tabs Root DOM element of tab component wrapped by jQuery
     * @alias module:Tab.isFlipInside
     * @return {boolean}
     * @private
     */
    var isFlipInside = function(instance) {
        return !!instance.find(".component.flip").length;
    };
    /**
     * tabsScrollable modify tabs to support scroll for tabs header
     * @memberOf module:Tab
     * @method
     * @param {jQuery} $tabsScroll Tabs Root DOM element of tab component wrapped by jQuery
     * @alias module:Tab.tabsScrollable
     * @private
     */
    function tabsScrollable($tabsScroll) {
        var speed = 150; //tabs scroll speed

        function initNavScroll($tabsNav) {
            var sum = 0,
                maxHeight = 0;

            if ($tabsNav.length) {
                $tabsNav
                    .parent()
                    .find(".prev")
                    .remove();
                $tabsNav
                    .parent()
                    .find(".next")
                    .remove();
                $tabsNav.unwrap();

                $tabsNav.css("width", "auto");
                $tabsNav.css("height", "auto");
                $tabsNav.css("left", 0);
            }

            $tabsNav.find("li").each(function() {
                sum += $(this).outerWidth(true);
            });

            $tabsNav.find("li").each(function() {
                maxHeight = Math.max(maxHeight, $(this).height());
            });

            $tabsNav.wrap("<div class='wrapper'>");
            $("<div class='next tab-slider'>></div>").insertAfter($tabsNav);
            $("<div class='prev tab-slider'><</div>").insertBefore($tabsNav);

            $tabsNav.parent().css("height", parseInt(maxHeight, 10));
            $tabsNav
                .parent()
                .find(".tab-slider")
                .css("height", parseInt(maxHeight, 10) - 2);
            //fix for 8111
            sum += 10;
            if (sum > $tabsNav.parent().width()) {
                $tabsNav
                    .parent()
                    .find(".prev")
                    .hide();
                $tabsNav.width(sum);
            } else {
                $tabsNav
                    .parent()
                    .find(".prev")
                    .hide();
                $tabsNav
                    .parent()
                    .find(".next")
                    .hide();
            }
        }

        function bindPrevNextEvents(current, $tabsNav) {
            current.find(".prev").click(function() {
                var left = parseInt($tabsNav.css("left"), 10);
                left += speed;

                if (left > 0) {
                    left = 0;
                    $tabsNav.stop().animate({
                        left: left
                    });

                    $tabsNav
                        .parent()
                        .find(".prev")
                        .hide();
                    $tabsNav
                        .parent()
                        .find(".next")
                        .show();
                } else {
                    $tabsNav.stop().animate({
                        left: left
                    });

                    $tabsNav
                        .parent()
                        .find(".prev")
                        .show();
                    $tabsNav
                        .parent()
                        .find(".next")
                        .show();
                }
            });

            current.find(".next").click(function() {
                var left = parseInt($tabsNav.css("left"), 10),
                    navWidth = $tabsNav.width(),
                    navParentWidth = $tabsNav.parent().width();

                left -= speed;

                if (navWidth + left < navParentWidth) {
                    left = navWidth - navParentWidth + 20;
                    $tabsNav.stop().animate({
                        left: -left
                    });

                    $tabsNav
                        .parent()
                        .find(".prev")
                        .show();
                    $tabsNav
                        .parent()
                        .find(".next")
                        .hide();
                } else {
                    $tabsNav.stop().animate({
                        left: left
                    });

                    $tabsNav
                        .parent()
                        .find(".prev")
                        .show();
                    $tabsNav
                        .parent()
                        .find(".next")
                        .show();
                }
            });
        }

        function bindChangeTabs($tabsNav, $tabsContainer) {
            $tabsNav.find("li").click(function(event) {
                var index = $(this).index(),
                    instance = $(this).closest(".component.tabs");

                $(this).addClass("active");
                $(this)
                    .siblings()
                    .removeClass("active");

                $tabsContainer.find(".tab").removeClass("active");
                $tabsContainer
                    .find(".tab:eq(" + index + ")")
                    .addClass("active");

                if (isFlipInside(instance)) {
                    try {
                        XA.component.flip.equalSideHeight(instance);
                    } catch (e) {
                        /* eslint-disable no-console */
                        console.warn(
                            "Error during calculation height of Flip list in toggle"
                        ); // jshint ignore:line
                        /* eslint-enable no-console */
                    }
                }
                event.preventDefault();
            });
        }

        function initTabsScrollable($tabs) {
            $tabs.each(function() {
                var $tabsNav = $(this).find(".tabs-heading"),
                    $tabsContainer = $(this).find(".tabs-container");

                $tabsNav.find("li:first-child").addClass("active");
                $tabsContainer.find(".tab:eq(0)").addClass("active");

                bindChangeTabs($tabsNav, $tabsContainer);
                initNavScroll($tabsNav);
                bindPrevNextEvents($(this), $tabsNav);
            });
        }

        initTabsScrollable($tabsScroll);
        $(window).resize(function() {
            initTabsScrollable($tabsScroll);
        });
    }
    /**
     * selectActiveTab activate selected tab
     * @memberOf module:Tab
     * @method
     * @param {jQuery} $tabNav Tabs header DOM element of tab component wrapped by jQuery
     * @param {jQuery} $tabs Tabs content DOM element of tab component wrapped by jQuery
     * @alias module:Tab.selectActiveTab
     * @private
     */
    function selectActiveTab($tabNav, $tabs) {
        var tabRootId = $tabNav.closest(".component.tabs").attr("id");
        var tabToSelect = XA.queryString.getQueryParam(tabRootId || "tab");

        if (
            tabToSelect != null &&
            !isNaN(parseInt(tabToSelect)) &&
            isFinite(tabToSelect)
        ) {
            tabToSelect = parseInt(tabToSelect);
            if ($tabNav.length > tabToSelect) {
                $($tabNav[tabToSelect]).addClass("active");
                $($tabs[tabToSelect]).addClass("active");

                return;
            }
        }
        $tabNav.first().addClass("active");
        $tabNav.first().attr("tabindex", "0");
        $tabs.first().addClass("active");
        $tabNav.first("li").focus();
    }

    /**
     * initInstance initializes component according to his option
     * @memberOf module:Tabs
     * @method
     * @param {jQuery} component Root DOM element of archive component wrapped by jQuery
     * @alias module:Tabs.initInstance
     */
    api.initInstance = function(component) {
        var $tabModule = component.find(".tabs-inner");
        if (component.hasClass("tabs-scrollable")) {
            tabsScrollable(component);
        } else {
            $tabModule.each(function() {
                var $tabNav = $(this).find(".tabs-heading > li"),
                    $tabs = $(this).find("> .tabs-container > .tab");

                selectActiveTab($tabNav, $tabs);

                $tabNav
                    .click(function(event) {
                        var current = $(this),
                            index = current.index(),
                            tabInner = current.parent().parent();

                        current.siblings().removeClass("active");
                        tabInner
                            .find("> .tabs-container > .tab")
                            .removeClass("active");
                        current.addClass("active");
                        $(
                            tabInner
                                .children(".tabs-container")
                                .children(".tab")
                                .eq(index)
                        ).addClass("active");
                        if (isFlipInside(component)) {
                            try {
                                XA.component.flip.equalSideHeight(component);
                            } catch (e) {
                                /* eslint-disable no-console */
                                console.warn(
                                    "Error during calculation height of Flip list in toggle"
                                ); // jshint ignore:line
                                /* eslint-enable no-console */
                            }
                        }
                        current.focus();
                        event.preventDefault();
                    })
                    .keyup(function(event) {
                        var currentHeading = $(this);
                        switch (event.keyCode) {
                            case 38: {
                                currentHeading.prev("li").click();
                                break;
                            }
                            case 40: {
                                currentHeading.next("li").click();
                                break;
                            }
                        }
                    });
            });
        }

        if (isFlipInside(component)) {
            try {
                XA.component.flip.equalSideHeight(component);
            } catch (e) {
                /* eslint-disable no-console */
                console.warn(
                    "Error during calculation height of Flip list in toggle"
                ); // jshint ignore:line
                /* eslint-enable no-console */
            }
        }
    };
    /**
     * init method calls in a loop for each
     * tabs component on a page and runs Tab's
     * ["initInstance"]{@link module:Tabs.api.initInstance} method
     * @memberOf module:Tabs
     * @alias module:Tabs.init
     */
    api.init = function() {
        var $tabs = $(".tabs:not(.initialized)");
        $tabs.each(function() {
            var instance = $(this);
            api.initInstance(instance);
            $(this).addClass("initialized");
        });
    };

    return api;
})(jQuery);

XA.register("tabs", XA.component.tabs);