$.ajaxSetup({ crossDomain: true, error: function (event, jqxhr, settings, exception) { Dashboard.hideLoadingMsg(); if (!Dashboard.suppressAjaxErrors) { setTimeout(function () { var msg = event.getResponseHeader("X-Application-Error-Code") || Dashboard.defaultErrorMessage; Dashboard.showError(msg); }, 500); } } }); $.support.cors = true; $(document).one('click', WebNotifications.requestPermission); var Dashboard = { jQueryMobileInit: function () { //$.mobile.defaultPageTransition = 'slide'; // Page //$.mobile.page.prototype.options.theme = "a"; //$.mobile.page.prototype.options.headerTheme = "a"; //$.mobile.page.prototype.options.contentTheme = "a"; //$.mobile.page.prototype.options.footerTheme = "a"; //$.mobile.button.prototype.options.theme = "c"; $.mobile.listview.prototype.options.dividerTheme = "b"; $.mobile.popup.prototype.options.theme = "c"; //$.mobile.collapsible.prototype.options.contentTheme = "a"; }, getCurrentUser: function () { if (!Dashboard.getUserPromise) { Dashboard.getUserPromise = ApiClient.getUser(Dashboard.getCurrentUserId()).fail(Dashboard.logout); } return Dashboard.getUserPromise; }, validateCurrentUser: function () { Dashboard.getUserPromise = null; if (Dashboard.getCurrentUserId()) { Dashboard.getCurrentUser(); } // Re-render the header $('.header').remove(); Dashboard.ensureHeader($.mobile.activePage); }, getCurrentUserId: function () { var userId = localStorage.getItem("userId"); if (!userId) { var autoLoginUserId = getParameterByName('u'); if (autoLoginUserId) { userId = autoLoginUserId; localStorage.setItem("userId", userId); } } return userId; }, setCurrentUser: function (userId) { localStorage.setItem("userId", userId); Dashboard.getUserPromise = null; }, logout: function () { localStorage.removeItem("userId"); Dashboard.getUserPromise = null; window.location = "login.html"; }, showError: function (message) { $.mobile.loading('show', { theme: "e", text: message, textonly: true, textVisible: true }); setTimeout(function () { $.mobile.loading('hide'); }, 2000); }, alert: function (message) { $.mobile.loading('show', { theme: "e", text: message, textonly: true, textVisible: true }); setTimeout(function () { $.mobile.loading('hide'); }, 2000); }, updateSystemInfo: function (info) { var isFirstLoad = !Dashboard.lastSystemInfo; Dashboard.lastSystemInfo = info; Dashboard.ensureWebSocket(info); if (!Dashboard.initialServerVersion) { Dashboard.initialServerVersion = info.Version; } if (info.HasPendingRestart) { Dashboard.hideDashboardVersionWarning(); Dashboard.showServerRestartWarning(); } else { Dashboard.hideServerRestartWarning(); if (Dashboard.initialServerVersion != info.Version) { Dashboard.showDashboardVersionWarning(); } } if (isFirstLoad) { Dashboard.showFailedAssemblies(info.FailedPluginAssemblies); } Dashboard.showInProgressInstallations(info.InProgressInstallations); }, showFailedAssemblies: function (failedAssemblies) { for (var i = 0, length = failedAssemblies.length; i < length; i++) { var assembly = failedAssemblies[i]; var html = ''; var index = assembly.lastIndexOf('\\'); if (index != -1) { assembly = assembly.substring(index + 1); } html += ''; html += assembly + " failed to load."; html += ''; Dashboard.showFooterNotification({ html: html }); } }, showInProgressInstallations: function (installations) { installations = installations || []; for (var i = 0, length = installations.length; i < length; i++) { var installation = installations[i]; var percent = installation.PercentComplete || 0; if (percent < 100) { Dashboard.showPackageInstallNotification(installation, "progress"); } } if (installations.length) { Dashboard.ensureInstallRefreshInterval(); } else { Dashboard.stopInstallRefreshInterval(); } }, ensureInstallRefreshInterval: function () { if (!Dashboard.installRefreshInterval) { if (Dashboard.isWebSocketOpen()) { Dashboard.sendWebSocketMessage("SystemInfoStart", "0,350"); } Dashboard.installRefreshInterval = 1; } }, stopInstallRefreshInterval: function () { if (Dashboard.installRefreshInterval) { if (Dashboard.isWebSocketOpen()) { Dashboard.sendWebSocketMessage("SystemInfoStop"); } Dashboard.installRefreshInterval = null; } }, cancelInstallation: function (id) { ApiClient.cancelPackageInstallation(id).always(Dashboard.refreshSystemInfoFromServer); }, showServerRestartWarning: function () { var html = 'Please restart Media Browser Server to finish updating.'; html += ''; Dashboard.showFooterNotification({ id: "serverRestartWarning", html: html, forceShow: true, allowHide: false }); }, hideServerRestartWarning: function () { $('#serverRestartWarning').remove(); }, showDashboardVersionWarning: function () { var html = 'Please refresh this page to receive new updates from the server.'; html += ''; Dashboard.showFooterNotification({ id: "dashboardVersionWarning", html: html, forceShow: true, allowHide: false }); }, reloadPage: function () { window.location.href = window.location.href; }, hideDashboardVersionWarning: function () { $('#dashboardVersionWarning').remove(); }, showFooterNotification: function (options) { var removeOnHide = !options.id; options.id = options.id || "notification" + new Date().getTime() + parseInt(Math.random()); var parentElem = $('#footerNotifications'); var elem = $('#' + options.id, parentElem); if (!elem.length) { elem = $('

').appendTo(parentElem); } var onclick = removeOnHide ? "$(\"#" + options.id + "\").remove();" : "$(\"#" + options.id + "\").hide();"; if (options.allowHide !== false) { options.html += ""; } if (options.forceShow) { elem.show(); } elem.html(options.html).trigger('create'); if (options.timeout) { setTimeout(function () { if (removeOnHide) { elem.remove(); } else { elem.hide(); } }, options.timeout); } }, getConfigurationPageUrl: function (name) { return "ConfigurationPage?name=" + encodeURIComponent(name); }, navigate: function (url, preserveQueryString) { var queryString = window.location.search; if (preserveQueryString && queryString) { url += queryString; } $.mobile.changePage(url); }, showLoadingMsg: function () { $.mobile.showPageLoadingMsg(); }, hideLoadingMsg: function () { $.mobile.hidePageLoadingMsg(); }, processPluginConfigurationUpdateResult: function () { Dashboard.hideLoadingMsg(); Dashboard.alert("Settings saved."); }, defaultErrorMessage: "There was an error processing the request.", processServerConfigurationUpdateResult: function (result) { Dashboard.hideLoadingMsg(); Dashboard.alert("Settings saved."); }, confirm: function (message, title, callback) { $('#confirmFlyout').popup("close").remove(); var html = '
'; html += '
'; html += '

' + title + '

'; html += '
'; html += '
'; html += '
'; html += message; html += '
'; html += '

'; html += '

'; html += '
'; html += '
'; $(document.body).append(html); $('#confirmFlyout').popup().trigger('create').popup("open").on("popupafterclose", function () { if (callback) { callback(this.confirm == true); } $(this).off("popupafterclose").remove(); }); }, refreshSystemInfoFromServer: function () { ApiClient.getSystemInfo().done(function (info) { Dashboard.updateSystemInfo(info); }); }, restartServer: function () { Dashboard.suppressAjaxErrors = true; Dashboard.showLoadingMsg(); ApiClient.performPendingRestart().done(function () { setTimeout(function () { Dashboard.reloadPageWhenServerAvailable(); }, 500); }).fail(function () { Dashboard.suppressAjaxErrors = false; }); }, reloadPageWhenServerAvailable: function (retryCount) { ApiClient.getSystemInfo().done(function () { Dashboard.reloadPage(); }).fail(function () { setTimeout(function () { retryCount = retryCount || 0; retryCount++; if (retryCount < 10) { Dashboard.reloadPageWhenServerAvailable(retryCount); } else { Dashboard.suppressAjaxErrors = false; } }, 500); }); }, getPosterViewHtml: function (options) { var html = ""; for (var i = 0, length = options.items.length; i < length; i++) { var item = options.items[i]; var hasPrimaryImage = item.ImageTags && item.ImageTags.Primary; var href = item.IsFolder ? (item.Id ? "itemList.html?parentId=" + item.Id : "#") : "itemDetails.html?id=" + item.Id; var showText = options.showTitle || !hasPrimaryImage || (item.Type !== 'Movie' && item.Type !== 'Series' && item.Type !== 'Season' && item.Type !== 'Trailer'); var cssClass = showText ? "posterViewItem" : "posterViewItem posterViewItemWithNoText"; html += "
"; if (options.preferBackdrop && item.BackdropImageTags && item.BackdropImageTags.length) { html += ""; } else if (hasPrimaryImage) { html += ""; } else if (item.BackdropImageTags && item.BackdropImageTags.length) { html += ""; } else { html += ""; } if (showText) { html += "
"; html += item.Name; html += "
"; } html += "
"; } return html; }, showUserFlyout: function () { Dashboard.getCurrentUser().done(function (user) { var html = '
'; html += 'Close'; html += '
'; html += '

' + user.Name + '

'; html += '
'; html += '
'; html += '

'; var imageUrl = user.PrimaryImageTag ? ApiClient.getUserImageUrl(user.Id, { height: 400, tag: user.PrimaryImageTag, type: "Primary" }) : "css/images/userFlyoutDefault.png"; html += ''; html += '

'; html += '

'; html += '

'; html += '
'; html += '
'; $(document.body).append(html); $('#userFlyout').popup().trigger('create').popup("open").on("popupafterclose", function () { $(this).off("popupafterclose").remove(); }); }); }, selectDirectory: function (options) { options = options || {}; options.header = options.header || "Select Media Path"; var html = ''; $($.mobile.activePage).append(html); var popup = $('#popupDirectoryPicker').popup().trigger('create').popup("open").on("popupafterclose", function () { $('form', this).off("submit"); $(this).off("click").off("popupafterclose").remove(); }).on("click", ".lnkDirectory", function () { var path = this.getAttribute('data-path'); Dashboard.refreshDirectoryBrowser(path); }); var txtCurrentPath = $('#txtDirectoryPickerPath', popup); if (options.path) { txtCurrentPath.val(options.path); } $('form', popup).on('submit', function () { if (options.callback) { options.callback($('#txtDirectoryPickerPath', this).val()); } return false; }); Dashboard.refreshDirectoryBrowser(txtCurrentPath.val()); }, refreshDirectoryBrowser: function (path) { var page = $.mobile.activePage; Dashboard.showLoadingMsg(); var promise; if (path === "Network") { promise = ApiClient.getNetworkDevices(); } else if (path) { promise = ApiClient.getDirectoryContents(path, { includeDirectories: true }); } else { promise = ApiClient.getDrives(); } promise.done(function (folders) { $('#txtDirectoryPickerPath', page).val(path || ""); var html = ''; if (path) { var parentPath = path; if (parentPath.endsWith('\\')) { parentPath = parentPath.substring(0, parentPath.length - 1); } var lastIndex = parentPath.lastIndexOf('\\'); parentPath = lastIndex == -1 ? "" : parentPath.substring(0, lastIndex); if (parentPath.endsWith(':')) { parentPath += "\\"; } if (parentPath == '\\') { parentPath = "Network"; } html += '
  • ..
  • '; } for (var i = 0, length = folders.length; i < length; i++) { var folder = folders[i]; html += '
  • ' + folder.Name + '
  • '; } if (!path) { html += '
  • Network
  • '; } $('#ulDirectoryPickerList', page).html(html).listview('refresh'); Dashboard.hideLoadingMsg(); }).fail(function () { $('#txtDirectoryPickerPath', page).val(""); $('#ulDirectoryPickerList', page).html('').listview('refresh'); Dashboard.hideLoadingMsg(); }); }, getPluginSecurityInfo: function () { if (!Dashboard.getPluginSecurityInfoPromise) { var deferred = $.Deferred(); // Don't let this blow up the dashboard when it fails $.ajax({ type: "GET", url: ApiClient.getUrl("Plugins/SecurityInfo"), dataType: 'json', error: function () { // Don't show normal dashboard errors } }).done(function (result) { deferred.resolveWith(null, [[result]]); }); Dashboard.getPluginSecurityInfoPromise = deferred; } return Dashboard.getPluginSecurityInfoPromise; }, resetPluginSecurityInfo: function () { Dashboard.getPluginSecurityInfoPromise = null; }, ensureHeader: function (page) { if (!$('.header', page).length) { var isLoggedIn = Dashboard.getCurrentUserId(); if (isLoggedIn) { Dashboard.getCurrentUser().done(function (user) { Dashboard.renderHeader(page, user); }); } else { Dashboard.renderHeader(page); } } }, renderHeader: function (page, user) { var headerHtml = ''; headerHtml += '
    '; var isLibraryPage = page.hasClass('libraryPage'); headerHtml += ''; var imageColor = isLibraryPage ? "White" : "Black"; if (user && !page.hasClass('wizardPage')) { headerHtml += '
    '; headerHtml += '' + user.Name + ''; if (user.PrimaryImageTag) { var url = ApiClient.getUserImageUrl(user.Id, { width: 225, tag: user.PrimaryImageTag, type: "Primary" }); headerHtml += ''; } else { headerHtml += ''; } headerHtml += ''; if (user.Configuration.IsAdministrator) { headerHtml += ''; } headerHtml += '
    '; } headerHtml += '
    '; page.prepend(headerHtml); Dashboard.getPluginSecurityInfo().done(function (pluginSecurityInfo) { if (pluginSecurityInfo.IsMBSupporter) { $('').insertBefore('.btnTools', page); } }); }, ensureToolsMenu: function (page) { if (!page.hasClass('type-interior')) { return; } var sidebar = $('.toolsSidebar', page); if (!sidebar.length) { var html = '
    '; html += '

    Tools

    '; html += ''; // content-secondary html += '
    '; $(page).append(html); } }, getToolsMenuLinks: function (page) { var pageElem = page[0]; return [{ name: "Dashboard", href: "dashboard.html", selected: pageElem.id == "dashboardPage" }, { name: "Media Library", href: "library.html", selected: pageElem.id == "mediaLibraryPage" }, { name: "Metadata", href: "metadata.html", selected: pageElem.id == "metadataConfigurationPage" || pageElem.id == "advancedMetadataConfigurationPage" || pageElem.id == "metadataImagesConfigurationPage" }, { name: "Plugins", href: "plugins.html", selected: page.hasClass("pluginConfigurationPage") }, { name: "User Profiles", href: "userProfiles.html", selected: page.hasClass("userProfilesConfigurationPage") }, { name: "Display Settings", href: "uiSettings.html", selected: pageElem.id == "displaySettingsPage" }, { name: "Advanced", href: "advanced.html", selected: pageElem.id == "advancedConfigurationPage" }, { name: "Scheduled Tasks", href: "scheduledTasks.html", selected: pageElem.id == "scheduledTasksPage" || pageElem.id == "scheduledTaskPage" }, { name: "Help", href: "support.html", selected: pageElem.id == "supportPage" || pageElem.id == "logPage" || pageElem.id == "supporterPage" || pageElem.id == "supporterKeyPage" }]; }, ensureWebSocket: function (systemInfo) { if (!("WebSocket" in window)) { // Not supported by the browser return; } if (Dashboard.webSocket) { if (Dashboard.webSocket.readyState === WebSocket.OPEN || Dashboard.webSocket.readyState === WebSocket.CONNECTING) { return; } } systemInfo = systemInfo || Dashboard.lastSystemInfo; var url = "ws://" + ApiClient.serverHostName + ":" + systemInfo.WebSocketPortNumber + "/mediabrowser"; var ws = new WebSocket(url); ws.onmessage = Dashboard.onWebSocketMessage; ws.onopen = function () { setTimeout(function () { $(document).trigger("websocketopen"); }, 500); }; ws.onerror = function () { setTimeout(function () { $(document).trigger("websocketerror"); }, 0); }; ws.onclose = function () { setTimeout(function () { $(document).trigger("websocketclose"); }, 0); }; Dashboard.webSocket = ws; }, resetWebSocketPingInterval: function () { if (Dashboard.pingWebSocketInterval) { clearInterval(Dashboard.pingWebSocketInterval); Dashboard.pingWebSocketInterval = null; } Dashboard.pingWebSocketInterval = setInterval(Dashboard.pingWebSocket, 30000); }, pingWebSocket: function () { // Send a ping to the server every so often to try and keep the connection alive if (Dashboard.isWebSocketOpen()) { Dashboard.sendWebSocketMessage("ping"); } }, onWebSocketMessage: function (msg) { msg = JSON.parse(msg.data); if (msg.MessageType === "LibraryChanged") { Dashboard.processLibraryUpdateNotification(msg.Data); } else if (msg.MessageType === "UserDeleted") { Dashboard.validateCurrentUser(); } else if (msg.MessageType === "SystemInfo") { Dashboard.updateSystemInfo(msg.Data); } else if (msg.MessageType === "HasPendingRestartChanged") { Dashboard.updateSystemInfo(msg.Data); } else if (msg.MessageType === "UserUpdated") { Dashboard.validateCurrentUser(); var user = msg.Data; if (user.Id == Dashboard.getCurrentUserId()) { $('.currentUsername').html(user.Name); } } else if (msg.MessageType === "PackageInstallationCompleted") { Dashboard.showPackageInstallNotification(msg.Data, "completed"); Dashboard.refreshSystemInfoFromServer(); } else if (msg.MessageType === "PackageInstallationFailed") { Dashboard.showPackageInstallNotification(msg.Data, "failed"); Dashboard.refreshSystemInfoFromServer(); } else if (msg.MessageType === "PackageInstallationCancelled") { Dashboard.showPackageInstallNotification(msg.Data, "cancelled"); Dashboard.refreshSystemInfoFromServer(); } else if (msg.MessageType === "PackageInstalling") { Dashboard.showPackageInstallNotification(msg.Data, "progress"); Dashboard.refreshSystemInfoFromServer(); } else if (msg.MessageType === "ScheduledTaskEndExecute") { Dashboard.showTaskCompletionNotification(msg.Data); } $(document).trigger("websocketmessage", [msg]); }, sendWebSocketMessage: function (name, data) { var msg = { MessageType: name }; if (data) { msg.Data = data; } msg = JSON.stringify(msg); Dashboard.webSocket.send(msg); }, isWebSocketOpen: function () { return Dashboard.webSocket && Dashboard.webSocket.readyState === WebSocket.OPEN; }, showTaskCompletionNotification: function (result) { var html = ''; if (result.Status == "Completed") { html += ''; return; } else if (result.Status == "Cancelled") { html += ''; return; } else { html += ''; } html += ''; html += result.Name + " " + result.Status; html += ''; var timeout = 0; if (result.Status == 'Cancelled') { timeout = 2000; } Dashboard.showFooterNotification({ html: html, id: result.Id, forceShow: true, timeout: timeout }); }, showPackageInstallNotification: function (installation, status) { var html = ''; if (status == 'completed') { html += ''; } else if (status == 'cancelled') { html += ''; } else if (status == 'failed') { html += ''; } else if (status == 'progress') { html += ''; } html += ''; if (status == 'completed') { html += installation.Name + ' ' + installation.Version + ' installation completed'; } else if (status == 'cancelled') { html += installation.Name + ' ' + installation.Version + ' installation was cancelled'; } else if (status == 'failed') { html += installation.Name + ' ' + installation.Version + ' installation failed'; } else if (status == 'progress') { html += 'Installing ' + installation.Name + ' ' + installation.Version; } html += ''; if (status == 'progress') { var percentComplete = Math.round(installation.PercentComplete || 0); html += ''; html += '' + percentComplete + '%'; html += ''; if (percentComplete < 100) { var btnId = "btnCancel" + installation.Id; html += ''; } } var timeout = 0; if (status == 'cancelled') { timeout = 2000; } var forceShow = status != "progress"; var allowHide = status != "progress" && status != 'cancelled'; Dashboard.showFooterNotification({ html: html, id: installation.Id, timeout: timeout, forceShow: forceShow, allowHide: allowHide }); }, processLibraryUpdateNotification: function (data) { var newItems = data.ItemsAdded.filter(function (a) { return !a.IsFolder; }); if (!Dashboard.newItems) { Dashboard.newItems = []; } for (var i = 0, length = newItems.length ; i < length; i++) { Dashboard.newItems.push(newItems[i]); } if (Dashboard.newItemTimeout) { clearTimeout(Dashboard.newItemTimeout); } Dashboard.newItemTimeout = setTimeout(Dashboard.onNewItemTimerStopped, 60000); }, onNewItemTimerStopped: function () { var newItems = Dashboard.newItems; newItems = newItems.sort(function (a, b) { if (a.PrimaryImageTag && b.PrimaryImageTag) { return 0; } if (a.PrimaryImageTag) { return -1; } return 1; }); Dashboard.newItems = []; Dashboard.newItemTimeout = null; // Show at most 3 notifications for (var i = 0, length = Math.min(newItems.length, 3) ; i < length; i++) { var item = newItems[i]; var data = { title: "New " + item.Type, body: item.Name, timeout: 6000 }; if (item.PrimaryImageTag) { data.icon = ApiClient.getImageUrl(item.Id, { width: 100, tag: item.PrimaryImageTag, type: "Primary" }); } WebNotifications.show(data); } }, ensurePageTitle: function (page) { if (!page.hasClass('type-interior')) { return; } var pageElem = page[0]; if (pageElem.hasPageTitle) { return; } var parent = $('.content-primary', page); if (!parent.length) { parent = $('.ui-content', page)[0]; } $(parent).prepend("

    " + (document.title || " ") + "

    "); pageElem.hasPageTitle = true; }, setPageTitle: function (title) { $('.pageTitle', $.mobile.activePage).html(title); if (title) { document.title = title; } }, metroColors: ["#6FBD45", "#4BB3DD", "#4164A5", "#E12026", "#800080", "#E1B222", "#008040", "#0094FF", "#FF00C7", "#FF870F", "#7F0037"], getRandomMetroColor: function () { var index = Math.floor(Math.random() * (Dashboard.metroColors.length - 1)); return Dashboard.metroColors[index]; } }; $(function () { var footerHtml = ''; $(document.body).append(footerHtml); }); Dashboard.jQueryMobileInit(); $(document).on('pagebeforeshow', ".page", function () { Dashboard.refreshSystemInfoFromServer(); var page = $(this); Dashboard.ensureHeader(page); Dashboard.ensurePageTitle(page); }).on('pageinit', ".page", function () { var page = $(this); var hasLogin = Dashboard.getCurrentUserId(); if (!hasLogin) { if (this.id !== "loginPage" && !page.hasClass('wizardPage')) { Dashboard.logout(); } } else { Dashboard.getCurrentUser().done(function (user) { if (user.Configuration.IsAdministrator) { Dashboard.ensureToolsMenu(page); } }); } });