Source: router.js

"use strict";
// SPDX-License-Identifier: GPL-3.0-or-later
// myMPD (c) 2018-2024 Juergen Mang <mail@jcgames.de>
// https://github.com/jcorporation/mympd

/** @module router_js */

/**
 * Shows the current view and highlights the navbar icon
 * @returns {void}
 */
function appPrepare() {
    if (app.current.card !== app.last.card ||
        app.current.tab !== app.last.tab ||
        app.current.view !== app.last.view)
    {
        //Hide all cards + nav
        for (let i = 0; i < domCache.navbarBtnsLen; i++) {
            domCache.navbarBtns[i].classList.remove('active');
        }
        const cards = ['cardHome', 'cardPlayback', 'cardSearch',
            'cardQueue', 'tabQueueCurrent', 'tabQueueLastPlayed',
            'tabQueueJukebox', 'viewQueueJukeboxSong', 'viewQueueJukeboxAlbum',
            'cardBrowse', 'tabBrowseFilesystem',
            'tabBrowseRadio', 'viewBrowseRadioFavorites', 'viewBrowseRadioWebradiodb',
            'tabBrowsePlaylist', 'viewBrowsePlaylistDetail', 'viewBrowsePlaylistList',
            'tabBrowseDatabase', 'viewBrowseDatabaseTagList', 'viewBrowseDatabaseAlbumDetail', 'viewBrowseDatabaseAlbumList'];
        for (const card of cards) {
            elHideId(card);
        }
        //show active card
        elShowId('card' + app.current.card);
        //show active tab
        if (app.current.tab !== undefined &&
            app.current.tab !== '')
        {
            elShowId('tab' + app.current.card + app.current.tab);
        }
        //show active view
        if (app.current.view !== undefined &&
            app.current.view !== '')
        {
            elShowId('view' + app.current.card + app.current.tab + app.current.view);
        }
        //highlight active navbar icon
        let nav = elGetById('nav' + app.current.card + app.current.tab);
        if (nav) {
            nav.classList.add('active');
        }
        else {
            nav = elGetById('nav' + app.current.card);
            if (nav) {
                elGetById('nav' + app.current.card).classList.add('active');
            }
        }
    }
    const list = elGetById(app.id + 'List');
    if (list) {
        setUpdateView(list);
    }
}

/**
 * Calculates the location hash and calls appRoute
 * @param {string} card card element name
 * @param {string} [tab] tab element name
 * @param {string} [view] view element name
 * @param {number} [offset] list offset
 * @param {number} [limit] list limit
 * @param {string | object} [filter] filter
 * @param {object} [sort] sort object
 * @param {string} [tag] tag name
 * @param {string | object} [search] search object or string
 * @param {number} [newScrollPos] new scrolling position
 * @returns {void}
 */
function appGoto(card, tab, view, offset, limit, filter, sort, tag, search, newScrollPos) {
    //old app
    const oldptr = app.cards[app.current.card].offset !== undefined
        ? app.cards[app.current.card]
        : app.cards[app.current.card].tabs[app.current.tab].offset !== undefined
            ? app.cards[app.current.card].tabs[app.current.tab]
            : app.cards[app.current.card].tabs[app.current.tab].views[app.current.view];

    //get default active tab or view from state
    if (app.cards[card].tabs) {
        if (tab === undefined) {
            tab = app.cards[card].active;
        }
        if (app.cards[card].tabs[tab].views) {
            if (view === undefined) {
                view = app.cards[card].tabs[tab].active;
            }
        }
    }

    //overwrite view for jukebox queue view
    if (card === 'Queue' &&
        tab === 'Jukebox')
    {
        view = settings.partition.jukeboxMode === 'album'
            ? 'Album'
            : 'Song';
    }

    //get ptr to new app
    const ptr = app.cards[card].offset !== undefined
        ? app.cards[card]
        : app.cards[card].tabs[tab].offset !== undefined
            ? app.cards[card].tabs[tab]
            : app.cards[card].tabs[tab].views[view];

    //save scrollPos of old app
    if (oldptr !== ptr) {
        oldptr.scrollPos = getScrollPosY();
    }

    //set options to default, if not defined
    if (offset === null || offset === undefined) { offset = ptr.offset; }
    if (limit === null || limit === undefined)   { limit = ptr.limit; }
    if (filter === null || filter === undefined) { filter = ptr.filter; }
    if (sort === null || sort === undefined)     { sort = ptr.sort; }
    if (tag === null || tag === undefined)       { tag = ptr.tag; }
    if (search === null || search === undefined) { search = ptr.search; }
    //enforce number type
    offset = Number(offset);
    limit = Number(limit);
    //set new scrollpos
    if (newScrollPos !== undefined) {
        ptr.scrollPos = newScrollPos;
    }
    //build hash
    app.goto = true;
    const newHash = myEncodeURIComponent(
        JSON.stringify({
            "card": card,
            "tab": tab,
            "view": view,
            "offset": offset,
            "limit": limit,
            "filter": filter,
            "sort": sort,
            "tag": tag,
            "search": search
        })
    );
    if (location.hash !== '#' + newHash) {
        location.hash = newHash;
    }
    appRoute(card, tab, view, offset, limit, filter, sort, tag, search);
}

/**
 * Checks if obj is string or object
 * @param {string | object} obj object to check
 * @returns {boolean} true if obj is object or string, else false
 */
function isArrayOrString(obj) {
    if (typeof obj === 'string') {
        return true;
    }
    return Array.isArray(obj);
}

/**
 * Executes the actions after the view is shown
 * @param {string} [card] card element name
 * @param {string} [tab] tab element name
 * @param {string} [view] view element name
 * @param {number} [offset] list offset
 * @param {number} [limit] list limit
 * @param {string | object} [filter] filter
 * @param {object} [sort] sort object
 * @param {string} [tag] tag name
 * @param {string | object} [search] search object or string
 * @returns {void}
 */
function appRoute(card, tab, view, offset, limit, filter, sort, tag, search) {
    if (settingsParsed === 'false') {
        appInitStart();
        return;
    }
    if (card === undefined || card === null) {
        const hash = location.hash.match(/^#(.*)$/);
        let jsonHash = null;
        if (hash !== null) {
            try {
                jsonHash = JSON.parse(decodeURIComponent(hash[1]));
                app.current.card = isArrayOrString(jsonHash.card) ? jsonHash.card : undefined;
                app.current.tab = typeof jsonHash.tab === 'string' ? jsonHash.tab : undefined;
                app.current.view = typeof jsonHash.view === 'string' ? jsonHash.view : undefined;
                app.current.offset = typeof jsonHash.offset === 'number' ? jsonHash.offset : '';
                app.current.limit = typeof jsonHash.limit === 'number' ? jsonHash.limit : '';
                app.current.filter = jsonHash.filter;
                app.current.sort = jsonHash.sort;
                app.current.tag = isArrayOrString(jsonHash.tag) ? jsonHash.tag : '';
                app.current.search = isArrayOrString(jsonHash.search) ? jsonHash.search : '';
            }
            catch(error) {
                //do nothing
                logDebug(error);
            }
        }
        if (jsonHash === null) {
            appPrepare();
            const initialStartupView = settings.webuiSettings.startupView === undefined || settings.webuiSettings.startupView === null
                ? features.featHome === true
                    ? 'Home'
                    : 'Playback'
                : features.featHome === false && settings.webuiSettings.startupView === 'Home'
                    ? 'Playback'
                    : settings.webuiSettings.startupView;
            settings.webuiSettings.startupView = initialStartupView;
            const path = initialStartupView.split('/');
            // @ts-ignore
            appGoto(...path);
            return;
        }
    }
    else {
        app.current.card = card;
        app.current.tab = tab;
        app.current.view = view;
        app.current.offset = offset;
        app.current.limit = limit;
        app.current.filter = filter;
        app.current.sort = sort;
        app.current.tag = tag;
        app.current.search = search;
    }
    app.id = app.current.card +
        (app.current.tab === undefined ? '' : app.current.tab) +
        (app.current.view === undefined ? '' : app.current.view);

    //get ptr to app options and set active tab/view
    let ptr;
    if (app.cards[app.current.card].offset !== undefined) {
        ptr = app.cards[app.current.card];
    }
    else if (app.cards[app.current.card].tabs[app.current.tab].offset !== undefined) {
        ptr = app.cards[app.current.card].tabs[app.current.tab];
        app.cards[app.current.card].active = app.current.tab;
    }
    else if (app.cards[app.current.card].tabs[app.current.tab].views[app.current.view].offset !== undefined) {
        ptr = app.cards[app.current.card].tabs[app.current.tab].views[app.current.view];
        app.cards[app.current.card].active = app.current.tab;
        app.cards[app.current.card].tabs[app.current.tab].active = app.current.view;
    }
    //set app options
    ptr.offset = app.current.offset;
    ptr.limit = app.current.limit;
    ptr.filter = app.current.filter;
    ptr.sort = app.current.sort;
    ptr.tag = app.current.tag;
    ptr.search = app.current.search;
    app.current.scrollPos = ptr.scrollPos;
    appPrepare();

    switch(app.id) {
        case 'Home':                      handleHome(); break;
        case 'Playback':                  handlePlayback(); break;
        case 'QueueCurrent':              handleQueueCurrent(); break;
        case 'QueueLastPlayed':           handleQueueLastPlayed(); break;
        case 'QueueJukeboxSong':          handleQueueJukeboxSong(); break;
        case 'QueueJukeboxAlbum':         handleQueueJukeboxAlbum(); break;
        case 'BrowsePlaylistList':        handleBrowsePlaylistList(); break;
        case 'BrowsePlaylistDetail':      handleBrowsePlaylistDetail(); break;
        case 'BrowseFilesystem':          handleBrowseFilesystem(); break;
        case 'BrowseDatabaseTagList':     handleBrowseDatabaseTagList(); break;
        case 'BrowseDatabaseAlbumList':   handleBrowseDatabaseAlbumList(); break;
        case 'BrowseDatabaseAlbumDetail': handleBrowseDatabaseAlbumDetail(); break;
        case 'BrowseRadioFavorites':      handleBrowseRadioFavorites(); break;
        case 'BrowseRadioWebradiodb':     handleBrowseRadioWebradiodb(); break;
        case 'Search':                    handleSearch(); break;
        default: {
            let initialStartupView = settings.webuiSettings.startupView;
            if (initialStartupView === undefined ||
                initialStartupView === null)
            {
                initialStartupView = features.featHome === true ? 'Home' : 'Playback';
            }
            const path = initialStartupView.split('/');
            // @ts-ignore
            appGoto(...path);
        }
    }

    app.last.card = app.current.card;
    app.last.tab = app.current.tab;
    app.last.view = app.current.view;
}

/**
 * Emulates the browser back button
 * @returns {void}
 */
//eslint-disable-next-line no-unused-vars
function historyBack() {
    history.back();
}