component-accordions.js

/* global _:true */
/**
 * Component Accordion
 * @module Accordion
 * @param  {jQuery} $ Instance of jQuery
 * @return {Object} List of accordion methods
 */
XA.component.accordions = (function($) {
    /**
     * This object stores all public api methods
     * @type {Object.<Methods>}
     * @memberOf module:Accordion
     * */
    var api = {};
    /**
     * toogleEvents list of toggle events
     * @memberOf module:Accordion
     * @private
     */
    var toogleEvents = {
        focus: function() {
            $(this).addClass("show");
        },
        blur: function() {
            $(this).removeClass("show");
        }
    };
    /**
     * headerBackground method set up background image for toggle-header
     * @memberOf module:Accordion
     * @param {DomElement} header toggle-header DOM element of accordion component
     * @alias module:Accordion.headerBackground
     * @private
     */
    function headerBackground(header) {
        var backgroundIcon = $(header),
            background = $(header).find("img"),
            backgroundSrc = background.attr("src");

        if (backgroundSrc) {
            backgroundIcon.parents(".accordion").addClass("accordion-image");

            backgroundIcon.css({
                background: "url(" + backgroundSrc + ")",
                "background-repeat": "no-repeat",
                "background-size": "cover",
                "background-position": "50% 100%"
            });

            background.hide();
        }
    }
    /**
     * calcSlideSize method calculate and set up width of accordion items
     * @memberOf module:Accordion
     * @param {DomElement} acc Root Dom element of accordion component
     * @alias module:Accordion.calcSlideSize
     * @private
     */
    function calcSlideSize(acc) {
        var accordionWidth = $(acc).width(),
            accordionItems = $(acc).find(".item"),
            maxHeight = 0;

        _.each(accordionItems, function(item) {
            var itemContent = $(item).find(".toggle-content"),
                itemHeader = $(item).find(".toggle-header"),
                slideWidth =
                    accordionWidth -
                    accordionItems.length * itemHeader.outerWidth();

            if ($(item).hasClass("active")) {
                $(item).css({
                    width: slideWidth
                });
            }

            //width
            itemContent.css({
                width: $(acc).hasClass("accordion-image")
                    ? slideWidth + itemHeader.outerWidth()
                    : slideWidth -
                      itemHeader.outerWidth() -
                      parseInt(itemHeader.css("padding"))
            });

            //height
            if (
                $(item)
                    .find(".toggle-content")
                    .height() > maxHeight
            ) {
                maxHeight = $(item)
                    .find(".toggle-content")
                    .height();
            }
        });
    }

    /**
     * animateHorizontal method set up animation for horizontally oriented
     * accordion component
     * @memberOf module:Accordion
     * @param {Object} properties list of properties for accordion component
     * @alias module:Accordion.animateHorizontal
     */
    api.animateHorizontal = function(properties) {
        var accordion = $(this).parents(".accordion"),
            panel = $(this).closest(".item"),
            header = panel.find(".toggle-header"),
            content = panel.find(".toggle-content"),
            items = accordion.find(".item"),
            siblings = panel.siblings(),
            siblingsContent = siblings.find(".toggle-content");

        panel.toggleClass("active");
        siblings.removeClass("active");
        siblings.stop(true).animate(
            {
                width: 0
            },
            properties.speed,
            properties.easing,
            function() {
                siblingsContent.css({
                    display: "none"
                });
            }
        );

        if (panel.hasClass("active")) {
            var slideWidth = accordion.hasClass("accordion-image")
                    ? content.outerWidth()
                    : accordion.width() -
                      ((items.length - 1) * panel.outerWidth() + 2),
                contentWidth = accordion.hasClass("accordion-image")
                    ? slideWidth
                    : slideWidth - header.outerWidth();

            panel.stop(true).animate(
                {
                    width: slideWidth
                },
                properties.speed,
                properties.easing,
                function() {}
            );

            content.css({
                width: contentWidth,
                display: "block"
            });
        } else {
            panel.stop(true).animate(
                {
                    width: 0
                },
                properties.speed,
                properties.easing,
                function() {
                    content.css({
                        display: "none"
                    });
                }
            );
        }
    };
    /**
     * getQueryParamKey get an Id of accordion component
     * @memberOf module:Accordion
     * @param {jQuery} accordions  Root Dom element of accordion component
     * @private
     * @return {null|string} return id of accordion component
     */
    function getQueryParamKey(accordions) {
        var firstAccordionId = accordions[0].id;
        if (accordions.length > 0 && firstAccordionId != "") {
            return firstAccordionId.toLocaleLowerCase();
        }
        return null;
    }
    /**
     * accordion method that sets up watchers that animate accordion slide
     * @memberOf module:Accordion
     * @method
     * @private
     * @param {jQuery} acc Root DOM element of archive component wrapped by jQuery
     * @param {Object} prop list of properties for accordion component
     * @alias module:Accordion.accordion
     */
    function accordion(acc, properties) {
        var ev = "click",
            toggleHeader = acc.find(".toggle-header");

        if (properties.expandOnHover) {
            ev = "mouseenter";
        }

        toggleHeader.on("mouseover", toogleEvents.focus);
        toggleHeader.on("mouseleave", toogleEvents.blur);
        toggleHeader.on("focus", toogleEvents.focus);
        toggleHeader.on("blur", toogleEvents.blur);
        toggleHeader.on("keyup", function(e) {
            if (e.keyCode == 13 || e.keyCode == 32) {
                $(this).click();
            }
        });

        if (acc.hasClass("accordion-horizontal")) {
            //calc slide width
            $(document).ready(function() {
                calcSlideSize(acc);
            });

            _.each(toggleHeader, function(header) {
                headerBackground(header);
            });
        }

        toggleHeader.on(ev, function() {
            var $this = $(this),
                panel = $this.closest(".item"),
                accordion = $this.parents(".accordion"),
                content = panel.find(".toggle-content"),
                siblings = panel.siblings(),
                siblingsContent = siblings.find(".toggle-content");

            // Horizontal animation
            if (accordion.hasClass("accordion-horizontal")) {
                api.animateHorizontal.call(this, properties);
                // Vertical animation
            } else {
                // Closes all other items if open
                // multiple option disabled
                if (!properties.canOpenMultiple) {
                    siblings.removeClass("active");
                    siblingsContent.stop().slideUp({
                        duration: properties.speed,
                        easing: properties.easing
                    });
                }

                panel.toggleClass("active");
                // Toggle state of selected item
                // If toggle state is enabled
                if (properties.canToggle) {
                    content.slideToggle({
                        duration: properties.speed,
                        easing: properties.easing
                    });
                } else {
                    content.slideDown({
                        duration: properties.speed,
                        easing: properties.easing
                    });
                }
            }
        });
    }
    /**
     * initInstance method prepare Dom elements according accordion setup
     * and call ["accordion"]{@link module:Accordion.accordion} method
     * @memberOf module:Acordion
     * @method
     * @param {jQuery} component Root DOM element of archive component wrapped by jQuery
     * @param {Object} prop list of properties for accordion component
     * @alias module:Accordion.initInstance
     */
    api.initInstance = function(component, prop) {
        // Set tabindex=0 for first header
        component
            .find(".toggle-header")
            .eq(0)
            .attr("tabindex", "0");
        //
        if (component.hasClass("toggle")) {
            $.extend(prop, {
                canToggle: true
            });
        }
        component.find(".toggle-content").hide();
        var current = XA.queryString.getQueryParam(getQueryParamKey(component));
        if (current != null) {
            var arr = current.split(",");
            var items = component.find("li");
            for (var index = 0; index < items.length; index++) {
                var element = items[index];
                if (arr.indexOf(index + "") > -1) {
                    $(element).addClass("active");
                    $(element)
                        .find(".toggle-content")
                        .show();
                }
            }
        } else if (prop.expandedByDefault) {
            component.find("li:first-child").addClass("active");
            component
                .find("li:first-child")
                .find(".toggle-content")
                .show();
        }
        accordion(component, prop);
    };
    /**
     * init method calls in a loop for each
     * accordions component on a page and run Accordions
     * [".initInstance"]{@link module:Accordion.initInstance} method.
     * @memberOf module:Accordion
     * @alias module:Accordion.init
     */
    api.init = function() {
        var accordions = $(".accordion:not(.initialized)");
        accordions.each(function() {
            var properties = $(this).data("properties"),
                acc = $(this);
            api.initInstance(acc, properties);
            acc.addClass("initialized");
        });
    };

    return api;
})(jQuery);

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