'use strict';

// Classe
// ----------------------------------------------------------
/**
 * @param {object} settings - Objeto de configuração da barra de menu
 * {
 *  accordion:{ keepOpen:[boolean] },
 *  overlay: {
 *      show: [boolean],
 *      bgColor: [color value]
 *  },
 *  animation: {
 *      show: [boolean],
 *      name: ['animation-name']
 *  },
 *  panel: {
 *      offset: [number]
 *  }
 * }
 */
class Menubar {
    static #config;
    static #settings;
    static #animationShow;
    static #animationName;
    static #menubar;
    static #menubarVertical;
    static #toolbar;    
    static #mainNavigation;
    static #mobileButton;
    static #mobileNavigation;    
    static #actionButton;
    static #userPanel;        
    static #accordion;
    static #collapseMenu


    constructor(settings) {  
        Menubar.#config = this.#configs();        
        Menubar.#settings = {...Menubar.#config.settings, ...settings};
        Menubar.#animationShow = Menubar.#settings.animation.show !== undefined ? Menubar.#settings.animation.show : Menubar.#config.settings.animation.show;
        Menubar.#animationName = Menubar.#settings.animation.name ? Menubar.#settings.animation.name : Menubar.#config.settings.animation.name;
        Menubar.#menubar = document.querySelector(Menubar.#config.selectors.menubar);
        Menubar.#menubarVertical = document.querySelector(Menubar.#config.selectors.menubarVertical);        
        Menubar.#toolbar = document.querySelector(Menubar.#config.selectors.toolbar);
        Menubar.#mainNavigation = Menubar.#menubar.querySelector(Menubar.#config.selectors.mainNavigation);
        Menubar.#mobileButton = Menubar.#menubar.querySelector(Menubar.#config.selectors.mobileButton);
        Menubar.#mobileNavigation = Menubar.#menubar.querySelector(Menubar.#config.selectors.mobileNavigation);         
        Menubar.#actionButton = document.querySelectorAll(Menubar.#config.selectors.actionButton);           
        Menubar.#accordion = document.querySelectorAll(Menubar.#config.selectors.accordion);
        Menubar.#collapseMenu = Menubar.#menubar.querySelector(Menubar.#config.selectors.switchMenuPosition);
        
        // init
        this.#init();     
    };      

    /**
     * Método de configuração
     * @private
     * @returns {object} config
     */    
    #configs() {
        const config = {
            selectors: {
                menubar: '.ae-menubar',
                menubarVertical: '.ae-menubar.menubar[aria-orientation="vertical"]',
                toolbar: '.ae-menubar [role="toolbar"]',
                mainNavigation: '.main-navigation',
                hasSubmenu: '[role="menuitem"][aria-haspopup="true"]',
                isActive: '[role="menuitem"][data-active="true"]',
                mobileButton: '.menu-mobile-control',
                mobileButtonIconControl: '#hamburger-icon-control',
                mobileNavigation: '.ae-menu-mobile',                
                mobileNavigationOverlay: '#mobile-navigation-overlay',
                panels: '.panel',                
                actionButton: '.action-button',
                userActionsMenu: '.section.actions',
                accordion: '.ae-menubar-accordion',
                switchMenuPosition: '#switch-menu-position',
                wrapperUser: '.wrapper-user',
                userButton: '.user.action-button',
                userMenu: '.user-menu',
                userInfo: '.user-info',
                userMenuContent: '.user-menu-content',
            },            
            menubar: {
                loadingTime: 500
            },
            settings: {
                keepOpen: false,
                animation: {
                    show: true,
                    name: 'slide-left'
                },
                overlay: {
                    show: true,
                    bgColor: 'rgba(0,0,0,.55)'
                },
                panel: {
                    offset: 4, //pixels
                    animation: 'slide-right-top'
                }
            },
            layers: {
                layer_1: 'layer-900',
                layer_2: 'layer-800',
            },
            animation: {
                className: [
                    'slide-top-bouncy',
                    'slide-right-bouncy',
                    'slide-bottom-bouncy',
                    'slide-left-bouncy',
                    'slide-top',
                    'slide-right',
                    'slide-bottom',
                    'slide-left',
                    'reveal-opacity',
                    'reveal-blur'
                ],
                duration: 650,
            }
        }
        return config;
    }

    #init(){        
        // inic. métodos
        // -----------------------------------
        if(Menubar.#menubar && Menubar.#menubar.classList.contains('menubar') && Menubar.#menubar.getAttribute('aria-orientation') === 'horizontal') {
            if(Menubar.#mainNavigation){
                this.#responsiveObserver()
            } else if(Menubar.#mobileNavigation) {
                Menubar.#mobileButton.style.display = 'grid';
                Menubar.#mobileNavigation.style.top = `${Menubar.#menubar.getBoundingClientRect().height}px`;
                Menubar.#mobileNavigation.classList.add(Menubar.#config.layers.layer_1);                                
            }
        }

        if(Menubar.#mobileButton && Menubar.#mobileNavigation) {
            this.#mobileControl();  
            
            document.addEventListener('click', () => {
                if(Menubar.#mobileButton.getAttribute('aria-pressed') === 'true') {
                    Menubar.#mobileButton.click();
                }
            });
        }        
        
        if(Menubar.#accordion) {
            Menubar.#accordion.forEach(accordion => {
                this.#menubarAccordion(accordion);
            });  
        }        

        if(Menubar.#menubarVertical) {
            if(Menubar.#collapseMenu) {
                Menubar.#collapseMenu.addEventListener('click', () => { 
                    
                    if(Menubar.#collapseMenu.getAttribute('aria-checked') === 'false') {
                        Menubar.#collapseMenu.setAttribute('aria-checked', 'true');
                        Menubar.#collapseMenu.classList.remove('fa-expand');
                        Menubar.#collapseMenu.classList.add('fa-compress');
                    } else {
                        Menubar.#collapseMenu.setAttribute('aria-checked', 'false');
                        Menubar.#collapseMenu.classList.remove('fa-compress');
                        Menubar.#collapseMenu.classList.add('fa-expand');
                    }
                    
                    if(Menubar.#menubarVertical.getAttribute('data-collapsible') === 'true') {
                        Menubar.#menubarVertical.setAttribute('data-collapsible', 'false');
                    } else {
                        Menubar.#menubarVertical.setAttribute('data-collapsible', 'true');
                    }

                    this.#collapsibleMenu();
                });
            }
            
            Menubar.#menubarVertical.addEventListener('mouseenter', () => {
                if(Menubar.#menubarVertical.classList.contains('collapsed')) {
                    this.#collapsibleMenu(); 
                }
            });
            Menubar.#menubarVertical.addEventListener('mouseleave', () => {
                if(Menubar.#menubarVertical.classList.contains('expanded')) {
                    this.#collapsibleMenu();
                }
            });               
            
            if(Menubar.#menubarVertical.querySelector(Menubar.#config.selectors.wrapperUser)) {
                this.#userMenu();
            }
        }
                
        if(document.querySelectorAll(`${Menubar.#config.selectors.menubar} ${Menubar.#config.selectors.panels}`).length !== 0) {
            this.#panel();            
            
            document.addEventListener('click', () => {
                this.#closepanelOpened();
            });
        }

        this.#close();
        
        // Configurações do objeto
        // -----------------------------------
        Object.preventExtensions(this);      
    }
   
    /**
     * Controla a responsividade da barra de menu e de seus elementos
     * @private
     */
    #responsiveObserver(){
        const gridGap = window.getComputedStyle(Menubar.#menubar.firstElementChild).columnGap;
        const menubarSplit = document.querySelector('.ae-menubar.menubar.split');            

        const mainNavigationControl = (visibility) => {
            if(visibility === 'show') {
                Menubar.#mobileButton.style.display = 'none';
                Menubar.#mainNavigation.style.display = 'flex';  
                Menubar.#mainNavigation.classList.remove('hide');
                Menubar.#mainNavigation.removeAttribute('aria-hidden');           
                Menubar.#mainNavigation.removeAttribute('aria-hidden');
            } else if (visibility === 'hide') {
                Menubar.#mobileButton.style.display = 'grid';                        
                Menubar.#mainNavigation.style.display = 'none';  
                Menubar.#mainNavigation.classList.add('hide');
                Menubar.#mainNavigation.classList.remove('show');
                Menubar.#mainNavigation.setAttribute('aria-hidden', 'true');
            }
        }        

        const responseMobile = () => {
            //oculta mainNavigation
            mainNavigationControl('hide');                                                   
        };

        setTimeout(() => {
            let menubarMinWidth = null;                           

            if(menubarSplit) {
                menubarMinWidth = Menubar.#mainNavigation.offsetWidth;
            } else {
                menubarMinWidth = sumElementsWidth(Array.from(Menubar.#menubar.children).splice(0,2));
            }
            
            const mobileObserver = new ResizeObserver(entries => {
                entries.forEach(entry => {            
                    if(entry.target.classList.contains((Menubar.#config.selectors.menubar).replace('.',''))) {                
                        if(menubarMinWidth > entry.borderBoxSize[0].inlineSize){                
                            responseMobile();                                                
                        } else{                
                            mainNavigationControl('show');                             
                            if(Menubar.#mobileNavigation && Menubar.#mobileButton.getAttribute('aria-pressed') === 'true'){
                                this.#mobileButtonControl();
                                this.#mobileNavigationControl();
                            }
                        }
                    }           
                });     
            });
            mobileObserver.observe(Menubar.#menubar);
        
            if(Menubar.#mobileNavigation){
                Menubar.#mobileNavigation.style.top = `${Menubar.#menubar.getBoundingClientRect().height}px`;
                Menubar.#mobileNavigation.classList.add(Menubar.#config.layers.layer_1);                
            }

        }, Menubar.#config.menubar.loadingTime);        
    }

    /**
     * Controla o botão de disparo do menu mobile
     * @private
     */
    #mobileButtonControl(){
        const mobileButtonIconControl = Menubar.#mobileButton.querySelector(Menubar.#config.selectors.mobileButtonIconControl);
        let ariaPressed = Menubar.#mobileButton.getAttribute('aria-pressed');        
                                
        if(ariaPressed === 'true'){
            Menubar.#mobileButton.setAttribute('aria-pressed', 'false');
            mobileButtonIconControl.checked = false;

        } else{
            Menubar.#mobileButton.setAttribute('aria-pressed', 'true');
            mobileButtonIconControl.checked = true;             
        }
    }
    
    /**
     * Controla a visibilidade e animação da navegação mobile
     * @private
     */
    #mobileNavigationControl() {
        const overlayShow = Menubar.#settings.overlay.show !== undefined ? Menubar.#settings.overlay.show : Menubar.#config.settings.overlay.show;

        const showMobile = () => {
            // props
            Menubar.#mobileNavigation.setAttribute('aria-hidden', 'false');
            Menubar.#mobileNavigation.setAttribute('data-show', 'true');
            if(!Menubar.#animationShow) {
                Menubar.#mobileNavigation.style.setProperty('display', 'block');            
            }
            // adiciona overlay   
            if(overlayShow) {                
                document.body.appendChild(this.#createOverlay());                
                Menubar.#mobileButton.classList.add(Menubar.#config.layers.layer_1);       
            }     
            // Fecha o painel aberto
            this.#closepanelOpened();
        }
        const hideMobile = () => {
            // props
            Menubar.#mobileNavigation.setAttribute('aria-hidden', 'true');
            Menubar.#mobileNavigation.setAttribute('data-show', 'false'); 
            if(!Menubar.#animationShow) {
                Menubar.#mobileNavigation.style.setProperty('display', 'none');            
            }     
            // remove overlay
            if(overlayShow) {
                this.#removeOverlay(document.getElementById(Menubar.#config.selectors.mobileNavigationOverlay));                                                                           
            }
        }

        if(Menubar.#mobileNavigation.getAttribute('data-show') === 'false') {
            showMobile();
        }   
        else {
            hideMobile();
        }

        // controle de animação
        if(Menubar.#animationShow) {
            this.#animationClassControl(Menubar.#mobileNavigation);
        }
    }   

    /**
     * Controla o status do botão mobile, a exibição do menu mobile e configurações iniciais do menu
     * @private
     */
    #mobileControl(){
        if(Menubar.#mobileButton) {                        
            Menubar.#mobileButton.addEventListener('click', () => {
                event.preventDefault();   
                event.stopImmediatePropagation();      
                this.#mobileButtonControl();
                this.#mobileNavigationControl();
                this.#isActive();                                           
            });            
        }

        if(Menubar.#mobileNavigation) {
            const mainNavMobile = Menubar.#mobileNavigation.querySelector('.main-navigation-mobile');
            mainNavMobile.style.paddingBottom = `${parseInt(window.getComputedStyle(mainNavMobile).paddingBottom.replace('px', '')) + Menubar.#menubar.offsetHeight}px`;
        }           
    }

    /**
     * Expande ou colapsa o submenu da navegação mobile   
     * @private  
     */
    #menubarAccordion(element){   
        if(element) {
            const hasSubmenu = element.querySelectorAll(Menubar.#config.selectors.hasSubmenu);             
            let animExpandId = null;           
            
            if(hasSubmenu) {
                hasSubmenu.forEach(menuitem => {  
                    // Funcionalidades
                    // ----------------------------------------------                                         
                    function toggle() { 
                        const nestingSubmenuOpen = menuitem.nextElementSibling.querySelectorAll('.submenu.isOpen');                          
                        let start;                                                                     
    
                        // accordion: animações
                        // -----------------------
                        const animExpand = (timestamp) => {        
                            if(start === undefined) { start = timestamp };

                            const elapsed = timestamp - start;
                            
                            if(menuitem.nextElementSibling.offsetHeight < menuitem.nextElementSibling.firstElementChild.offsetHeight) {
                                menuitem.nextElementSibling.style.height = `${menuitem.nextElementSibling.offsetHeight + Math.round(0.1 * elapsed)}px`;
                                menuitem.classList.add('animating');
                                animExpandId = requestAnimationFrame(animExpand);
                            }
                            // aplica altura flexível para comportar automaticamente a altura dos submenus aninhados (filhos)
                            if(menuitem.nextElementSibling.offsetHeight >= menuitem.nextElementSibling.firstElementChild.offsetHeight) {                                
                                menuitem.nextElementSibling.style.height = '100%';
                                menuitem.classList.remove('animating');
                            }                            
                        };                                           
                        const animCollapse = (timestamp) => {    
                            if(start === undefined) { start = timestamp };

                            const elapsed = timestamp - start;

                            if(menuitem.nextElementSibling.offsetHeight > 0) {
                                menuitem.nextElementSibling.style.height = `${menuitem.nextElementSibling.offsetHeight - Math.round(0.1 * elapsed)}px`;
                                menuitem.classList.add('animating');
                                requestAnimationFrame(animCollapse);                               
                            }
                            // verifica se o resultado da subtração é um número negativo e iguala altura a zero
                            if(Math.sign(menuitem.nextElementSibling.offsetHeight - Math.round(0.1 * elapsed)) === -1) {
                                menuitem.nextElementSibling.style.height = '0px';
                                menuitem.classList.remove('animating');
                            }
                        };    
                        // -----------------------                                            
                        
                        // accordion: expande submenu
                        if(menuitem.nextElementSibling.offsetHeight === 0) {
                            requestAnimationFrame(animExpand);                            
                            menuitem.setAttribute('aria-expanded', 'true');                            
                            menuitem.nextElementSibling.firstElementChild.setAttribute('aria-hidden', 'false');                            
                        } 
                        // accordion: colapsa submenu
                        else {                            
                            if(!Menubar.#settings.keepOpen) {                                
                                //fechar todos os submenus aninhados
                                let nestedMenuitems = menuitem.parentElement.querySelectorAll('[role="menuitem"][aria-haspopup="true"][aria-expanded="true"]');
                                nestedMenuitems = Array.from(nestedMenuitems);                                
                                for (let i=1; i < nestedMenuitems.length; i++){
                                    nestedMenuitems[i].click();
                                }
                            }                            
                            requestAnimationFrame(animCollapse);
                            menuitem.setAttribute('aria-expanded', 'false');                            
                            menuitem.nextElementSibling.firstElementChild.setAttribute('aria-hidden', 'true');                            
                        }
                        //submenu aninhado
                        if(menuitem.parentElement.parentElement.classList.contains('submenu')) {              
                            const globalWrapper = menuitem.parentElement.parentElement.parentElement;                                                           
                            
                            if(!menuitem.nextElementSibling.firstElementChild.classList.contains('isOpen')) {                                                       
                                menuitem.nextElementSibling.firstElementChild.classList.add('isOpen');                                                            
                            }
                            else {
                                menuitem.nextElementSibling.firstElementChild.classList.remove('isOpen');                                 
                            }
                        }
                    }
    
                    // Eventos
                    // ----------------------------------------------
                    menuitem.addEventListener('click', () => {
                        event.stopPropagation();
                        toggle();
                    })                    
                })
            }
        }     
    } 

    /**
     * Fecha o elemento aberto ao apertar a tecla ESC (menubar e/ou painel)
     * @private
     */
    #close() {
        const closeAll = () => {
            // fecha o menu mobile
            if(Menubar.#mobileNavigation && Menubar.#mobileNavigation.getAttribute('data-show') === 'true') {
                Menubar.#mobileButton.click();
            }
            
            // fecha painel aberto
            this.#closepanelOpened();
        }

        window.addEventListener('keydown', () => {
            if (event.keyCode === 27) {                
                closeAll();
            }
        });
    }
    
    /**
     * Fecha o painel aberto
     */
    #closepanelOpened() {
        const panelOpeneds = document.querySelectorAll(`${Menubar.#config.selectors.panels}[is-open]`);

        if(panelOpeneds) {
            panelOpeneds.forEach(panel => {
                document.querySelector(`[aria-owns="${panel.getAttribute('id')}"]`).click();
            });
        }
    }

    /** 
     * Configura e controla comportamentos padrões de painéis da toolbar
     * @private
     */
    #panel() {
        const panelAnimationName = Menubar.#settings.panel.animation;        

        const addClass = (panel) => {
            if(panel) {
                panel.classList.add(panelAnimationName);
            } else {
                event.target.classList.add(panelAnimationName);
            }
        }
        const removeClass = (panel) => { 
            if(panel) {
                panel.classList.remove(panelAnimationName);
            } else {
                event.target.classList.remove(panelAnimationName);
            }
        }
        const startAnimation = (event) => {                                    
            removeClass();
            event.target.removeEventListener('animationend', startAnimation);
        }
        const endAnimation = (event) => {                        
            removeClass();
            event.target.style.opacity = '0';
            event.target.style.visibility = 'hidden';
            event.target.removeEventListener('animationend', endAnimation);
        } 
        const setPanelPosition = (panel, button)=> {
            let offsetTop;

            if(document.querySelector('.ae-menubar.menubar[aria-orientation="horizontal"]')) {
                if(document.querySelector('.ae-menubar.menubar.split')) {
                    offsetTop = `${((parseInt(window.getComputedStyle(document.querySelector('.ae-menubar.menubar')).height.replace('px', ''))) / 2) + Menubar.#settings.panel.offset}px`    
                } else {
                    offsetTop = `${(parseInt(window.getComputedStyle(document.querySelector('.ae-menubar.menubar')).height.replace('px', ''))) + Menubar.#settings.panel.offset}px`
                }
            } else {
                offsetTop = `${(parseInt(window.getComputedStyle(document.querySelector('.ae-menubar.toolbar')).height.replace('px', ''))) + Menubar.#settings.panel.offset}px`
            }
            
            panel.style.position = 'absolute';
            panel.style.top = offsetTop; 
            
            if(!Menubar.#menubar.classList.contains('is-rtl')) {
                if(document.querySelector('.ae-menubar.toolbar')){
                    panel.style.left = button.getBoundingClientRect().right - panel.offsetWidth - document.querySelector('.ae-menubar.toolbar').offsetLeft + 'px';                   
                }
                else if(document.querySelector('.ae-menubar.menubar .toolbar') || document.querySelector('.ae-menubar.menubar .user.action-button')) {
                    panel.style.left = button.getBoundingClientRect().right - panel.offsetWidth - document.querySelector('.ae-menubar.menubar').offsetLeft + 'px';                   
                }            
            } else {
                if(document.querySelector('.ae-menubar.toolbar')){
                    panel.style.left = button.getBoundingClientRect().right - button.offsetWidth - document.querySelector('.ae-menubar.toolbar').offsetLeft + 'px';                   
                }
                else if(document.querySelector('.ae-menubar.menubar .toolbar') || document.querySelector('.ae-menubar.menubar .user.action-button')) {
                    panel.style.left = button.getBoundingClientRect().right - button.offsetWidth - document.querySelector('.ae-menubar.menubar').offsetLeft + 'px';                   
                }           
            }

        }

        // configurações iniciais dos painéis
        document.querySelectorAll(Menubar.#config.selectors.panels).forEach(panel => {
            const panelButton =  document.querySelector(`[aria-owns="${panel.getAttribute('id')}"]`);
           
            // adiciona classe de animação na inicialização dos painéis
            if (panel && !panel.classList.contains(panelAnimationName)){
                panel.style.setProperty('animation-direction', 'normal', 'important');
                addClass(panel, panelAnimationName);            
                panel.addEventListener('animationend', startAnimation);
            }

            if(panel) {
                if (panelButton) {
                    // reposicionamento dinâmico
                    window.addEventListener('resize', () => {
                        setPanelPosition(panel, panelButton);
                    });
                    // reposicionamento no click
                    panelButton.addEventListener('click', () => {
                      setPanelPosition(panel, panelButton);
                    });
                }
            }
        });
        
        // controle de exibição
        Menubar.#actionButton.forEach(button => {
            button.addEventListener('click', () => {   
                event.stopImmediatePropagation();

                const panelOpened = document.querySelector(`${Menubar.#config.selectors.panels}[is-open]`);
                let panel = button.getAttribute('aria-owns');
                    panel = document.getElementById(panel);

                if(panelOpened && panelOpened.getAttribute('id') !== panel.getAttribute('id')) {
                    const panelOwns = document.querySelector(`[aria-owns="${panelOpened.getAttribute('id')}"]`);                  
                          panelOwns.setAttribute('aria-expanded', 'false');
                    panelOpened.style.opacity = '0';
                    panelOpened.style.visibility = 'hidden';
                    panelOpened.removeAttribute('is-open');
                    panelOpened.setAttribute('aria-hidden', 'true');
                }

                try{
                    if(panel.hasAttribute('is-open')) {        
                        panel.removeAttribute('is-open');  
                        panel.setAttribute('aria-hidden', 'true');
                        panel.style.setProperty('animation-direction', 'reverse', 'important');
                        addClass(panel);
                        panel.addEventListener('animationend', endAnimation);
                        button.setAttribute('aria-expanded', 'false');
    
                    } else {                            
                        panel.setAttribute('is-open', 'true');  
                        panel.style.opacity = '1';          
                        panel.style.visibility = 'visible';  
                        panel.setAttribute('aria-hidden', 'false');
                        panel.style.setProperty('animation-direction', 'normal', 'important');
                        addClass(panel)
                        panel.addEventListener('animationend', startAnimation);  
                        button.setAttribute('aria-expanded', 'true');                                                                                    
                        // fecha o menu mobile
                        if(Menubar.#mobileNavigation && Menubar.#mobileNavigation.getAttribute('data-show') === 'true') {
                            Menubar.#mobileButton.click();
                        }
                    }
                } catch(err) {
                    console.debug('Este trigger não possui um painel associado!', err);
                }


            })
        })
    }
    
    /**
     * Verifica se um item do menu tem o status ativo e realiza ações complementares
     * @private
     */
    #isActive() {
        const isActive = Menubar.#mobileNavigation.querySelectorAll(Menubar.#config.selectors.isActive);           
        
        if(isActive) {
            isActive.forEach(menuitem => {
                if(!menuitem.getAttribute('href') && menuitem.getAttribute('aria-expanded') === 'false') {
                    menuitem.click();
                }
            })
        }
    }

    /**
     * Cria um overlay para impedir interações com os elementos abaixo deste
     * @private
     * @return { DOM Element} overlay
     */
    #createOverlay() {
        const overlay = document.createElement('div');

        overlay.style.position = 'fixed';
        overlay.style.top = '0';
        overlay.style.left = '0';
        overlay.style.width = '100%';
        overlay.style.height = '100%';        
        overlay.style.backgroundColor = Menubar.#settings.overlay.bgColor ? Menubar.#settings.overlay.bgColor : Menubar.#config.settings.overlay.bgColor;
        overlay.classList.add('ae-overlay', Menubar.#config.layers.layer_2);
        overlay.setAttribute('id', Menubar.#config.selectors.mobileNavigationOverlay);

        return overlay;
    }
    
    /**
     * Remove uma overlay ativa
     * @param {DOM Element} overlay ['mobile-navigation' || 'main-navigation'] Informa ao método qual overlay remover.
     */
    #removeOverlay(target) {        
        document.querySelector('body').removeChild(target);            
    }

    /**
     * Controla a animação de entrada e saída do elemento
     * @param {DOM Element} element Elemento que receberá a classe de animação
     */
    #animationClassControl(element) {
        const startAnimation = () => {
            switch(Menubar.#animationName) {                    
                case 'slide-top-bouncy':                        
                    element.classList.add('slide-top-bouncy');
                    break;                   
                case 'slide-right-bouncy':                        
                    element.classList.add('slide-right-bouncy');
                    break;                   
                case 'slide-bottom-bouncy':                        
                    element.classList.add('slide-bottom-bouncy');
                    break;                    
                case 'slide-left-bouncy':                        
                    element.classList.add('slide-left-bouncy');
                    break;                 
                case 'slide-top':                        
                    element.classList.add('slide-top');
                    break;                  
                case 'slide-right':                        
                    element.classList.add('slide-right');
                    break;                
                case 'slide-bottom':                        
                    element.classList.add('slide-bottom');
                    break;                
                case 'slide-left':                        
                    element.classList.add('slide-left');
                    break;                
                case 'reveal-opacity':                        
                    element.classList.add('reveal-opacity');
                    break;              
                case 'reveal-blur':                        
                    element.classList.add('reveal-blur');
                    break;
            }

            Menubar.#mobileButton.style.setProperty('pointer-events', 'none');
            element.style.setProperty('display', 'block');
            element.addEventListener('animationend', endAnimation);
        }

        const endAnimation = () => {
            if(Menubar.#animationShow) {
                Menubar.#config.animation.className.forEach(className => {
                    if(element.classList.contains(className)) {
                        element.classList.remove(className);
                    }
                });                       
                
                element.style.setProperty('animation-direction', 'reverse', 'important');
                Menubar.#mobileButton.style.removeProperty('pointer-events');
                element.removeEventListener('animationend', endAnimation);
            }
            
            if(element.getAttribute('data-show') === 'false') {
                element.style.setProperty('display', 'none');

                if(Menubar.#animationShow) {
                    element.style.setProperty('animation-direction', 'normal', 'important');
                }                    
            }
        }
        
        
        if (Menubar.#animationShow) {
            startAnimation();                        
        }
    }    
    
    /**
     * Controla a interação de visualização do menu vertical entre colapsada e extendida
     */
    #collapsibleMenu(){        
        if(Menubar.#menubarVertical) {
            let logos = Menubar.#menubarVertical.querySelectorAll('.logo img');             
            let menubarVert_width = Menubar.#menubarVertical.offsetWidth;
            const menuitems = Menubar.#menubarVertical.querySelectorAll('.wrapper-navigation [role="menuitem"]');   
            const menuitems_labels = Menubar.#menubarVertical.querySelectorAll('.wrapper-navigation [role="menubar"] > .wrapper > [role="menuitem"] > .label');   
            const navgroup_labels = Menubar.#menubarVertical.querySelectorAll('.wrapper-navigation [role="menubar"] .navigation-group > .label');  
            const menuitems_opened = Menubar.#menubarVertical.querySelectorAll('.wrapper-navigation [role="menubar"] [role="menuitem"][aria-expanded="true"]');         
            
            const wrapperUser = Menubar.#menubarVertical.querySelector(Menubar.#config.selectors.wrapperUser);  
            let userMenu, userInfo, userInfoGroup, userButton;

            if(wrapperUser) {
                userMenu = wrapperUser.querySelector(Menubar.#config.selectors.userMenu);
                userInfo = wrapperUser.querySelector(Menubar.#config.selectors.userInfo);
                userInfoGroup = wrapperUser.querySelector(`${Menubar.#config.selectors.userInfo} > .group`);
                userButton = wrapperUser.querySelector(Menubar.#config.selectors.userButton);
            }

            let start, previousTimestamp;
            
            const animate_expand = timestamp => {
                if(start === undefined) { start = timestamp };
                
                const menubarMaxWidth = parseInt(Menubar.#menubarVertical.getAttribute('data-width'));
                const menubarWidth = Menubar.#menubarVertical.offsetWidth;
                const elapsed = timestamp - start;

                if(previousTimestamp !== timestamp) {
                    const width = Math.min(menubarWidth + elapsed, menubarMaxWidth); 
                    Menubar.#menubarVertical.style.width = `${width}px`;
                }

                if(menubarWidth !== menubarMaxWidth) { 
                    requestAnimationFrame(animate_expand) 
                } else {
                    Menubar.#menubarVertical.style.width = 'auto';
                    Menubar.#menubarVertical.classList.add('expanded');                    
                }
            }

            const animate_collapse = timestamp => {
                if(start === undefined) { start = timestamp };
                
                const menubarWidth = Menubar.#menubarVertical.offsetWidth;
                const elapsed = timestamp - start;

                if(menubarWidth === menubarVert_width) {
                    Menubar.#menubarVertical.setAttribute('data-width', menubarWidth);
                }

                if(previousTimestamp !== timestamp) {
                    const width = Math.max(menubarWidth - elapsed, 80); 
                    Menubar.#menubarVertical.style.width = `${width}px`;
                    Menubar.#menubarVertical.style.minWidth = `${width}px`;
                }

                if(menubarWidth !== 80) { 
                    requestAnimationFrame(animate_collapse) 
                } else if(menubarWidth === 80) {
                    Menubar.#menubarVertical.classList.add('collapsed'); 
                }
            }
            
            const handler_collapse = () => {
                requestAnimationFrame(animate_collapse);                 
                
                Menubar.#menubarVertical.classList.remove('expanded');  
                Menubar.#collapseMenu.classList.add('d-none');
                
                logos[0].classList.add('d-none');
                logos[0].classList.remove('d-block');
                logos[1].classList.add('d-block');
                logos[1].classList.remove('d-none');
                
                let checkWrapperUserAnimating = null;    
                const checkWrapperUserClassAnimating = () => {     
                    if (wrapperUser && !wrapperUser.classList.contains('animating')) {                        
                        clearInterval(checkWrapperUserAnimating);
                        
                        wrapperUser.style.display = 'flex';
                        wrapperUser.style.flexFlow = 'row nowrap';
                        wrapperUser.style.justifyContent = 'center';
                        userMenu.classList.add('d-none');
                        userInfoGroup.classList.add('d-none');
                        userInfo.setAttribute('data-expanded', userInfo.getAttribute('aria-expanded'));
                        userInfo.removeAttribute('aria-expanded');
                    }
                }    

                if(wrapperUser) {
                    checkWrapperUserAnimating = setInterval(checkWrapperUserClassAnimating, 50);
                }

                let checkMenuitemAnimation = null;
                const checkMenuitemClassAnimation = () => {
                    menuitems.forEach(menuitem => {
                        if(!menuitem.classList.contains('animating')) {
                            clearInterval(checkMenuitemAnimation);
                            
                            menuitems_opened.forEach(menuitem => {
                                menuitem.nextElementSibling.classList.add('d-none');
                            });
                            
                            menuitems_labels.forEach(label => {
                                label.classList.add('d-none');
                                label.parentElement.style.justifyContent = 'center';
                            });
                
                            navgroup_labels.forEach(label => {                    
                                label.classList.add('d-none');
                                label.previousElementSibling.classList.remove('d-none'); 
                                label.parentElement.style.justifyContent = 'center';                   
                            });
                        }
                    })
                }

                checkMenuitemAnimation = setInterval(checkMenuitemClassAnimation, 50);
            }

            const handler_expand = () => {
                requestAnimationFrame(animate_expand);   
                
                Menubar.#menubarVertical.classList.remove('collapsed');
                Menubar.#collapseMenu.classList.remove('d-none');                
    
                logos[1].classList.add('d-none');
                logos[1].classList.remove('d-block');
                logos[0].classList.add('d-block');
                logos[0].classList.remove('d-none');

                let checkWrapperUserAnimating = null;                
                const checkWrapperUserClassAnimating = () => {     
                    if (wrapperUser && !wrapperUser.classList.contains('animating')) {
                        clearInterval(checkWrapperUserAnimating);
                        
                        wrapperUser.style.display = 'block';
                        wrapperUser.style.flexFlow = '';
                        wrapperUser.style.justifyContent = '';
                        userMenu.classList.remove('d-none');
                        userInfoGroup.classList.remove('d-none');
                        userInfo.setAttribute('aria-expanded', userInfo.getAttribute('data-expanded'));
                        userInfo.removeAttribute('data-expanded');
                    }
                }       

                if(wrapperUser) {
                    checkWrapperUserAnimating = setInterval(checkWrapperUserClassAnimating, 50);
                }

                let checkMenuitemAnimation = null;
                const checkMenuitemClassAnimation = () => {
                    menuitems.forEach(menuitem => {
                        if(!menuitem.classList.contains('animating')) {
                            clearInterval(checkMenuitemAnimation);

                            menuitems_opened.forEach(menuitem => {
                                menuitem.nextElementSibling.classList.remove('d-none');
                            });
                            
                            menuitems_labels.forEach(label => {
                                label.classList.remove('d-none');    
                                label.parentElement.style.justifyContent = 'flex-start';                
                            });
                
                            navgroup_labels.forEach(label => {                    
                                label.classList.remove('d-none');
                                label.previousElementSibling.classList.add('d-none');
                                label.parentElement.style.justifyContent = 'flex-start';
                            });
                        }
                    })
                }

                checkMenuitemAnimation = setInterval(checkMenuitemClassAnimation, 50);
            }

            if(Menubar.#menubarVertical.getAttribute('data-collapsible') === 'true') {
                if(!Menubar.#menubarVertical.classList.contains('collapsed')) { 
                    handler_collapse();                    
                } else {  
                    handler_expand();                        
                }
            }

        }
    }

    /**
     * Controla o menu do usuário da menubar vertical
     */
    #userMenu() {
        const wrapperUser = Menubar.#menubarVertical.querySelector(Menubar.#config.selectors.wrapperUser);                
        const userInfo = wrapperUser.querySelector(Menubar.#config.selectors.userInfo)
        
        if(wrapperUser) {
            userInfo.addEventListener('click', () => {
                const expanded = userInfo.getAttribute('aria-expanded');
                const userMenu = wrapperUser.querySelector(Menubar.#config.selectors.userMenu);
                const userMenuWrapper = wrapperUser.querySelector(`${Menubar.#config.selectors.userMenu} > .wrapper`);
                const userMenuContentHeight = wrapperUser.querySelector(Menubar.#config.selectors.userMenuContent).offsetHeight;               
                let start;

                const animExpand = timestamp => {
                    if(start === undefined) { start = timestamp };

                    const elapsed = timestamp - start;
                    
                    if(userMenuWrapper.offsetHeight < userMenuContentHeight) {
                        userMenuWrapper.style.height = `${Math.min(userMenuWrapper.offsetHeight + (0.1 * elapsed), userMenuContentHeight)}px`;
                        wrapperUser.classList.add('animating');
                        requestAnimationFrame(animExpand);
                    } else {
                        userInfo.setAttribute('aria-expanded', 'true');
                        userMenuWrapper.style.height = 'fit-content';
                        wrapperUser.classList.remove('animating');
                    }
                }

                const animCollapse = timestamp => {
                    if(start === undefined) { start = timestamp };

                    const elapsed = timestamp - start;
                    
                    if(userMenuWrapper.offsetHeight !== 0) {
                        userMenuWrapper.style.height = `${Math.max(userMenuWrapper.offsetHeight - (0.1 * elapsed), 0)}px`;
                        wrapperUser.classList.add('animating');
                        requestAnimationFrame(animCollapse);
                    } else {
                        userInfo.setAttribute('aria-expanded', 'false');
                        userMenu.classList.remove('m-top-4');
                        wrapperUser.classList.remove('animating');
                    }
                }
                
                if(expanded === 'false') {
                    requestAnimationFrame(animExpand);
                    userMenu.classList.add('m-top-4');                    
                } else {
                    requestAnimationFrame(animCollapse);                    
                }
            })
        }
    }
}

// Funções auxiliares
// ----------------------------------------------------------
function sumElementsWidth(nodeElements) {
    let width = 0;
    if(nodeElements){
        nodeElements.forEach(element => {         
          width += element.offsetWidth;  
        });
        return width;
    }
}
