function getWindowLocationSearch(win) {
    'use strict';

    var search = (win || window).location.search;

    if (!search) {
        var index = window.location.href.indexOf('?');

        if (-1 != index) {
            search = window.location.href.substring(index);
        }
    }

    return search || '';
}

function getParameterByName(name, url) {
    'use strict';

    name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
    var regexS = '[\\?&]' + name + '=([^&#]*)';
    var regex = new RegExp(regexS, 'i');
    var results = regex.exec(url || getWindowLocationSearch());

    if (null == results) {
        return '';
    }

    return decodeURIComponent(results[1].replace(/\+/g, ' '));
}

function pageClassOn(eventName, className, fn) {
    'use strict';

    document.addEventListener(eventName, function (event) {
        var target = event.target;

        if (target.classList.contains(className)) {
            fn.call(target, event);
        }
    });
}

function pageIdOn(eventName, id, fn) {
    'use strict';

    document.addEventListener(eventName, function (event) {
        var target = event.target;

        if (target.id === id) {
            fn.call(target, event);
        }
    });
}

var Dashboard = {
    getCurrentUser: function () {
        return window.ApiClient.getCurrentUser(false);
    },

    //TODO: investigate url prefix support for serverAddress function
    serverAddress: function () {
        if (AppInfo.isNativeApp) {
            var apiClient = window.ApiClient;

            if (apiClient) {
                return apiClient.serverAddress();
            }

            return null;
        }

        var urlLower = window.location.href.toLowerCase();
        var index = urlLower.lastIndexOf('/web');

        if (-1 != index) {
            return urlLower.substring(0, index);
        }

        var loc = window.location;
        var address = loc.protocol + '//' + loc.hostname;

        if (loc.port) {
            address += ':' + loc.port;
        }

        return address;
    },
    getCurrentUserId: function () {
        var apiClient = window.ApiClient;

        if (apiClient) {
            return apiClient.getCurrentUserId();
        }

        return null;
    },
    onServerChanged: function (userId, accessToken, apiClient) {
        apiClient = apiClient || window.ApiClient;
        window.ApiClient = apiClient;
    },
    logout: function () {
        ConnectionManager.logout().then(function () {
            var loginPage;

            if (AppInfo.isNativeApp) {
                loginPage = 'selectserver.html';
                window.ApiClient = null;
            } else {
                loginPage = 'login.html';
            }

            Dashboard.navigate(loginPage);
        });
    },
    getConfigurationPageUrl: function (name) {
        return 'configurationpage?name=' + encodeURIComponent(name);
    },
    getConfigurationResourceUrl: function (name) {
        if (AppInfo.isNativeApp) {
            return ApiClient.getUrl('web/ConfigurationPage', {
                name: name
            });
        }

        return Dashboard.getConfigurationPageUrl(name);
    },
    navigate: function (url, preserveQueryString) {
        if (!url) {
            throw new Error('url cannot be null or empty');
        }

        var queryString = getWindowLocationSearch();

        if (preserveQueryString && queryString) {
            url += queryString;
        }

        return new Promise(function (resolve, reject) {
            require(['appRouter'], function (appRouter) {
                return appRouter.show(url).then(resolve, reject);
            });
        });
    },
    navigate_direct: function (path) {
        return new Promise(function (resolve, reject) {
            require(['appRouter'], function (appRouter) {
                return appRouter.showDirect(path).then(resolve, reject);
            });
        });
    },
    processPluginConfigurationUpdateResult: function () {
        require(['loading', 'toast'], function (loading, toast) {
            loading.hide();
            toast(Globalize.translate('MessageSettingsSaved'));
        });
    },
    processServerConfigurationUpdateResult: function (result) {
        require(['loading', 'toast'], function (loading, toast) {
            loading.hide();
            toast(Globalize.translate('MessageSettingsSaved'));
        });
    },
    processErrorResponse: function (response) {
        require(['loading'], function (loading) {
            loading.hide();
        });

        var status = '' + response.status;

        if (response.statusText) {
            status = response.statusText;
        }

        Dashboard.alert({
            title: status,
            message: response.headers ? response.headers.get('X-Application-Error-Code') : null
        });
    },
    alert: function (options) {
        if ('string' == typeof options) {
            return void require(['toast'], function (toast) {
                toast({
                    text: options
                });
            });
        }

        require(['alert'], function (alert) {
            alert({
                title: options.title || Globalize.translate('HeaderAlert'),
                text: options.message
            }).then(options.callback || function () {});
        });
    },
    capabilities: function (appHost) {
        var capabilities = {
            PlayableMediaTypes: ['Audio', 'Video'],
            SupportedCommands: ['MoveUp', 'MoveDown', 'MoveLeft', 'MoveRight', 'PageUp', 'PageDown', 'PreviousLetter', 'NextLetter', 'ToggleOsd', 'ToggleContextMenu', 'Select', 'Back', 'SendKey', 'SendString', 'GoHome', 'GoToSettings', 'VolumeUp', 'VolumeDown', 'Mute', 'Unmute', 'ToggleMute', 'SetVolume', 'SetAudioStreamIndex', 'SetSubtitleStreamIndex', 'DisplayContent', 'GoToSearch', 'DisplayMessage', 'SetRepeatMode', 'ChannelUp', 'ChannelDown', 'PlayMediaSource', 'PlayTrailers'],
            SupportsPersistentIdentifier: 'cordova' === self.appMode || 'android' === self.appMode,
            SupportsMediaControl: true
        };
        appHost.getPushTokenInfo();
        return capabilities = Object.assign(capabilities, appHost.getPushTokenInfo());
    },
    selectServer: function () {
        if (window.NativeShell && typeof window.NativeShell.selectServer === 'function') {
            window.NativeShell.selectServer();
        } else {
            Dashboard.navigate('selectserver.html');
        }
    },
    hideLoadingMsg: function() {
        'use strict';
        require(['loading'], function(loading) {
            loading.hide();
        });
    },
    showLoadingMsg: function() {
        'use strict';
        require(['loading'], function(loading) {
            loading.show();
        });
    },
    confirm: function(message, title, callback) {
        'use strict';
        require(['confirm'], function(confirm) {
            confirm(message, title).then(function() {
                callback(!0);
            }).catch(function() {
                callback(!1);
            });
        });
    }
};

var AppInfo = {};

!function () {
    'use strict';

    function defineConnectionManager(connectionManager) {
        window.ConnectionManager = connectionManager;
        define('connectionManager', [], function () {
            return connectionManager;
        });
    }

    function bindConnectionManagerEvents(connectionManager, events, userSettings) {
        window.Events = events;

        connectionManager.currentApiClient = function () {
            if (!localApiClient) {
                var server = connectionManager.getLastUsedServer();

                if (server) {
                    localApiClient = connectionManager.getApiClient(server.Id);
                }
            }

            return localApiClient;
        };

        connectionManager.onLocalUserSignedIn = function (user) {
            localApiClient = connectionManager.getApiClient(user.ServerId);
            window.ApiClient = localApiClient;
            return userSettings.setUserInfo(user.Id, localApiClient);
        };

        events.on(connectionManager, 'localusersignedout', function () {
            userSettings.setUserInfo(null, null);
        });
    }

    function createConnectionManager() {
        return require(['connectionManagerFactory', 'apphost', 'credentialprovider', 'events', 'userSettings'], function (ConnectionManager, apphost, credentialProvider, events, userSettings) {
            var credentialProviderInstance = new credentialProvider();
            var promises = [apphost.getSyncProfile(), apphost.init()];

            return Promise.all(promises).then(function (responses) {
                var deviceProfile = responses[0];
                var capabilities = Dashboard.capabilities(apphost);

                capabilities.DeviceProfile = deviceProfile;

                var connectionManager = new ConnectionManager(credentialProviderInstance, apphost.appName(), apphost.appVersion(), apphost.deviceName(), apphost.deviceId(), capabilities);

                defineConnectionManager(connectionManager);
                bindConnectionManagerEvents(connectionManager, events, userSettings);

                if (!AppInfo.isNativeApp) {
                    console.debug('loading ApiClient singleton');

                    return require(['apiclient'], function (apiClientFactory) {
                        console.debug('creating ApiClient singleton');

                        var apiClient = new apiClientFactory(Dashboard.serverAddress(), apphost.appName(), apphost.appVersion(), apphost.deviceName(), apphost.deviceId());

                        apiClient.enableAutomaticNetworking = false;
                        apiClient.manualAddressOnly = true;

                        connectionManager.addApiClient(apiClient);

                        window.ApiClient = apiClient;
                        localApiClient = apiClient;

                        console.debug('loaded ApiClient singleton');
                    });
                }

                return Promise.resolve();
            });
        });
    }

    function returnFirstDependency(obj) {
        return obj;
    }

    function returnDefault(obj) {
        if (obj.default === null) {
            throw new Error('Object has no default!');
        }
        return obj.default;
    }

    function getBowerPath() {
        return 'libraries';
    }

    function getComponentsPath() {
        return 'components';
    }

    function getElementsPath() {
        return 'elements';
    }

    function getScriptsPath() {
        return 'scripts';
    }

    function getPlaybackManager(playbackManager) {
        window.addEventListener('beforeunload', function () {
            try {
                playbackManager.onAppClose();
            } catch (err) {
                console.error('error in onAppClose: ' + err);
            }
        });
        return playbackManager;
    }

    function getLayoutManager(layoutManager, appHost) {
        if (appHost.getDefaultLayout) {
            layoutManager.defaultLayout = appHost.getDefaultLayout();
        }

        layoutManager.init();
        return layoutManager;
    }

    function createSharedAppFooter(appFooter) {
        return new appFooter({});
    }

    function onRequireJsError(requireType, requireModules) {
        console.error('RequireJS error: ' + (requireType || 'unknown') + '. Failed modules: ' + (requireModules || []).join(','));
    }

    function defineResizeObserver() {
        if (self.ResizeObserver) {
            define('ResizeObserver', [], function () {
                return self.ResizeObserver;
            });
        } else {
            define('ResizeObserver', ['resize-observer-polyfill'], returnFirstDependency);
        }
    }

    function initRequireWithBrowser() {
        var componentsPath = getComponentsPath();
        var scriptsPath = getScriptsPath();

        define('filesystem', [scriptsPath + '/filesystem'], returnFirstDependency);

        define('lazyLoader', [componentsPath + '/lazyLoader/lazyLoaderIntersectionObserver'], returnFirstDependency);
        define('shell', [scriptsPath + '/shell'], returnFirstDependency);

        define('registerElement', ['document-register-element'], returnFirstDependency);

        define('alert', [componentsPath + '/alert'], returnFirstDependency);

        defineResizeObserver();

        define('dialog', [componentsPath + '/dialog/dialog'], returnFirstDependency);

        define('confirm', [componentsPath + '/confirm/confirm'], returnFirstDependency);

        define('prompt', [componentsPath + '/prompt/prompt'], returnFirstDependency);

        define('loading', [componentsPath + '/loading/loading'], returnFirstDependency);
        define('multi-download', [scriptsPath + '/multiDownload'], returnFirstDependency);
        define('fileDownloader', [scriptsPath + '/fileDownloader'], returnFirstDependency);

        define('castSenderApiLoader', [componentsPath + '/castSenderApi'], returnFirstDependency);
    }

    function init() {
        define('livetvcss', ['css!assets/css/livetv.css'], returnFirstDependency);
        define('detailtablecss', ['css!assets/css/detailtable.css'], returnFirstDependency);

        var promises = [];
        if (!window.fetch) {
            promises.push(require(['fetch']));
        }

        Promise.all(promises).then(function () {
            createConnectionManager().then(function () {
                console.debug('initAfterDependencies promises resolved');

                require(['globalize', 'browser'], function (globalize, browser) {
                    window.Globalize = globalize;
                    loadCoreDictionary(globalize).then(function () {
                        onGlobalizeInit(browser);
                    });
                });
                require(['keyboardnavigation'], function(keyboardnavigation) {
                    keyboardnavigation.enable();
                });
                require(['mouseManager']);
                require(['focusPreventScroll']);
                require(['autoFocuser'], function(autoFocuser) {
                    autoFocuser.enable();
                });
                require(['globalize', 'connectionManager', 'events'], function (globalize, connectionManager, events) {
                    events.on(connectionManager, 'localusersignedin', globalize.updateCurrentCulture);
                });
            });
        });
    }

    function loadCoreDictionary(globalize) {
        var languages = ['ar', 'be-by', 'bg-bg', 'ca', 'cs', 'da', 'de', 'el', 'en-gb', 'en-us', 'es', 'es-ar', 'es-mx', 'fa', 'fi', 'fr', 'fr-ca', 'gsw', 'he', 'hi-in', 'hr', 'hu', 'id', 'it', 'kk', 'ko', 'lt-lt', 'ms', 'nb', 'nl', 'pl', 'pt-br', 'pt-pt', 'ro', 'ru', 'sk', 'sl-si', 'sv', 'tr', 'uk', 'vi', 'zh-cn', 'zh-hk', 'zh-tw'];
        var translations = languages.map(function (language) {
            return {
                lang: language,
                path: 'strings/' + language + '.json'
            };
        });
        globalize.defaultModule('core');
        return globalize.loadStrings({
            name: 'core',
            translations: translations
        });
    }

    function onGlobalizeInit(browser) {
        if ('android' === self.appMode) {
            if (-1 !== self.location.href.toString().toLowerCase().indexOf('start=backgroundsync')) {
                return onAppReady(browser);
            }
        }

        document.title = Globalize.translateDocument(document.title, 'core');

        if (browser.tv && !browser.android) {
            console.debug('using system fonts with explicit sizes');
            require(['systemFontsSizedCss']);
        } else {
            console.debug('using default fonts');
            require(['systemFontsCss']);
        }

        require(['apphost', 'css!assets/css/librarybrowser'], function (appHost) {
            loadPlugins(appHost, browser).then(function () {
                onAppReady(browser);
            });
        });
    }

    function loadPlugins(appHost, browser, shell) {
        console.debug('loading installed plugins');
        var list = [
            'plugins/playAccessValidation/plugin',
            'plugins/experimentalWarnings/plugin',
            'plugins/htmlAudioPlayer/plugin',
            'plugins/htmlVideoPlayer/plugin',
            'plugins/photoPlayer/plugin',
            'plugins/bookPlayer/plugin',
            'plugins/youtubePlayer/plugin',
            'plugins/backdropScreensaver/plugin',
            'plugins/logoScreensaver/plugin'
        ];

        if (appHost.supports('remotecontrol')) {
            list.push('plugins/sessionPlayer/plugin');

            if (browser.chrome || browser.opera) {
                list.push('plugins/chromecastPlayer/plugin');
            }
        }

        if (window.NativeShell) {
            list = list.concat(window.NativeShell.getPlugins());
        }

        return new Promise(function (resolve, reject) {
            Promise.all(list.map(loadPlugin)).then(function () {
                require(['packageManager'], function (packageManager) {
                    packageManager.init().then(resolve, reject);
                });
            }, reject);
        });
    }

    function loadPlugin(url) {
        return new Promise(function (resolve, reject) {
            require(['pluginManager'], function (pluginManager) {
                pluginManager.loadPlugin(url).then(resolve, reject);
            });
        });
    }

    function onAppReady(browser) {
        console.debug('begin onAppReady');

        // ensure that appHost is loaded in this point
        require(['apphost', 'appRouter'], function (appHost, appRouter) {
            window.Emby = {};

            console.debug('onAppReady: loading dependencies');
            if (browser.iOS) {
                require(['css!assets/css/ios.css']);
            }

            window.Emby.Page = appRouter;

            require(['emby-button', 'scripts/themeLoader', 'libraryMenu', 'scripts/routes'], function () {
                Emby.Page.start({
                    click: false,
                    hashbang: true
                });

                require(['components/themeMediaPlayer', 'scripts/autoBackdrops']);

                if (!browser.tv && !browser.xboxOne && !browser.ps4) {
                    require(['components/nowPlayingBar/nowPlayingBar']);
                }

                if (appHost.supports('remotecontrol')) {
                    require(['playerSelectionMenu', 'components/playback/remotecontrolautoplay']);
                }

                require(['libraries/screensavermanager']);

                if (!appHost.supports('physicalvolumecontrol') || browser.touch) {
                    require(['components/playback/volumeosd']);
                }

                /* eslint-disable-next-line compat/compat */
                if (navigator.mediaSession || window.NativeShell) {
                    require(['mediaSession']);
                }
                require(['serverNotifications']);
                require(['date-fns', 'date-fns/locale']);

                if (!browser.tv && !browser.xboxOne) {
                    require(['components/playback/playbackorientation']);
                    registerServiceWorker();

                    if (window.Notification) {
                        require(['components/notifications/notifications']);
                    }
                }

                require(['playerSelectionMenu']);

                var apiClient = window.ConnectionManager && window.ConnectionManager.currentApiClient();
                if (apiClient) {
                    fetch(apiClient.getUrl('Branding/Css'))
                        .then(function(response) {
                            if (!response.ok) {
                                throw new Error(response.status + ' ' + response.statusText);
                            }
                            return response.text();
                        })
                        .then(function(css) {
                            // Inject the branding css as a dom element in body so it will take
                            // precedence over other stylesheets
                            var style = document.createElement('style');
                            style.appendChild(document.createTextNode(css));
                            document.body.appendChild(style);
                        })
                        .catch(function(err) {
                            console.warn('Error applying custom css', err);
                        });
                }
            });
        });
    }

    function registerServiceWorker() {
        /* eslint-disable compat/compat */
        if (navigator.serviceWorker && self.appMode !== 'cordova' && self.appMode !== 'android') {
            try {
                navigator.serviceWorker.register('serviceworker.js');
            } catch (err) {
                console.error('error registering serviceWorker: ' + err);
            }
        } else {
            console.warn('serviceWorker unsupported');
        }
        /* eslint-enable compat/compat */
    }

    function onWebComponentsReady() {
        initRequireWithBrowser();

        if (self.appMode === 'cordova' || self.appMode === 'android' || self.appMode === 'standalone') {
            AppInfo.isNativeApp = true;
        }

        init();
    }

    var localApiClient;

    (function () {
        var urlArgs = 'v=' + (window.dashboardVersion || new Date().getDate());

        var bowerPath = getBowerPath();
        var componentsPath = getComponentsPath();
        var elementsPath = getElementsPath();
        var scriptsPath = getScriptsPath();

        var paths = {
            browserdeviceprofile: 'scripts/browserDeviceProfile',
            browser: 'scripts/browser',
            libraryBrowser: 'scripts/libraryBrowser',
            inputManager: 'scripts/inputManager',
            datetime: 'scripts/datetime',
            globalize: 'scripts/globalize',
            dfnshelper: 'scripts/dfnshelper',
            libraryMenu: 'scripts/libraryMenu',
            playlisteditor: componentsPath + '/playlisteditor/playlisteditor',
            medialibrarycreator: componentsPath + '/mediaLibraryCreator/mediaLibraryCreator',
            medialibraryeditor: componentsPath + '/mediaLibraryEditor/mediaLibraryEditor',
            imageoptionseditor: componentsPath + '/imageOptionsEditor/imageOptionsEditor',
            apphost: componentsPath + '/apphost',
            visibleinviewport: bowerPath + '/visibleinviewport',
            qualityoptions: componentsPath + '/qualityOptions',
            focusManager: componentsPath + '/focusManager',
            itemHelper: componentsPath + '/itemHelper',
            itemShortcuts: componentsPath + '/shortcuts',
            playQueueManager: componentsPath + '/playback/playqueuemanager',
            nowPlayingHelper: componentsPath + '/playback/nowplayinghelper',
            pluginManager: componentsPath + '/pluginManager',
            packageManager: componentsPath + '/packageManager',
            screensaverManager: componentsPath + '/screensavermanager',
            chromecastHelper: 'plugins/chromecastPlayer/chromecastHelpers'
        };

        requirejs.onError = onRequireJsError;
        requirejs.config({
            waitSeconds: 0,
            map: {
                '*': {
                    css: 'components/require/requirecss',
                    text: 'components/require/requiretext'
                }
            },
            bundles: {
                bundle: [
                    'document-register-element',
                    'fetch',
                    'flvjs',
                    'jstree',
                    'epubjs',
                    'jQuery',
                    'hlsjs',
                    'howler',
                    'native-promise-only',
                    'resize-observer-polyfill',
                    'shaka',
                    'swiper',
                    'queryString',
                    'sortable',
                    'webcomponents',
                    'material-icons',
                    'jellyfin-noto',
                    'date-fns',
                    'page',
                    'polyfill',
                    'fast-text-encoding',
                    'intersection-observer',
                    'classlist-polyfill',
                    'screenfull',
                    'headroom',
                    'apiclient',
                    'events',
                    'credentialprovider',
                    'connectionManagerFactory',
                    'appStorage'
                ]
            },
            urlArgs: urlArgs,
            paths: paths,
            onError: onRequireJsError
        });

        require(['fetch']);
        require(['polyfill']);
        require(['fast-text-encoding']);
        require(['intersection-observer']);
        require(['classlist-polyfill']);

        // Expose jQuery globally
        require(['jQuery'], function(jQuery) {
            window.$ = jQuery;
            window.jQuery = jQuery;
        });

        require(['css!assets/css/site']);
        require(['jellyfin-noto']);

        // define styles
        // TODO determine which of these files can be moved to the components themselves
        define('systemFontsCss', ['css!assets/css/fonts'], returnFirstDependency);
        define('systemFontsSizedCss', ['css!assets/css/fonts.sized'], returnFirstDependency);
        define('scrollStyles', ['css!assets/css/scrollstyles'], returnFirstDependency);
        define('dashboardcss', ['css!assets/css/dashboard'], returnFirstDependency);
        define('programStyles', ['css!' + componentsPath + '/guide/programs'], returnFirstDependency);
        define('listViewStyle', ['css!' + componentsPath + '/listview/listview'], returnFirstDependency);
        define('formDialogStyle', ['css!' + componentsPath + '/formdialog'], returnFirstDependency);
        define('clearButtonStyle', ['css!assets/css/clearbutton'], returnFirstDependency);
        define('cardStyle', ['css!' + componentsPath + '/cardbuilder/card'], returnFirstDependency);
        define('flexStyles', ['css!assets/css/flexstyles'], returnFirstDependency);

        // define legacy features
        // TODO delete the rest of these
        define('fnchecked', ['legacy/fnchecked'], returnFirstDependency);
        define('legacySelectMenu', ['legacy/selectmenu'], returnFirstDependency);

        // there are several objects that need to be instantiated
        // TODO find a better way to do this
        define('appFooter', [componentsPath + '/appFooter/appFooter'], returnFirstDependency);
        define('appFooter-shared', ['appFooter'], createSharedAppFooter);

        // TODO remove these libraries
        // all of these have been modified so we need to fix that first
        define('scroller', [bowerPath + '/scroller'], returnFirstDependency);
        define('navdrawer', [bowerPath + '/navdrawer/navdrawer'], returnFirstDependency);

        define('emby-button', [elementsPath + '/emby-button/emby-button'], returnFirstDependency);
        define('paper-icon-button-light', [elementsPath + '/emby-button/paper-icon-button-light'], returnFirstDependency);
        define('emby-checkbox', [elementsPath + '/emby-checkbox/emby-checkbox'], returnFirstDependency);
        define('emby-collapse', [elementsPath + '/emby-collapse/emby-collapse'], returnFirstDependency);
        define('emby-input', [elementsPath + '/emby-input/emby-input'], returnFirstDependency);
        define('emby-progressring', [elementsPath + '/emby-progressring/emby-progressring'], returnFirstDependency);
        define('emby-radio', [elementsPath + '/emby-radio/emby-radio'], returnFirstDependency);
        define('emby-select', [elementsPath + '/emby-select/emby-select'], returnFirstDependency);
        define('emby-slider', [elementsPath + '/emby-slider/emby-slider'], returnFirstDependency);
        define('emby-textarea', [elementsPath + '/emby-textarea/emby-textarea'], returnFirstDependency);
        define('emby-toggle', [elementsPath + '/emby-toggle/emby-toggle'], returnFirstDependency);
        define('emby-scroller', [elementsPath + '/emby-scroller/emby-scroller'], returnFirstDependency);
        define('emby-tabs', [elementsPath + '/emby-tabs/emby-tabs'], returnFirstDependency);
        define('emby-scrollbuttons', [elementsPath + '/emby-scrollbuttons/emby-scrollbuttons'], returnFirstDependency);
        define('emby-itemrefreshindicator', [elementsPath + '/emby-itemrefreshindicator/emby-itemrefreshindicator'], returnFirstDependency);
        define('emby-itemscontainer', [elementsPath + '/emby-itemscontainer/emby-itemscontainer'], returnFirstDependency);
        define('emby-playstatebutton', [elementsPath + '/emby-playstatebutton/emby-playstatebutton'], returnFirstDependency);
        define('emby-ratingbutton', [elementsPath + '/emby-ratingbutton/emby-ratingbutton'], returnFirstDependency);
        define('emby-progressbar', [elementsPath + '/emby-progressbar/emby-progressbar'], returnFirstDependency);
        define('emby-programcell', [elementsPath + '/emby-programcell/emby-programcell'], returnFirstDependency);

        define('webSettings', [scriptsPath + '/settings/webSettings'], returnFirstDependency);
        define('appSettings', [scriptsPath + '/settings/appSettings'], returnFirstDependency);
        define('userSettings', [scriptsPath + '/settings/userSettings'], returnFirstDependency);

        define('mediaSession', [componentsPath + '/playback/mediasession'], returnFirstDependency);
        define('actionsheet', [componentsPath + '/actionSheet/actionSheet'], returnFirstDependency);
        define('tunerPicker', [componentsPath + '/tunerPicker'], returnFirstDependency);
        define('mainTabsManager', [componentsPath + '/maintabsmanager'], returnFirstDependency);
        define('imageLoader', [componentsPath + '/images/imageLoader'], returnFirstDependency);
        define('directorybrowser', [componentsPath + '/directorybrowser/directorybrowser'], returnFirstDependency);
        define('metadataEditor', [componentsPath + '/metadataEditor/metadataEditor'], returnFirstDependency);
        define('personEditor', [componentsPath + '/metadataEditor/personEditor'], returnFirstDependency);
        define('playerSelectionMenu', [componentsPath + '/playback/playerSelectionMenu'], returnFirstDependency);
        define('playerSettingsMenu', [componentsPath + '/playback/playersettingsmenu'], returnFirstDependency);
        define('playMethodHelper', [componentsPath + '/playback/playmethodhelper'], returnFirstDependency);
        define('brightnessOsd', [componentsPath + '/playback/brightnessosd'], returnFirstDependency);
        define('alphaNumericShortcuts', [scriptsPath + '/alphanumericshortcuts'], returnFirstDependency);
        define('multiSelect', [componentsPath + '/multiSelect/multiSelect'], returnFirstDependency);
        define('alphaPicker', [componentsPath + '/alphaPicker/alphaPicker'], returnFirstDependency);
        define('tabbedView', [componentsPath + '/tabbedview/tabbedview'], returnFirstDependency);
        define('itemsTab', [componentsPath + '/tabbedview/itemstab'], returnFirstDependency);
        define('collectionEditor', [componentsPath + '/collectionEditor/collectionEditor'], returnFirstDependency);
        define('playlistEditor', [componentsPath + '/playlisteditor/playlisteditor'], returnFirstDependency);
        define('recordingCreator', [componentsPath + '/recordingcreator/recordingcreator'], returnFirstDependency);
        define('recordingEditor', [componentsPath + '/recordingcreator/recordingeditor'], returnFirstDependency);
        define('seriesRecordingEditor', [componentsPath + '/recordingcreator/seriesrecordingeditor'], returnFirstDependency);
        define('recordingFields', [componentsPath + '/recordingcreator/recordingfields'], returnFirstDependency);
        define('recordingButton', [componentsPath + '/recordingcreator/recordingbutton'], returnFirstDependency);
        define('recordingHelper', [componentsPath + '/recordingcreator/recordinghelper'], returnFirstDependency);
        define('subtitleEditor', [componentsPath + '/subtitleeditor/subtitleeditor'], returnFirstDependency);
        define('subtitleSync', [componentsPath + '/subtitlesync/subtitlesync'], returnFirstDependency);
        define('itemIdentifier', [componentsPath + '/itemidentifier/itemidentifier'], returnFirstDependency);
        define('itemMediaInfo', [componentsPath + '/itemMediaInfo/itemMediaInfo'], returnFirstDependency);
        define('mediaInfo', [componentsPath + '/mediainfo/mediainfo'], returnFirstDependency);
        define('itemContextMenu', [componentsPath + '/itemContextMenu'], returnFirstDependency);
        define('imageEditor', [componentsPath + '/imageeditor/imageeditor'], returnFirstDependency);
        define('imageDownloader', [componentsPath + '/imageDownloader/imageDownloader'], returnFirstDependency);
        define('dom', [scriptsPath + '/dom'], returnFirstDependency);
        define('playerStats', [componentsPath + '/playerstats/playerstats'], returnFirstDependency);
        define('searchFields', [componentsPath + '/search/searchfields'], returnFirstDependency);
        define('searchResults', [componentsPath + '/search/searchresults'], returnFirstDependency);
        define('upNextDialog', [componentsPath + '/upnextdialog/upnextdialog'], returnFirstDependency);
        define('subtitleAppearanceHelper', [componentsPath + '/subtitlesettings/subtitleappearancehelper'], returnFirstDependency);
        define('subtitleSettings', [componentsPath + '/subtitlesettings/subtitlesettings'], returnFirstDependency);
        define('displaySettings', [componentsPath + '/displaySettings/displaySettings'], returnFirstDependency);
        define('playbackSettings', [componentsPath + '/playbackSettings/playbackSettings'], returnFirstDependency);
        define('homescreenSettings', [componentsPath + '/homeScreenSettings/homeScreenSettings'], returnFirstDependency);
        define('playbackManager', [componentsPath + '/playback/playbackmanager'], getPlaybackManager);
        define('timeSyncManager', [componentsPath + '/syncPlay/timeSyncManager'], returnDefault);
        define('groupSelectionMenu', [componentsPath + '/syncPlay/groupSelectionMenu'], returnFirstDependency);
        define('syncPlayManager', [componentsPath + '/syncPlay/syncPlayManager'], returnDefault);
        define('playbackPermissionManager', [componentsPath + '/syncPlay/playbackPermissionManager'], returnDefault);
        define('layoutManager', [componentsPath + '/layoutManager', 'apphost'], getLayoutManager);
        define('homeSections', [componentsPath + '/homesections/homesections'], returnFirstDependency);
        define('playMenu', [componentsPath + '/playmenu'], returnFirstDependency);
        define('refreshDialog', [componentsPath + '/refreshdialog/refreshdialog'], returnFirstDependency);
        define('backdrop', [componentsPath + '/backdrop/backdrop'], returnFirstDependency);
        define('fetchHelper', [componentsPath + '/fetchhelper'], returnFirstDependency);
        define('cardBuilder', [componentsPath + '/cardbuilder/cardBuilder'], returnFirstDependency);
        define('peoplecardbuilder', [componentsPath + '/cardbuilder/peoplecardbuilder'], returnFirstDependency);
        define('chaptercardbuilder', [componentsPath + '/cardbuilder/chaptercardbuilder'], returnFirstDependency);
        define('deleteHelper', [scriptsPath + '/deleteHelper'], returnFirstDependency);
        define('tvguide', [componentsPath + '/guide/guide'], returnFirstDependency);
        define('guide-settings-dialog', [componentsPath + '/guide/guide-settings'], returnFirstDependency);
        define('viewManager', [componentsPath + '/viewManager/viewManager'], function (viewManager) {
            window.ViewManager = viewManager;
            viewManager.dispatchPageEvents(true);
            return viewManager;
        });
        define('slideshow', [componentsPath + '/slideshow/slideshow'], returnFirstDependency);
        define('focusPreventScroll', ['legacy/focusPreventScroll'], returnFirstDependency);
        define('userdataButtons', [componentsPath + '/userdatabuttons/userdatabuttons'], returnFirstDependency);
        define('listView', [componentsPath + '/listview/listview'], returnFirstDependency);
        define('indicators', [componentsPath + '/indicators/indicators'], returnFirstDependency);
        define('viewSettings', [componentsPath + '/viewSettings/viewSettings'], returnFirstDependency);
        define('filterMenu', [componentsPath + '/filtermenu/filtermenu'], returnFirstDependency);
        define('sortMenu', [componentsPath + '/sortmenu/sortmenu'], returnFirstDependency);
        define('sanitizefilename', [componentsPath + '/sanitizeFilename'], returnFirstDependency);
        define('toast', [componentsPath + '/toast/toast'], returnFirstDependency);
        define('scrollHelper', [scriptsPath + '/scrollHelper'], returnFirstDependency);
        define('touchHelper', [scriptsPath + '/touchHelper'], returnFirstDependency);
        define('imageUploader', [componentsPath + '/imageUploader/imageUploader'], returnFirstDependency);
        define('htmlMediaHelper', [componentsPath + '/htmlMediaHelper'], returnFirstDependency);
        define('viewContainer', [componentsPath + '/viewContainer'], returnFirstDependency);
        define('dialogHelper', [componentsPath + '/dialogHelper/dialogHelper'], returnFirstDependency);
        define('serverNotifications', [scriptsPath + '/serverNotifications'], returnFirstDependency);
        define('skinManager', [componentsPath + '/skinManager'], returnFirstDependency);
        define('keyboardnavigation', [scriptsPath + '/keyboardNavigation'], returnFirstDependency);
        define('mouseManager', [scriptsPath + '/mouseManager'], returnFirstDependency);
        define('scrollManager', [componentsPath + '/scrollManager'], returnFirstDependency);
        define('autoFocuser', [componentsPath + '/autoFocuser'], returnFirstDependency);
        define('connectionManager', [], function () {
            return ConnectionManager;
        });
        define('apiClientResolver', [], function () {
            return function () {
                return window.ApiClient;
            };
        });
        define('appRouter', [componentsPath + '/appRouter', 'itemHelper'], function (appRouter, itemHelper) {
            function showItem(item, serverId, options) {
                if ('string' == typeof item) {
                    require(['connectionManager'], function (connectionManager) {
                        var apiClient = connectionManager.currentApiClient();
                        apiClient.getItem(apiClient.getCurrentUserId(), item).then(function (item) {
                            appRouter.showItem(item, options);
                        });
                    });
                } else {
                    if (2 == arguments.length) {
                        options = arguments[1];
                    }

                    appRouter.show('/' + appRouter.getRouteUrl(item, options), {
                        item: item
                    });
                }
            }

            appRouter.showLocalLogin = function (serverId, manualLogin) {
                Dashboard.navigate('login.html?serverid=' + serverId);
            };

            appRouter.showVideoOsd = function () {
                return Dashboard.navigate('videoosd.html');
            };

            appRouter.showSelectServer = function () {
                Dashboard.navigate(AppInfo.isNativeApp ? 'selectserver.html' : 'login.html');
            };

            appRouter.showWelcome = function () {
                Dashboard.navigate(AppInfo.isNativeApp ? 'selectserver.html' : 'login.html');
            };

            appRouter.showSettings = function () {
                Dashboard.navigate('mypreferencesmenu.html');
            };

            appRouter.showGuide = function () {
                Dashboard.navigate('livetv.html?tab=1');
            };

            appRouter.goHome = function () {
                Dashboard.navigate('home.html');
            };

            appRouter.showSearch = function () {
                Dashboard.navigate('search.html');
            };

            appRouter.showLiveTV = function () {
                Dashboard.navigate('livetv.html');
            };

            appRouter.showRecordedTV = function () {
                Dashboard.navigate('livetv.html?tab=3');
            };

            appRouter.showFavorites = function () {
                Dashboard.navigate('home.html?tab=1');
            };

            appRouter.showSettings = function () {
                Dashboard.navigate('mypreferencesmenu.html');
            };

            appRouter.setTitle = function (title) {
                LibraryMenu.setTitle(title);
            };

            appRouter.getRouteUrl = function (item, options) {
                if (!item) {
                    throw new Error('item cannot be null');
                }

                if (item.url) {
                    return item.url;
                }

                var context = options ? options.context : null;
                var id = item.Id || item.ItemId;

                if (!options) {
                    options = {};
                }

                var url;
                var itemType = item.Type || (options ? options.itemType : null);
                var serverId = item.ServerId || options.serverId;

                if ('settings' === item) {
                    return 'mypreferencesmenu.html';
                }

                if ('wizard' === item) {
                    return 'wizardstart.html';
                }

                if ('manageserver' === item) {
                    return 'dashboard.html';
                }

                if ('recordedtv' === item) {
                    return 'livetv.html?tab=3&serverId=' + options.serverId;
                }

                if ('nextup' === item) {
                    return 'list.html?type=nextup&serverId=' + options.serverId;
                }

                if ('list' === item) {
                    var url = 'list.html?serverId=' + options.serverId + '&type=' + options.itemTypes;

                    if (options.isFavorite) {
                        url += '&IsFavorite=true';
                    }

                    return url;
                }

                if ('livetv' === item) {
                    if ('programs' === options.section) {
                        return 'livetv.html?tab=0&serverId=' + options.serverId;
                    }
                    if ('guide' === options.section) {
                        return 'livetv.html?tab=1&serverId=' + options.serverId;
                    }

                    if ('movies' === options.section) {
                        return 'list.html?type=Programs&IsMovie=true&serverId=' + options.serverId;
                    }

                    if ('shows' === options.section) {
                        return 'list.html?type=Programs&IsSeries=true&IsMovie=false&IsNews=false&serverId=' + options.serverId;
                    }

                    if ('sports' === options.section) {
                        return 'list.html?type=Programs&IsSports=true&serverId=' + options.serverId;
                    }

                    if ('kids' === options.section) {
                        return 'list.html?type=Programs&IsKids=true&serverId=' + options.serverId;
                    }

                    if ('news' === options.section) {
                        return 'list.html?type=Programs&IsNews=true&serverId=' + options.serverId;
                    }

                    if ('onnow' === options.section) {
                        return 'list.html?type=Programs&IsAiring=true&serverId=' + options.serverId;
                    }

                    if ('dvrschedule' === options.section) {
                        return 'livetv.html?tab=4&serverId=' + options.serverId;
                    }

                    if ('seriesrecording' === options.section) {
                        return 'livetv.html?tab=5&serverId=' + options.serverId;
                    }

                    return 'livetv.html?serverId=' + options.serverId;
                }

                if ('SeriesTimer' == itemType) {
                    return 'details?seriesTimerId=' + id + '&serverId=' + serverId;
                }

                if ('livetv' == item.CollectionType) {
                    return 'livetv.html';
                }

                if ('Genre' === item.Type) {
                    url = 'list.html?genreId=' + item.Id + '&serverId=' + serverId;

                    if ('livetv' === context) {
                        url += '&type=Programs';
                    }

                    if (options.parentId) {
                        url += '&parentId=' + options.parentId;
                    }

                    return url;
                }

                if ('MusicGenre' === item.Type) {
                    url = 'list.html?musicGenreId=' + item.Id + '&serverId=' + serverId;

                    if (options.parentId) {
                        url += '&parentId=' + options.parentId;
                    }

                    return url;
                }

                if ('Studio' === item.Type) {
                    url = 'list.html?studioId=' + item.Id + '&serverId=' + serverId;

                    if (options.parentId) {
                        url += '&parentId=' + options.parentId;
                    }

                    return url;
                }

                if ('folders' !== context && !itemHelper.isLocalItem(item)) {
                    if ('movies' == item.CollectionType) {
                        url = 'movies.html?topParentId=' + item.Id;

                        if (options && 'latest' === options.section) {
                            url += '&tab=1';
                        }

                        return url;
                    }

                    if ('tvshows' == item.CollectionType) {
                        url = 'tv.html?topParentId=' + item.Id;

                        if (options && 'latest' === options.section) {
                            url += '&tab=2';
                        }

                        return url;
                    }

                    if ('music' == item.CollectionType) {
                        return 'music.html?topParentId=' + item.Id;
                    }
                }

                var itemTypes = ['Playlist', 'TvChannel', 'Program', 'BoxSet', 'MusicAlbum', 'MusicGenre', 'Person', 'Recording', 'MusicArtist'];

                if (itemTypes.indexOf(itemType) >= 0) {
                    return 'details?id=' + id + '&serverId=' + serverId;
                }

                var contextSuffix = context ? '&context=' + context : '';

                if ('Series' == itemType || 'Season' == itemType || 'Episode' == itemType) {
                    return 'details?id=' + id + contextSuffix + '&serverId=' + serverId;
                }

                if (item.IsFolder) {
                    if (id) {
                        return 'list.html?parentId=' + id + '&serverId=' + serverId;
                    }

                    return '#';
                }

                return 'details?id=' + id + '&serverId=' + serverId;
            };

            appRouter.showItem = showItem;
            return appRouter;
        });
    })();

    return onWebComponentsReady();
}();

pageClassOn('viewshow', 'standalonePage', function () {
    document.querySelector('.skinHeader').classList.add('noHeaderRight');
});

pageClassOn('viewhide', 'standalonePage', function () {
    document.querySelector('.skinHeader').classList.remove('noHeaderRight');
});
