HEX
Server: Microsoft-IIS/8.5
System: Windows NT YDAWBH120 6.3 build 9600 (Windows Server 2012 R2 Standard Edition) AMD64
User: tentjecom_web (0)
PHP: 7.4.14
Disabled: NONE
Upload Files
File: D:/HostingSpaces/carrotps/carrotps.com/resources/js/kms/entity/tabs.js
class TabsController {

    /**
     * The tab controller can obviously control tabs.
     * You just have to have a couple of divs that hold the content. These divs must all have a class name that
     * corresponds to the value of constructor parameter tabContentIdAndClassAndPrefix. And they all must have an id
     * that also starts with the value of tabContentIdAndClassAndPrefix followed by the tab slug.
     *
     * You can specify if you have an hidden input field that needs to be updated with the curent tab slug. You do that
     * with the tabSlugInputSelector.
     *
     * Content tab divs and buttons will also receive an active state class to make them visible / stand out when present
     * or hidden / to background. You can specify this class name with the activeClass parameter.
     *
     * The tabButtonGroupSelector selects an element that holds the tab buttons to switch tabs. This element must have an
     * ul element that holds li elements containing a elements.
     *
     * The reactToUrlHashChange parameter controls if the controller should react if the hash part of the url did change.
     *
     * @param tabContentIdAndClassAndPrefix string
     * @param tabButtonGroupSelector string
     * @param activeClass string
     * @param reactToUrlHashChange
     * @param tabSlugInputSelector string
     */
    constructor(tabContentIdAndClassAndPrefix = 'tab', tabButtonGroupSelector = '.tab-buttons', activeClass = 'active', reactToUrlHashChange = false, tabSlugInputSelector = undefined) {
        let input = document.querySelector(this.tabSlugInputId);

        this.tabButtonGroupSelector = tabButtonGroupSelector;
        this.tabContentDivsClassAndIdPrefix = tabContentIdAndClassAndPrefix;
        this.tabSlugInputId = tabSlugInputSelector;
        this.activeClass = activeClass;
        this.reactToUrlHashChange = reactToUrlHashChange;

        if(reactToUrlHashChange) this.addListenerForHashChange(reactToUrlHashChange);
    }

    /**
     * Opens the tab by using the tab slug
     *
     * @param tabSlug
     */
    openTab(tabSlug, clearStorage) {
        //Remove trailing / on the left hand side
        tabSlug = this.removeLeftHandSlashInSlug(tabSlug);
        if (!tabSlug) { return; }

        if(clearStorage) {
            sessionStorage.clear();
        }

        this.updateTabSlugInput(tabSlug);
        this.showTabContentForTabWithSlug(tabSlug);
        this.makeTabButtonActiveForSlug(tabSlug);
    }

    /**
     * Removes a / at the beginning of a tab slug
     *
     * @param tabSlug
     */
    removeLeftHandSlashInSlug(tabSlug) {
        return tabSlug.replace(/^\/(.*)/, '$1');
    }

    /**
     * Updates a usually hidden input field holding the tab slug with a new slug
     *
     * @param newSlug string
     */
    updateTabSlugInput(newSlug)
    {
        if(this.tabSlugInputId === undefined) return;
        let element = document.querySelector(this.tabSlugInputId);
        if(element) element.value = newSlug;
    }

    makeTabButtonActiveForSlug(slug)
    {
        let entityTabs = document.querySelectorAll('.entity-tabs > ul > li');
        let nTabs = entityTabs.length;
        for(let index = 0; index < nTabs; index++)
        {
            let currentTab = entityTabs[index];
            currentTab.classList.remove('active');
        }

        let activeTab = document.querySelector('.entity-tabs > ul > li a[href="#' + slug + '"]');
        activeTab.parentElement.classList.add('active');
    }

    /**
     * Shows the tab content div with the specified slug and hides the other content div tabs.
     * It does this by adding and removing classes (the active class you specified in the constructor)
     *
     * @param slug string
     */
    showTabContentForTabWithSlug(slug)
    {
        //Remove the active class from all tab content classes
        let items = document.querySelectorAll('.'+this.tabContentDivsClassAndIdPrefix);
        let length = items.length;

        for(let index = 0; index < length; index++) {
          let element = items[index];
            element.classList.remove(this.activeClass)
        }

        //Add the active class to the tab that has the correct slug appended to the IdPrefix
        let activeTab = document.querySelector("#"+this.tabContentDivsClassAndIdPrefix+"-"+slug);
        if(!activeTab) { console.error("TabsController: Could not make content tab active. It should have an ID with: #"+this.tabContentDivsClassAndIdPrefix+"-"+slug); return; }

        activeTab.classList.add(this.activeClass);

        if(!isset(sessionStorage.getItem("componentScrollPosition"))) {
            activeTab.parentElement.scrollTop = 0;
        }

        activeTab.parentElement.addEventListener('scroll', function (ev) {
            sessionStorage.componentScrollPosition = ev.currentTarget.scrollTop;
        });
    }

    /**
     * Adds or disables listening for hashchange events to update tabs
     *
     * @param boolean
     */
    addListenerForHashChange(boolean)
    {
        if(boolean) {
            window.addEventListener('hashchange', this.hashChanged.bind(this));
            window.addEventListener('load', this.hashChanged.bind(this));
        } else {
            window.removeEventListener('hashchange', this.hashChanged);
            window.removeEventListener('load', this.hashChanged);
        }
    }

    /**
     * Called automatically by an internal listener (controlled by the addListenerForHashChange method)
     */
    hashChanged(event)
    {
        let tabSlug = window.location.hash.substring(1);
        if (tabSlug) {
            this.openTab(tabSlug, true);
            return;
        }

        let tabSlugElement = document.querySelector(this.tabSlugInputId);
        if(!tabSlugElement) return;

        this.openTab(tabSlugElement.value, false);
    }
}