/*

* ES2015 accessible tabs panel system, using ARIA
* Website: https://van11y.net/accessible-tab-panel/
* License MIT: https://github.com/nico3333fr/van11y-accessible-tab-panel-aria/blob/master/LICENSE
*/

(doc => {

'use strict';

const TABS_JS = 'js-tabs';
const TABS_JS_LIST = 'js-tablist';
const TABS_JS_LISTITEM = 'js-tablist__item';
const TABS_JS_LISTLINK = 'js-tablist__link';
const TABS_JS_CONTENT = 'js-tabcontent';
const TABS_JS_LINK_TO_TAB = 'js-link-to-tab';

const TABS_DATA_PREFIX_CLASS = 'data-tabs-prefix-class';
const TABS_DATA_HX = 'data-hx';
const TABS_DATA_GENERATED_HX_CLASS = 'data-tabs-generated-hx-class';
const TABS_DATA_EXISTING_HX = 'data-existing-hx';

const TABS_DATA_SELECTED_TAB = 'data-selected';

const TABS_PREFIX_IDS = 'label_';

const TABS_STYLE = 'tabs';
const TABS_LIST_STYLE = 'tabs__list';
const TABS_LISTITEM_STYLE = 'tabs__item';
const TABS_LINK_STYLE = 'tabs__link';
const TABS_CONTENT_STYLE = 'tabs__content';

const TABS_HX_DEFAULT_CLASS = 'invisible';

const TABS_ROLE_TABLIST = 'tablist';
const TABS_ROLE_TAB = 'tab';
const TABS_ROLE_TABPANEL = 'tabpanel';
const TABS_ROLE_PRESENTATION = 'presentation';

const ATTR_ROLE = 'role';
const ATTR_LABELLEDBY = 'aria-labelledby';
const ATTR_HIDDEN = 'aria-hidden';
const ATTR_CONTROLS = 'aria-controls';
const ATTR_SELECTED = 'aria-selected';

const DELAY_HASH_UPDATE = 1000;

let hash = window.location.hash.replace('#', '');

//const IS_OPENED_CLASS = 'is-opened';

const findById = id => doc.getElementById(id);

const addClass = (el, className) => {
    if (el.classList) {
        el.classList.add(className); // IE 10+
    } else {
        el.className += ' ' + className; // IE 8+
    }
}

/*const removeClass = (el, className) => {
      if (el.classList) {
        el.classList.remove(className); // IE 10+
      }
      else {
           el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); // IE 8+
           }
      }*/

const hasClass = (el, className) => {
    if (el.classList) {
        return el.classList.contains(className); // IE 10+
    } else {
        return new RegExp('(^| )' + className + '( |$)', 'gi').test(el.className); // IE 8+ ?
    }
}

const setAttributes = (node, attrs) => {
    Object
        .keys(attrs)
        .forEach((attribute) => {
            node.setAttribute(attribute, attrs[attribute]);
        });
};
const unSelectLinks = (elts) => {
    elts
        .forEach((link_node) => {
            setAttributes(link_node, {
                [ATTR_SELECTED]: 'false',
                'tabindex': '-1'
            });
        });
}
const unSelectContents = (elts) => {
    elts
        .forEach((content_node) => {
            content_node.setAttribute(ATTR_HIDDEN, true);
        });
}

const selectLink = (el) => {
    let destination = findById(el.getAttribute(ATTR_CONTROLS));
    setAttributes(el, {
        [ATTR_SELECTED]: 'true',
        'tabindex': '0'
    });
    destination.removeAttribute(ATTR_HIDDEN);
    setTimeout(function() {
        el.focus();
    }, 0);
    setTimeout(function() {
        history.pushState(null, null, location.pathname + location.search + '#' + el.getAttribute(ATTR_CONTROLS))
    }, DELAY_HASH_UPDATE);
}

const selectLinkInList = (itemsList, linkList, contentList, param) => {
    let indice_trouve;

    itemsList
        .forEach((itemNode, index) => {
            if (itemNode.querySelector('.' + TABS_JS_LISTLINK).getAttribute(ATTR_SELECTED) === 'true') {
                indice_trouve = index;
            }
        });
    unSelectLinks(linkList);
    unSelectContents(contentList);
    if (param === 'next') {
        selectLink(linkList[indice_trouve + 1]);
        setTimeout(function() {
            linkList[indice_trouve + 1].focus();
        }, 0);
    }
    if (param === 'prev') {
        selectLink(linkList[indice_trouve - 1]);
        setTimeout(function() {
            linkList[indice_trouve - 1].focus();
        }, 0);
    }

}

/* gets an element el, search if it is child of parent class, returns id of the parent */
let searchParent = (el, parentClass) => {
    let found = false;
    let parentElement = el.parentNode;
    while (parentElement && found === false) {
        if (hasClass(parentElement, parentClass) === true) {
            found = true;
        } else {
            parentElement = parentElement.parentNode;
        }
    }
    if (found === true) {
        return parentElement.getAttribute('id');
    } else {
        return '';
    }
}

/** Find all tabs inside a container
 * @param  {Node} node Default document
 * @return {Array}
 */
const $listTabs = (node = doc) => [].slice.call(node.querySelectorAll('.' + TABS_JS));

/**
 * Build tooltips for a container
 * @param  {Node} node
 */
const attach = (node) => {

    $listTabs(node)
        .forEach((tabs_node) => {

            let iLisible = Math.random().toString(32).slice(2, 12);
            let prefixClassName = tabs_node.hasAttribute(TABS_DATA_PREFIX_CLASS) === true ? tabs_node.getAttribute(TABS_DATA_PREFIX_CLASS) + '-' : '';
            let hx = tabs_node.hasAttribute(TABS_DATA_HX) === true ? tabs_node.getAttribute(TABS_DATA_HX) : '';
            let hxGeneratedClass = tabs_node.hasAttribute(TABS_DATA_GENERATED_HX_CLASS) === true ? tabs_node.getAttribute(TABS_DATA_GENERATED_HX_CLASS) : TABS_HX_DEFAULT_CLASS;
            let existingHx = tabs_node.hasAttribute(TABS_DATA_EXISTING_HX) === true ? tabs_node.getAttribute(TABS_DATA_EXISTING_HX) : '';
            let $tabList = [].slice.call(tabs_node.querySelectorAll('.' + TABS_JS_LIST));
            let $tabListItems = [].slice.call(tabs_node.querySelectorAll('.' + TABS_JS_LISTITEM));
            let $tabListLinks = [].slice.call(tabs_node.querySelectorAll('.' + TABS_JS_LISTLINK));
            let $tabListPanels = [].slice.call(tabs_node.querySelectorAll('.' + TABS_JS_CONTENT));
            let noTabSelected = true;

            // container
            addClass(tabs_node, prefixClassName + TABS_STYLE);
            tabs_node.setAttribute('id', TABS_STYLE + iLisible);

            // ul
            $tabList.forEach((tabList) => {
                addClass(tabList, prefixClassName + TABS_LIST_STYLE);
                setAttributes(tabList, {
                    [ATTR_ROLE]: TABS_ROLE_TABLIST,
                    'id': TABS_LIST_STYLE + iLisible
                });
            });
            // li
            $tabListItems.forEach((tabListItem, index) => {
                addClass(tabListItem, prefixClassName + TABS_LISTITEM_STYLE);
                setAttributes(tabListItem, {
                    [ATTR_ROLE]: TABS_ROLE_PRESENTATION,
                    'id': TABS_LISTITEM_STYLE + iLisible + '-' + (index + 1)
                });
            });
            // a
            $tabListLinks.forEach((tabListLink) => {
                let idHref = tabListLink.getAttribute("href").replace('#', '');
                let panelControlled = findById(idHref);
                let linkText = tabListLink.innerText;
                let panelSelected = tabListLink.hasAttribute(TABS_DATA_SELECTED_TAB) === true;

                addClass(tabListLink, prefixClassName + TABS_LINK_STYLE);
                setAttributes(tabListLink, {
                    'id': TABS_PREFIX_IDS + idHref,
                    [ATTR_ROLE]: TABS_ROLE_TAB,
                    [ATTR_CONTROLS]: idHref,
                    'tabindex': '-1',
                    [ATTR_SELECTED]: 'false'
                });

                // panel controlled
                setAttributes(panelControlled, {
                    [ATTR_HIDDEN]: 'true',
                    [ATTR_ROLE]: TABS_ROLE_TABPANEL,
                    [ATTR_LABELLEDBY]: TABS_PREFIX_IDS + idHref
                });
                addClass(panelControlled, prefixClassName + TABS_CONTENT_STYLE);

                // if already selected
                if (panelSelected && noTabSelected) {
                    noTabSelected = false;
                    setAttributes(tabListLink, {
                        'tabindex': '0',
                        [ATTR_SELECTED]: 'true'
                    });
                    setAttributes(panelControlled, {
                        [ATTR_HIDDEN]: 'false'
                    });
                }

                // hx
                if (hx !== '') {
                    let hx_node = document.createElement(hx);
                    hx_node.setAttribute('class', hxGeneratedClass);
                    hx_node.setAttribute('tabindex', '0');
                    hx_node.innerHTML = linkText;
                    panelControlled.insertBefore(hx_node, panelControlled.firstChild);
                }
                // existingHx

                if (existingHx !== '') {
                    let $hx_existing = [].slice.call(panelControlled.querySelectorAll(existingHx + ':first-child'));
                    $hx_existing.forEach((hx_item) => {
                        hx_item.setAttribute('tabindex', '0');
                    });

                }

                tabListLink.removeAttribute('href');

            });

            if (hash !== '') {
                let nodeHashed = findById(hash);
                if (nodeHashed !== null) { // just in case of an dumb error
                    // search if hash is current tabs_node
                    if (tabs_node.querySelector('#' + hash) !== null) {
                        // search if hash is ON tabs
                        if (hasClass(nodeHashed, TABS_JS_CONTENT) === true) {
                            // unselect others
                            unSelectLinks($tabListLinks);
                            unSelectContents($tabListPanels);
                            // select this one
                            nodeHashed.removeAttribute(ATTR_HIDDEN);
                            let linkHashed = findById(TABS_PREFIX_IDS + hash);
                            setAttributes(linkHashed, {
                                'tabindex': '0',
                                [ATTR_SELECTED]: 'true'
                            });
                            noTabSelected = false;
                        } else {
                            // search if hash is IN tabs
                            let panelParentId = searchParent(nodeHashed, TABS_JS_CONTENT);
                            if (panelParentId !== '') {
                                // unselect others
                                unSelectLinks($tabListLinks);
                                unSelectContents($tabListPanels);
                                // select this one
                                let panelParent = findById(panelParentId);
                                panelParent.removeAttribute(ATTR_HIDDEN);
                                let linkParent = findById(TABS_PREFIX_IDS + panelParentId);
                                setAttributes(linkParent, {
                                    'tabindex': '0',
                                    [ATTR_SELECTED]: 'true'
                                });
                                noTabSelected = false;
                            }
                        }
                    }
                }
            }

            // if no selected => select first
            if (noTabSelected === true) {
                setAttributes($tabListLinks[0], {
                    'tabindex': '0',
                    [ATTR_SELECTED]: 'true'
                });
                let panelFirst = findById($tabListLinks[0].getAttribute(ATTR_CONTROLS));
                panelFirst.removeAttribute(ATTR_HIDDEN);
            }

        });
};

/* listeners */
['click', 'keydown']
.forEach(eventName => {
    //let isCtrl = false;

    doc.body
        .addEventListener(eventName, e => {

            // click on a tab link or on something IN a tab link
            let parentLink = searchParent(e.target, TABS_JS_LISTLINK);
            if ((hasClass(e.target, TABS_JS_LISTLINK) === true || parentLink !== '') && eventName === 'click') {
                let linkSelected = hasClass(e.target, TABS_JS_LISTLINK) === true ? e.target : findById(parentLink);
                let parentTabId = searchParent(e.target, TABS_JS);
                let parentTab = findById(parentTabId);
                //let $parentListItems = [].slice.call(parentTab.querySelectorAll('.' + TABS_JS_LISTITEM));
                let $parentListLinks = [].slice.call(parentTab.querySelectorAll('.' + TABS_JS_LISTLINK));
                let $parentListContents = [].slice.call(parentTab.querySelectorAll('.' + TABS_JS_CONTENT));

                // aria selected false on all links
                unSelectLinks($parentListLinks);
                // add aria-hidden on all tabs contents
                unSelectContents($parentListContents);
                // add aria selected on selected link + show linked panel
                selectLink(linkSelected);

                e.preventDefault();
            }

            // Key down on tabs
            if ((hasClass(e.target, TABS_JS_LISTLINK) === true || parentLink !== '') && eventName === 'keydown') {
                //let linkSelected = hasClass( e.target, TABS_JS_LISTLINK) === true ? e.target : findById( parentLink );
                let parentTabId = searchParent(e.target, TABS_JS);
                let parentTab = findById(parentTabId);
                let $parentListItems = [].slice.call(parentTab.querySelectorAll('.' + TABS_JS_LISTITEM));
                let $parentListLinks = [].slice.call(parentTab.querySelectorAll('.' + TABS_JS_LISTLINK));
                let $parentListContents = [].slice.call(parentTab.querySelectorAll('.' + TABS_JS_CONTENT));
                let firstLink = $parentListItems[0].querySelector('.' + TABS_JS_LISTLINK);
                let lastLink = $parentListItems[$parentListItems.length - 1].querySelector('.' + TABS_JS_LISTLINK);

                // strike home on a tab => 1st tab
                if (e.keyCode === 36) {
                    unSelectLinks($parentListLinks);
                    unSelectContents($parentListContents);
                    selectLink(firstLink);

                    e.preventDefault();
                }
                // strike end on a tab => last tab
                else if (e.keyCode === 35) {
                    unSelectLinks($parentListLinks);
                    unSelectContents($parentListContents);
                    selectLink(lastLink);

                    e.preventDefault();
                }
                // strike up or left on the tab => previous tab
                else if ((e.keyCode === 37 || e.keyCode === 38) && !e.ctrlKey) {
                    if (firstLink.getAttribute(ATTR_SELECTED) === 'true') {
                        unSelectLinks($parentListLinks);
                        unSelectContents($parentListContents);
                        selectLink(lastLink);

                        e.preventDefault();
                    } else {
                        selectLinkInList($parentListItems, $parentListLinks, $parentListContents, 'prev');
                        e.preventDefault();
                    }
                }
                // strike down or right in the tab => next tab
                else if ((e.keyCode === 40 || e.keyCode === 39) && !e.ctrlKey) {
                    if (lastLink.getAttribute(ATTR_SELECTED) === 'true') {
                        unSelectLinks($parentListLinks);
                        unSelectContents($parentListContents);
                        selectLink(firstLink);

                        e.preventDefault();
                    } else {
                        selectLinkInList($parentListItems, $parentListLinks, $parentListContents, 'next');
                        e.preventDefault();
                    }
                }

            }

            // Key down in tab panels
            let parentTabPanelId = searchParent(e.target, TABS_JS_CONTENT);
            if (parentTabPanelId !== '' && eventName === 'keydown') {
                let linkSelected = findById(findById(parentTabPanelId).getAttribute(ATTR_LABELLEDBY));
                let parentTabId = searchParent(e.target, TABS_JS);
                let parentTab = findById(parentTabId);
                let $parentListItems = [].slice.call(parentTab.querySelectorAll('.' + TABS_JS_LISTITEM));
                let $parentListLinks = [].slice.call(parentTab.querySelectorAll('.' + TABS_JS_LISTLINK));
                let $parentListContents = [].slice.call(parentTab.querySelectorAll('.' + TABS_JS_CONTENT));
                let firstLink = $parentListItems[0].querySelector('.' + TABS_JS_LISTLINK);
                let lastLink = $parentListItems[$parentListItems.length - 1].querySelector('.' + TABS_JS_LISTLINK);

                // strike up + ctrl => go to header
                if (e.keyCode === 38 && e.ctrlKey) {
                    setTimeout(function() {
                        linkSelected.focus();
                    }, 0);
                    e.preventDefault();
                }
                // strike pageup + ctrl => go to prev header
                if (e.keyCode === 33 && e.ctrlKey) {
                    // go to header
                    linkSelected.focus();
                    e.preventDefault();
                    // then previous
                    if (firstLink.getAttribute(ATTR_SELECTED) === 'true') {
                        unSelectLinks($parentListLinks);
                        unSelectContents($parentListContents);
                        selectLink(lastLink);

                    } else {
                        selectLinkInList($parentListItems, $parentListLinks, $parentListContents, 'prev');
                    }

                }
                // strike pagedown + ctrl => go to next header
                if (e.keyCode === 34 && e.ctrlKey) {
                    // go to header
                    linkSelected.focus();
                    e.preventDefault();
                    // then next
                    if (lastLink.getAttribute(ATTR_SELECTED) === 'true') {
                        unSelectLinks($parentListLinks);
                        unSelectContents($parentListContents);
                        selectLink(firstLink);

                    } else {
                        selectLinkInList($parentListItems, $parentListLinks, $parentListContents, 'next');
                    }
                }
            }

            // click on a tab link
            let parentLinkToPanelId = searchParent(e.target, TABS_JS_LINK_TO_TAB);
            if ((hasClass(e.target, TABS_JS_LINK_TO_TAB) === true || parentLinkToPanelId !== '') && eventName === 'click') {
                let panelSelectedId = hasClass(e.target, TABS_JS_LINK_TO_TAB) === true ? e.target.getAttribute('href').replace('#', '') : findById(parentLinkToPanelId).replace('#', '');
                let panelSelected = findById(panelSelectedId);
                let buttonPanelSelected = findById(panelSelected.getAttribute(ATTR_LABELLEDBY));
                let parentTabId = searchParent(e.target, TABS_JS);
                let parentTab = findById(parentTabId);
                //let $parentListItems = [].slice.call(parentTab.querySelectorAll('.' + TABS_JS_LISTITEM));
                let $parentListLinks = [].slice.call(parentTab.querySelectorAll('.' + TABS_JS_LISTLINK));
                let $parentListContents = [].slice.call(parentTab.querySelectorAll('.' + TABS_JS_CONTENT));

                unSelectLinks($parentListLinks);
                unSelectContents($parentListContents);
                selectLink(buttonPanelSelected);

                e.preventDefault();
            }

        }, true);
});

const onLoad = () => {
    attach();
    document.removeEventListener('DOMContentLoaded', onLoad);
}

document.addEventListener('DOMContentLoaded', onLoad);

window.van11yAccessibleTabPanelAria = attach;

})(document);