diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..80f9bc36ed --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +/CONTRIBUTORS.md merge=union diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 80c1723091..3773562e54 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -9,8 +9,14 @@ - [bfayers](https://github.com/bfayers) - [Bond_009](https://github.com/Bond-009) - [AnthonyLavado](https://github.com/anthonylavado) + - [dkanada](https://github.com/dkanada) - [sparky8251](https://github.com/sparky8251) - [LeoVerto](https://github.com/LeoVerto) + - [cvium](https://github.com/cvium) + - [grafixeyehero](https://github.com/grafixeyehero) + - [Drago96](https://github.com/drago-96) + - [ViXXoR](https://github.com/ViXXoR) + - [nkmerrill] (https://github.com/nkmerrill) # Emby Contributors diff --git a/src/addplugin.html b/src/addplugin.html index 1c8f0d4bc3..c8b1d4ecff 100644 --- a/src/addplugin.html +++ b/src/addplugin.html @@ -35,44 +35,6 @@

- -
diff --git a/src/addserver.html b/src/addserver.html new file mode 100644 index 0000000000..f87e889502 --- /dev/null +++ b/src/addserver.html @@ -0,0 +1,22 @@ +
+
+
+

${HeaderConnectToServer}

+
+ +
${LabelServerHostHelp}
+
+
+
+ +
+
+ + +
+
+
\ No newline at end of file diff --git a/src/bower_components/emby-apiclient/apiclient.js b/src/bower_components/emby-apiclient/apiclient.js index def6a26664..2aed3b9987 100644 --- a/src/bower_components/emby-apiclient/apiclient.js +++ b/src/bower_components/emby-apiclient/apiclient.js @@ -1,4 +1,4 @@ -define(["events", "appStorage", "wakeOnLan"], function(events, appStorage, wakeOnLan) { +define(["events", "appStorage"], function(events, appStorage) { "use strict"; function redetectBitrate(instance) { @@ -201,36 +201,6 @@ define(["events", "appStorage", "wakeOnLan"], function(events, appStorage, wakeO ratio && (options.minScale && (ratio = Math.max(options.minScale, ratio)), options.width && (options.width = Math.round(options.width * ratio)), options.height && (options.height = Math.round(options.height * ratio)), options.maxWidth && (options.maxWidth = Math.round(options.maxWidth * ratio)), options.maxHeight && (options.maxHeight = Math.round(options.maxHeight * ratio))), options.quality = options.quality || instance.getDefaultImageQuality(options.type), instance.normalizeImageOptions && instance.normalizeImageOptions(options) } - function getCachedWakeOnLanInfo(instance) { - var serverId = instance.serverId(), - json = appStorage.getItem("server-" + serverId + "-wakeonlaninfo"); - return json ? JSON.parse(json) : [] - } - - function refreshWakeOnLanInfoIfNeeded(instance) { - wakeOnLan.isSupported() && instance.accessToken() && !1 !== instance.enableAutomaticBitrateDetection && (console.log("refreshWakeOnLanInfoIfNeeded"), setTimeout(refreshWakeOnLanInfo.bind(instance), 1e4)) - } - - function refreshWakeOnLanInfo() { - var instance = this; - console.log("refreshWakeOnLanInfo"), instance.getWakeOnLanInfo().then(function(info) { - var serverId = instance.serverId(); - return appStorage.setItem("server-" + serverId + "-wakeonlaninfo", JSON.stringify(info)), info - }, function(err) { - return [] - }) - } - - function sendNextWakeOnLan(infos, index, resolve) { - if (index >= infos.length) return void resolve(); - var info = infos[index]; - console.log("sending wakeonlan to " + info.MacAddress), wakeOnLan.send(info).then(function(result) { - sendNextWakeOnLan(infos, index + 1, resolve) - }, function() { - sendNextWakeOnLan(infos, index + 1, resolve) - }) - } - function compareVersions(a, b) { a = a.split("."), b = b.split("."); for (var i = 0, length = Math.max(a.length, b.length); i < length; i++) { @@ -266,7 +236,7 @@ define(["events", "appStorage", "wakeOnLan"], function(events, appStorage, wakeO } return this._serverAddress }, ApiClient.prototype.onNetworkChange = function() { - this.lastDetectedBitrate = 0, this.lastDetectedBitrateTime = 0, setSavedEndpointInfo(this, null), redetectBitrate(this), refreshWakeOnLanInfoIfNeeded(this) + this.lastDetectedBitrate = 0, this.lastDetectedBitrateTime = 0, setSavedEndpointInfo(this, null), redetectBitrate(this) }, ApiClient.prototype.getUrl = function(name, params, serverAddress) { if (!name) throw new Error("Url name cannot be empty"); var url = serverAddress || this._serverAddress; @@ -301,7 +271,7 @@ define(["events", "appStorage", "wakeOnLan"], function(events, appStorage, wakeO } return this.fetchWithFailover(request, !0) }, ApiClient.prototype.setAuthenticationInfo = function(accessKey, userId) { - this._currentUser = null, this._serverInfo.AccessToken = accessKey, this._serverInfo.UserId = userId, redetectBitrate(this), refreshWakeOnLanInfoIfNeeded(this) + this._currentUser = null, this._serverInfo.AccessToken = accessKey, this._serverInfo.UserId = userId, redetectBitrate(this) }, ApiClient.prototype.serverInfo = function(info) { return info && (this._serverInfo = info), this._serverInfo }, ApiClient.prototype.getCurrentUserId = function() { @@ -360,7 +330,7 @@ define(["events", "appStorage", "wakeOnLan"], function(events, appStorage, wakeO contentType: "application/json" }).then(function(result) { var afterOnAuthenticated = function() { - redetectBitrate(instance), refreshWakeOnLanInfoIfNeeded(instance), resolve(result) + redetectBitrate(instance), resolve(result) }; instance.onAuthenticated ? instance.onAuthenticated(instance, result).then(afterOnAuthenticated) : afterOnAuthenticated() }, reject) @@ -1579,19 +1549,10 @@ define(["events", "appStorage", "wakeOnLan"], function(events, appStorage, wakeO return this.getJSON(this.getUrl("System/Endpoint")).then(function(endPointInfo) { return setSavedEndpointInfo(instance, endPointInfo), endPointInfo }) - }, ApiClient.prototype.getWakeOnLanInfo = function() { - return this.getJSON(this.getUrl("System/WakeOnLanInfo")) }, ApiClient.prototype.getLatestItems = function(options) { return options = options || {}, this.getJSON(this.getUrl("Users/" + this.getCurrentUserId() + "/Items/Latest", options)) }, ApiClient.prototype.getFilters = function(options) { return this.getJSON(this.getUrl("Items/Filters2", options)) - }, ApiClient.prototype.supportsWakeOnLan = function() { - return !!wakeOnLan.isSupported() && getCachedWakeOnLanInfo(this).length > 0 - }, ApiClient.prototype.wakeOnLan = function() { - var infos = getCachedWakeOnLanInfo(this); - return new Promise(function(resolve, reject) { - sendNextWakeOnLan(infos, 0, resolve) - }) }, ApiClient.prototype.setSystemInfo = function(info) { this._serverVersion = info.Version }, ApiClient.prototype.serverVersion = function() { diff --git a/src/bower_components/emby-apiclient/credentials.js b/src/bower_components/emby-apiclient/credentials.js index 51a9cb55c6..a200b60124 100644 --- a/src/bower_components/emby-apiclient/credentials.js +++ b/src/bower_components/emby-apiclient/credentials.js @@ -24,6 +24,6 @@ define(["events", "appStorage"], function(events, appStorage) { var existing = list.filter(function(s) { return s.Id === server.Id })[0]; - return existing ? (existing.DateLastAccessed = Math.max(existing.DateLastAccessed || 0, server.DateLastAccessed || 0), existing.UserLinkType = server.UserLinkType, server.AccessToken && (existing.AccessToken = server.AccessToken, existing.UserId = server.UserId), server.ExchangeToken && (existing.ExchangeToken = server.ExchangeToken), server.RemoteAddress && (existing.RemoteAddress = server.RemoteAddress), server.ManualAddress && (existing.ManualAddress = server.ManualAddress), server.LocalAddress && (existing.LocalAddress = server.LocalAddress), server.Name && (existing.Name = server.Name), server.WakeOnLanInfos && server.WakeOnLanInfos.length && (existing.WakeOnLanInfos = server.WakeOnLanInfos), null != server.LastConnectionMode && (existing.LastConnectionMode = server.LastConnectionMode), server.ConnectServerId && (existing.ConnectServerId = server.ConnectServerId), existing) : (list.push(server), server) + return existing ? (existing.DateLastAccessed = Math.max(existing.DateLastAccessed || 0, server.DateLastAccessed || 0), existing.UserLinkType = server.UserLinkType, server.AccessToken && (existing.AccessToken = server.AccessToken, existing.UserId = server.UserId), server.ExchangeToken && (existing.ExchangeToken = server.ExchangeToken), server.RemoteAddress && (existing.RemoteAddress = server.RemoteAddress), server.ManualAddress && (existing.ManualAddress = server.ManualAddress), server.LocalAddress && (existing.LocalAddress = server.LocalAddress), server.Name && (existing.Name = server.Name), null != server.LastConnectionMode && (existing.LastConnectionMode = server.LastConnectionMode), server.ConnectServerId && (existing.ConnectServerId = server.ConnectServerId), existing) : (list.push(server), server) }, Credentials }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/actionsheet/actionsheet.css b/src/bower_components/emby-webcomponents/actionsheet/actionsheet.css index e22ca0bb7b..611fafa5ca 100644 --- a/src/bower_components/emby-webcomponents/actionsheet/actionsheet.css +++ b/src/bower_components/emby-webcomponents/actionsheet/actionsheet.css @@ -1,158 +1,113 @@ -.actionSheet, -.actionSheetContent { - display: -webkit-box; - display: -webkit-flex -} - -.actionSheetContent, -.actionSheetScroller { - -webkit-box-orient: vertical; - -webkit-box-direction: normal -} - .actionSheet { display: flex; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; padding: 0; border: none; max-height: 84%; - -webkit-border-radius: .1em !important; - border-radius: .1em !important + border-radius: .1em !important; } .actionsheet-not-fullscreen { max-width: 90%; - max-height: 90% + max-height: 90%; } .actionsheet-fullscreen { max-height: none; - -webkit-border-radius: 0 !important; - border-radius: 0 !important + border-radius: 0 !important; } .actionSheetContent-centered { text-align: center; - -webkit-box-align: center; - -webkit-align-items: center; - align-items: center + align-items: center; } .actionSheetContent { margin: 0 !important; padding: .4em 0 !important; - -webkit-flex-direction: column; flex-direction: column; display: flex; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; - -webkit-box-flex: 1; - -webkit-flex-grow: 1; flex-grow: 1; - overflow: hidden + overflow: hidden; } .actionSheetMenuItem { font-weight: inherit; - -webkit-box-shadow: none; box-shadow: none; - -webkit-flex-shrink: 0; - flex-shrink: 0 + flex-shrink: 0; } -.actionSheetMenuItem:focus { - -webkit-transform: none !important; - transform: none !important -} + .actionSheetMenuItem:focus { + transform: none !important; + } .actionsheetListItemBody { - padding: .4em 1em .4em .6em !important + padding: .4em 1em .4em .6em !important; } .actionSheetItemText { white-space: nowrap; overflow: hidden; - -o-text-overflow: ellipsis; text-overflow: ellipsis; vertical-align: middle; - -webkit-box-flex: 1; - -webkit-flex-grow: 1; flex-grow: 1; - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-pack: start; - -webkit-justify-content: flex-start; - justify-content: flex-start + justify-content: flex-start; } .actionSheetItemAsideText { opacity: .7; font-size: 90%; - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-pack: end; - -webkit-justify-content: flex-end; justify-content: flex-end; - -webkit-flex-shrink: 0; flex-shrink: 0; margin-left: 5em; - margin-right: .5em + margin-right: .5em; } .actionSheetScroller { + /* Override default style being applied by polymer */ margin-bottom: 0 !important; - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-flex-direction: column; flex-direction: column; - width: 100% + width: 100%; } .actionSheetScroller-tv { max-height: 64%; max-width: 60%; - width: auto + width: auto; } .actionsheetDivider { height: .07em; margin: .25em 0; - -webkit-flex-shrink: 0; - flex-shrink: 0 + flex-shrink: 0; } .actionSheetTitle { margin: .6em 0 .7em !important; padding: 0 .9em; - -webkit-box-flex: 0; - -webkit-flex-grow: 0; - flex-grow: 0 + flex-grow: 0; } .actionSheetText { padding: 0 1em; - -webkit-box-flex: 0; - -webkit-flex-grow: 0; - flex-grow: 0 + flex-grow: 0; } .actionsheetMenuItemIcon { margin: 0 .85em 0 .45em !important; - padding: 0 !important + padding: 0 !important; } .actionsheet-xlargeFont { - font-size: 112% !important + font-size: 112%!important; } .btnCloseActionSheet { position: fixed; top: .75em; - left: .5em -} \ No newline at end of file + left: .5em; +} diff --git a/src/bower_components/emby-webcomponents/actionsheet/actionsheet.js b/src/bower_components/emby-webcomponents/actionsheet/actionsheet.js index 6c93bb2507..067d427aef 100644 --- a/src/bower_components/emby-webcomponents/actionsheet/actionsheet.js +++ b/src/bower_components/emby-webcomponents/actionsheet/actionsheet.js @@ -1,93 +1,363 @@ -define(["dialogHelper", "layoutManager", "globalize", "browser", "dom", "emby-button", "css!./actionsheet", "material-icons", "scrollStyles", "listViewStyle"], function(dialogHelper, layoutManager, globalize, browser, dom) { - "use strict"; +define(['dialogHelper', 'layoutManager', 'globalize', 'browser', 'dom', 'emby-button', 'css!./actionsheet', 'material-icons', 'scrollStyles', 'listViewStyle'], function (dialogHelper, layoutManager, globalize, browser, dom) { + 'use strict'; function getOffsets(elems) { - var doc = document, - results = []; - if (!doc) return results; - for (var box, elem, i = 0, length = elems.length; i < length; i++) elem = elems[i], box = elem.getBoundingClientRect ? elem.getBoundingClientRect() : { - top: 0, - left: 0 - }, results[i] = { - top: box.top, - left: box.left, - width: box.width, - height: box.height - }; - return results + + var doc = document; + var results = []; + + if (!doc) { + return results; + } + + var box; + var elem; + + for (var i = 0, length = elems.length; i < length; i++) { + + elem = elems[i]; + // Support: BlackBerry 5, iOS 3 (original iPhone) + // If we don't have gBCR, just use 0,0 rather than error + if (elem.getBoundingClientRect) { + box = elem.getBoundingClientRect(); + } else { + box = { top: 0, left: 0 }; + } + + results[i] = { + top: box.top, + left: box.left, + width: box.width, + height: box.height + }; + } + + return results; } function getPosition(options, dlg) { - var windowSize = dom.getWindowSize(), - windowHeight = windowSize.innerHeight, - windowWidth = windowSize.innerWidth; - if (windowWidth < 600 || windowHeight < 600) return null; + + var windowSize = dom.getWindowSize(); + var windowHeight = windowSize.innerHeight; + var windowWidth = windowSize.innerWidth; + + if (windowWidth < 600 || windowHeight < 600) { + return null; + } + var pos = getOffsets([options.positionTo])[0]; - "top" !== options.positionY && (pos.top += (pos.height || 0) / 2), pos.left += (pos.width || 0) / 2; - var height = dlg.offsetHeight || 300, - width = dlg.offsetWidth || 160; - pos.top -= height / 2, pos.left -= width / 2; - var overflowX = pos.left + width - windowWidth, - overflowY = pos.top + height - windowHeight; - return overflowX > 0 && (pos.left -= overflowX + 20), overflowY > 0 && (pos.top -= overflowY + 20), pos.top += options.offsetTop || 0, pos.left += options.offsetLeft || 0, pos.top = Math.max(pos.top, 10), pos.left = Math.max(pos.left, 10), pos + + if (options.positionY !== 'top') { + pos.top += (pos.height || 0) / 2; + } + + pos.left += (pos.width || 0) / 2; + + var height = dlg.offsetHeight || 300; + var width = dlg.offsetWidth || 160; + + // Account for popup size + pos.top -= height / 2; + pos.left -= width / 2; + + // Avoid showing too close to the bottom + var overflowX = pos.left + width - windowWidth; + var overflowY = pos.top + height - windowHeight; + + if (overflowX > 0) { + pos.left -= (overflowX + 20); + } + if (overflowY > 0) { + pos.top -= (overflowY + 20); + } + + pos.top += (options.offsetTop || 0); + pos.left += (options.offsetLeft || 0); + + // Do some boundary checking + pos.top = Math.max(pos.top, 10); + pos.left = Math.max(pos.left, 10); + + return pos; } function centerFocus(elem, horiz, on) { - require(["scrollHelper"], function(scrollHelper) { - var fn = on ? "on" : "off"; - scrollHelper.centerFocus[fn](elem, horiz) - }) + require(['scrollHelper'], function (scrollHelper) { + var fn = on ? 'on' : 'off'; + scrollHelper.centerFocus[fn](elem, horiz); + }); } function show(options) { - var isFullscreen, dialogOptions = { - removeOnClose: !0, + + // items + // positionTo + // showCancel + // title + var dialogOptions = { + removeOnClose: true, enableHistory: options.enableHistory, - scrollY: !1 + scrollY: false }; - layoutManager.tv ? (dialogOptions.size = "fullscreen", isFullscreen = !0, !0, dialogOptions.autoFocus = !0) : (dialogOptions.modal = !1, dialogOptions.entryAnimation = options.entryAnimation, dialogOptions.exitAnimation = options.exitAnimation, dialogOptions.entryAnimationDuration = options.entryAnimationDuration || 140, dialogOptions.exitAnimationDuration = options.exitAnimationDuration || 100, dialogOptions.autoFocus = !1); - var dlg = dialogHelper.createDialog(dialogOptions); - isFullscreen ? dlg.classList.add("actionsheet-fullscreen") : dlg.classList.add("actionsheet-not-fullscreen"), dlg.classList.add("actionSheet"), options.dialogClass && dlg.classList.add(options.dialogClass); - var html = "", - scrollClassName = layoutManager.tv ? "scrollY smoothScrollY hiddenScrollY" : "scrollY", - style = ""; - if (options.items.length > 20) { - style += "min-width:" + (dom.getWindowSize().innerWidth >= 300 ? 240 : 200) + "px;" + + var backButton = false; + var isFullscreen; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + isFullscreen = true; + backButton = true; + dialogOptions.autoFocus = true; + } else { + + dialogOptions.modal = false; + dialogOptions.entryAnimation = options.entryAnimation; + dialogOptions.exitAnimation = options.exitAnimation; + dialogOptions.entryAnimationDuration = options.entryAnimationDuration || 140; + dialogOptions.exitAnimationDuration = options.exitAnimationDuration || 100; + dialogOptions.autoFocus = false; } - var i, length, option, itemIcon, renderIcon = !1, - icons = []; - for (i = 0, length = options.items.length; i < length; i++) option = options.items[i], itemIcon = option.icon || (option.selected ? "check" : null), itemIcon && (renderIcon = !0), icons.push(itemIcon || ""); - layoutManager.tv && (html += ''); - var center = options.title && !renderIcon; - center || layoutManager.tv ? html += '
' : html += '
', options.title && (html += '

', html += options.title, html += "

"), options.text && (html += '

', html += options.text, html += "

"); - var scrollerClassName = "actionSheetScroller"; - layoutManager.tv && (scrollerClassName += " actionSheetScroller-tv focuscontainer-x focuscontainer-y"), html += '
'; - var menuItemClass = "listItem listItem-button actionSheetMenuItem"; - for ((options.border || options.shaded) && (menuItemClass += " listItem-border"), options.menuItemClass && (menuItemClass += " " + options.menuItemClass), layoutManager.tv && (menuItemClass += " listItem-focusscale"), layoutManager.mobile && (menuItemClass += " actionsheet-xlargeFont"), i = 0, length = options.items.length; i < length; i++) - if (option = options.items[i], option.divider) html += '
'; - else { - var autoFocus = option.selected && layoutManager.tv ? " autoFocus" : "", - optionId = null == option.id || "" === option.id ? option.value : option.id; - html += "', itemIcon = icons[i], itemIcon ? html += '' + itemIcon + "" : renderIcon && !center && (html += ''), html += '
', html += '
', html += option.name || option.textContent || option.innerText, html += "
", option.secondaryText && (html += '
', html += option.secondaryText, html += "
"), html += "
", option.asideText && (html += '
', html += option.asideText, html += "
"), html += "" - } options.showCancel && (html += '
', html += '", html += "
"), html += "
", dlg.innerHTML = html, layoutManager.tv && centerFocus(dlg.querySelector(".actionSheetScroller"), !1, !0), dlg.querySelector(".btnCloseActionSheet") && dlg.querySelector(".btnCloseActionSheet").addEventListener("click", function() { - dialogHelper.close(dlg) - }); - var selectedId, timeout; - return options.timeout && (timeout = setTimeout(function() { - dialogHelper.close(dlg) - }, options.timeout)), new Promise(function(resolve, reject) { + + var dlg = dialogHelper.createDialog(dialogOptions); + + if (isFullscreen) { + dlg.classList.add('actionsheet-fullscreen'); + } else { + dlg.classList.add('actionsheet-not-fullscreen'); + } + + dlg.classList.add('actionSheet'); + + if (options.dialogClass) { + dlg.classList.add(options.dialogClass); + } + + var html = ''; + + var scrollClassName = layoutManager.tv ? 'scrollY smoothScrollY hiddenScrollY' : 'scrollY'; + var style = ''; + + // Admittedly a hack but right now the scrollbar is being factored into the width which is causing truncation + if (options.items.length > 20) { + var minWidth = dom.getWindowSize().innerWidth >= 300 ? 240 : 200; + style += "min-width:" + minWidth + "px;"; + } + + var i, length, option; + var renderIcon = false; + var icons = []; + var itemIcon; + for (i = 0, length = options.items.length; i < length; i++) { + + option = options.items[i]; + + itemIcon = option.icon || (option.selected ? 'check' : null); + + if (itemIcon) { + renderIcon = true; + } + icons.push(itemIcon || ''); + } + + if (layoutManager.tv) { + html += ''; + } + + // If any items have an icon, give them all an icon just to make sure they're all lined up evenly + var center = options.title && (!renderIcon /*|| itemsWithIcons.length != options.items.length*/); + + if (center || layoutManager.tv) { + html += '
'; + } else { + html += '
'; + } + + if (options.title) { + + html += '

'; + html += options.title; + html += '

'; + } + if (options.text) { + html += '

'; + html += options.text; + html += '

'; + } + + var scrollerClassName = 'actionSheetScroller'; + if (layoutManager.tv) { + scrollerClassName += ' actionSheetScroller-tv focuscontainer-x focuscontainer-y'; + } + html += '
'; + + var menuItemClass = 'listItem listItem-button actionSheetMenuItem'; + + if (options.border || options.shaded) { + menuItemClass += ' listItem-border'; + } + + if (options.menuItemClass) { + menuItemClass += ' ' + options.menuItemClass; + } + + if (layoutManager.tv) { + menuItemClass += ' listItem-focusscale'; + } + + if (layoutManager.mobile) { + menuItemClass += ' actionsheet-xlargeFont'; + } + + for (i = 0, length = options.items.length; i < length; i++) { + + option = options.items[i]; + + if (option.divider) { + + html += '
'; + continue; + } + + var autoFocus = option.selected && layoutManager.tv ? ' autoFocus' : ''; + + // Check for null in case int 0 was passed in + var optionId = option.id == null || option.id === '' ? option.value : option.id; + html += ''; + + itemIcon = icons[i]; + + if (itemIcon) { + + html += '' + itemIcon + ''; + } + else if (renderIcon && !center) { + html += ''; + } + + html += '
'; + + html += '
'; + html += (option.name || option.textContent || option.innerText); + html += '
'; + + if (option.secondaryText) { + html += '
'; + html += option.secondaryText; + html += '
'; + } + + html += '
'; + + if (option.asideText) { + html += '
'; + html += option.asideText; + html += '
'; + } + + html += ''; + } + + if (options.showCancel) { + html += '
'; + html += ''; + html += '
'; + } + html += '
'; + + dlg.innerHTML = html; + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.actionSheetScroller'), false, true); + } + + var btnCloseActionSheet = dlg.querySelector('.btnCloseActionSheet'); + if (btnCloseActionSheet) { + dlg.querySelector('.btnCloseActionSheet').addEventListener('click', function () { + dialogHelper.close(dlg); + }); + } + + // Seeing an issue in some non-chrome browsers where this is requiring a double click + //var eventName = browser.firefox ? 'mousedown' : 'click'; + var selectedId; + + var timeout; + if (options.timeout) { + timeout = setTimeout(function () { + dialogHelper.close(dlg); + }, options.timeout); + } + + return new Promise(function (resolve, reject) { + var isResolved; - dlg.addEventListener("click", function(e) { - var actionSheetMenuItem = dom.parentWithClass(e.target, "actionSheetMenuItem"); - actionSheetMenuItem && (selectedId = actionSheetMenuItem.getAttribute("data-id"), options.resolveOnClick && (options.resolveOnClick.indexOf ? -1 !== options.resolveOnClick.indexOf(selectedId) && (resolve(selectedId), isResolved = !0) : (resolve(selectedId), isResolved = !0)), dialogHelper.close(dlg)) - }), dlg.addEventListener("close", function() { - layoutManager.tv && centerFocus(dlg.querySelector(".actionSheetScroller"), !1, !1), timeout && (clearTimeout(timeout), timeout = null), isResolved || (null != selectedId ? (options.callback && options.callback(selectedId), resolve(selectedId)) : reject()) - }), dialogHelper.open(dlg); - var pos = options.positionTo && "fullscreen" !== dialogOptions.size ? getPosition(options, dlg) : null; - pos && (dlg.style.position = "fixed", dlg.style.margin = 0, dlg.style.left = pos.left + "px", dlg.style.top = pos.top + "px") - }) + + dlg.addEventListener('click', function (e) { + + var actionSheetMenuItem = dom.parentWithClass(e.target, 'actionSheetMenuItem'); + + if (actionSheetMenuItem) { + selectedId = actionSheetMenuItem.getAttribute('data-id'); + + if (options.resolveOnClick) { + + if (options.resolveOnClick.indexOf) { + + if (options.resolveOnClick.indexOf(selectedId) !== -1) { + + resolve(selectedId); + isResolved = true; + } + + } else { + resolve(selectedId); + isResolved = true; + } + } + + dialogHelper.close(dlg); + } + + }); + + dlg.addEventListener('close', function () { + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.actionSheetScroller'), false, false); + } + + if (timeout) { + clearTimeout(timeout); + timeout = null; + } + + if (!isResolved) { + if (selectedId != null) { + if (options.callback) { + options.callback(selectedId); + } + + resolve(selectedId); + } else { + reject(); + } + } + }); + + dialogHelper.open(dlg); + + var pos = options.positionTo && dialogOptions.size !== 'fullscreen' ? getPosition(options, dlg) : null; + + if (pos) { + dlg.style.position = 'fixed'; + dlg.style.margin = 0; + dlg.style.left = pos.left + 'px'; + dlg.style.top = pos.top + 'px'; + } + }); } + return { show: show - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/alert/alert.js b/src/bower_components/emby-webcomponents/alert/alert.js index c9df6c23a3..83090c8d45 100644 --- a/src/bower_components/emby-webcomponents/alert/alert.js +++ b/src/bower_components/emby-webcomponents/alert/alert.js @@ -1,18 +1,34 @@ -define(["dialog", "globalize"], function(dialog, globalize) { - "use strict"; - return function(text, title) { +define(['dialog', 'globalize'], function (dialog, globalize) { + 'use strict'; + + return function (text, title) { + var options; - options = "string" == typeof text ? { - title: title, - text: text - } : text; + if (typeof text === 'string') { + options = { + title: title, + text: text + }; + } else { + options = text; + } + var items = []; - return items.push({ - name: globalize.translate("sharedcomponents#ButtonGotIt"), - id: "ok", - type: "submit" - }), options.buttons = items, dialog(options).then(function(result) { - return "ok" === result ? Promise.resolve() : Promise.reject() - }) - } + + items.push({ + name: globalize.translate('sharedcomponents#ButtonGotIt'), + id: 'ok', + type: 'submit' + }); + + options.buttons = items; + + return dialog(options).then(function (result) { + if (result === 'ok') { + return Promise.resolve(); + } + + return Promise.reject(); + }); + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/alert/nativealert.js b/src/bower_components/emby-webcomponents/alert/nativealert.js index 3d06cf10fe..f565e802c7 100644 --- a/src/bower_components/emby-webcomponents/alert/nativealert.js +++ b/src/bower_components/emby-webcomponents/alert/nativealert.js @@ -1,14 +1,23 @@ -define([], function() { - "use strict"; +define([], function () { + 'use strict'; function replaceAll(str, find, replace) { - return str.split(find).join(replace) - } - return function(options) { - "string" == typeof options && (options = { - text: options - }); - var text = replaceAll(options.text || "", "
", "\n"); - return alert(text), Promise.resolve() + + return str.split(find).join(replace); } + + return function (options) { + + if (typeof options === 'string') { + options = { + text: options + }; + } + + var text = replaceAll(options.text || '', '
', '\n'); + + alert(text); + + return Promise.resolve(); + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/alphanumericshortcuts/alphanumericshortcuts.js b/src/bower_components/emby-webcomponents/alphanumericshortcuts/alphanumericshortcuts.js index 2b4abe9e92..8517d1c3df 100644 --- a/src/bower_components/emby-webcomponents/alphanumericshortcuts/alphanumericshortcuts.js +++ b/src/bower_components/emby-webcomponents/alphanumericshortcuts/alphanumericshortcuts.js @@ -1,59 +1,130 @@ -define(["dom", "focusManager"], function(dom, focusManager) { - "use strict"; +define(['dom', 'focusManager'], function (dom, focusManager) { + 'use strict'; + + var inputDisplayElement; + var currentDisplayText = ''; + var currentDisplayTextContainer; function onKeyDown(e) { - if (!e.ctrlKey && !e.shiftKey && !e.altKey) { - var key = e.key, - chr = key ? alphanumeric(key) : null; - chr && (chr = chr.toString().toUpperCase(), 1 === chr.length && (currentDisplayTextContainer = this.options.itemsContainer, onAlphanumericKeyPress(e, chr))) + + if (e.ctrlKey) { + return; + } + if (!!e.shiftKey) { + return; + } + if (e.altKey) { + return; + } + + var key = e.key; + var chr = key ? alphanumeric(key) : null; + + if (chr) { + + chr = chr.toString().toUpperCase(); + + if (chr.length === 1) { + currentDisplayTextContainer = this.options.itemsContainer; + onAlphanumericKeyPress(e, chr); + } } } function alphanumeric(value) { var letterNumber = /^[0-9a-zA-Z]+$/; - return value.match(letterNumber) + return value.match(letterNumber); } function ensureInputDisplayElement() { - inputDisplayElement || (inputDisplayElement = document.createElement("div"), inputDisplayElement.classList.add("alphanumeric-shortcut"), inputDisplayElement.classList.add("hide"), document.body.appendChild(inputDisplayElement)) + if (!inputDisplayElement) { + inputDisplayElement = document.createElement('div'); + inputDisplayElement.classList.add('alphanumeric-shortcut'); + inputDisplayElement.classList.add('hide'); + + document.body.appendChild(inputDisplayElement); + } } + var alpanumericShortcutTimeout; function clearAlphaNumericShortcutTimeout() { - alpanumericShortcutTimeout && (clearTimeout(alpanumericShortcutTimeout), alpanumericShortcutTimeout = null) + if (alpanumericShortcutTimeout) { + clearTimeout(alpanumericShortcutTimeout); + alpanumericShortcutTimeout = null; + } } - function resetAlphaNumericShortcutTimeout() { - clearAlphaNumericShortcutTimeout(), alpanumericShortcutTimeout = setTimeout(onAlphanumericShortcutTimeout, 2e3) + clearAlphaNumericShortcutTimeout(); + alpanumericShortcutTimeout = setTimeout(onAlphanumericShortcutTimeout, 2000); } function onAlphanumericKeyPress(e, chr) { - currentDisplayText.length >= 3 || (ensureInputDisplayElement(), currentDisplayText += chr, inputDisplayElement.innerHTML = currentDisplayText, inputDisplayElement.classList.remove("hide"), resetAlphaNumericShortcutTimeout()) + if (currentDisplayText.length >= 3) { + return; + } + ensureInputDisplayElement(); + currentDisplayText += chr; + inputDisplayElement.innerHTML = currentDisplayText; + inputDisplayElement.classList.remove('hide'); + resetAlphaNumericShortcutTimeout(); } function onAlphanumericShortcutTimeout() { - var value = currentDisplayText, - container = currentDisplayTextContainer; - currentDisplayText = "", currentDisplayTextContainer = null, inputDisplayElement.innerHTML = "", inputDisplayElement.classList.add("hide"), clearAlphaNumericShortcutTimeout(), selectByShortcutValue(container, value) + var value = currentDisplayText; + var container = currentDisplayTextContainer; + + currentDisplayText = ''; + currentDisplayTextContainer = null; + inputDisplayElement.innerHTML = ''; + inputDisplayElement.classList.add('hide'); + clearAlphaNumericShortcutTimeout(); + selectByShortcutValue(container, value); } function selectByShortcutValue(container, value) { + value = value.toUpperCase(); + var focusElem; - "#" === value && (focusElem = container.querySelector("*[data-prefix]")), focusElem || (focusElem = container.querySelector("*[data-prefix^='" + value + "']")), focusElem && focusManager.focus(focusElem) + if (value === '#') { + + focusElem = container.querySelector('*[data-prefix]'); + } + + if (!focusElem) { + focusElem = container.querySelector('*[data-prefix^=\'' + value + '\']'); + } + + if (focusElem) { + focusManager.focus(focusElem); + } } function AlphaNumericShortcuts(options) { + this.options = options; + var keyDownHandler = onKeyDown.bind(this); - dom.addEventListener(window, "keydown", keyDownHandler, { - passive: !0 - }), this.keyDownHandler = keyDownHandler + + dom.addEventListener(window, 'keydown', keyDownHandler, { + passive: true + }); + + this.keyDownHandler = keyDownHandler; } - var inputDisplayElement, currentDisplayTextContainer, alpanumericShortcutTimeout, currentDisplayText = ""; - return AlphaNumericShortcuts.prototype.destroy = function() { + + AlphaNumericShortcuts.prototype.destroy = function () { + var keyDownHandler = this.keyDownHandler; - keyDownHandler && (dom.removeEventListener(window, "keydown", keyDownHandler, { - passive: !0 - }), this.keyDownHandler = null), this.options = null - }, AlphaNumericShortcuts + + if (keyDownHandler) { + dom.removeEventListener(window, 'keydown', keyDownHandler, { + passive: true + }); + this.keyDownHandler = null; + } + this.options = null; + }; + + return AlphaNumericShortcuts; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/alphapicker/alphapicker.js b/src/bower_components/emby-webcomponents/alphapicker/alphapicker.js index 1d7fabb6f8..4cf77f451a 100644 --- a/src/bower_components/emby-webcomponents/alphapicker/alphapicker.js +++ b/src/bower_components/emby-webcomponents/alphapicker/alphapicker.js @@ -1,127 +1,324 @@ -define(["focusManager", "layoutManager", "dom", "css!./style.css", "paper-icon-button-light", "material-icons"], function(focusManager, layoutManager, dom) { - "use strict"; +define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-button-light', 'material-icons'], function (focusManager, layoutManager, dom) { + 'use strict'; + + var selectedButtonClass = 'alphaPickerButton-selected'; function focus() { - var scope = this, - selected = scope.querySelector("." + selectedButtonClass); - selected ? focusManager.focus(selected) : focusManager.autoFocus(scope, !0) + var scope = this; + var selected = scope.querySelector('.' + selectedButtonClass); + + if (selected) { + focusManager.focus(selected); + } else { + focusManager.autoFocus(scope, true); + } } function getAlphaPickerButtonClassName(vertical) { - var alphaPickerButtonClassName = "alphaPickerButton"; - return layoutManager.tv && (alphaPickerButtonClassName += " alphaPickerButton-tv"), vertical && (alphaPickerButtonClassName += " alphaPickerButton-vertical"), alphaPickerButtonClassName + + var alphaPickerButtonClassName = 'alphaPickerButton'; + + if (layoutManager.tv) { + alphaPickerButtonClassName += ' alphaPickerButton-tv'; + } + + if (vertical) { + alphaPickerButtonClassName += ' alphaPickerButton-vertical'; + } + + return alphaPickerButtonClassName; } function getLetterButton(l, vertical) { - return '" + return ''; } function mapLetters(letters, vertical) { - return letters.map(function(l) { - return getLetterButton(l, vertical) - }) + + return letters.map(function (l) { + return getLetterButton(l, vertical); + }); } function render(element, options) { - element.classList.add("alphaPicker"), layoutManager.tv && element.classList.add("alphaPicker-tv"); - var vertical = element.classList.contains("alphaPicker-vertical"); - vertical || element.classList.add("focuscontainer-x"); - var letters, html = "", - alphaPickerButtonClassName = getAlphaPickerButtonClassName(vertical), - rowClassName = "alphaPickerRow"; - vertical && (rowClassName += " alphaPickerRow-vertical"), html += '
', "keyboard" === options.mode ? html += '' : (letters = ["#"], html += mapLetters(letters, vertical).join("")), letters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"], html += mapLetters(letters, vertical).join(""), "keyboard" === options.mode ? (html += '', html += "
", letters = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], html += '
', html += "
", html += mapLetters(letters, vertical).join(""), html += "
") : html += "
", element.innerHTML = html, element.classList.add("focusable"), element.focus = focus + + element.classList.add('alphaPicker'); + + if (layoutManager.tv) { + element.classList.add('alphaPicker-tv'); + } + + var vertical = element.classList.contains('alphaPicker-vertical'); + + if (vertical) { + + } else { + element.classList.add('focuscontainer-x'); + } + + var html = ''; + var letters; + + var alphaPickerButtonClassName = getAlphaPickerButtonClassName(vertical); + + var rowClassName = 'alphaPickerRow'; + + if (vertical) { + rowClassName += ' alphaPickerRow-vertical'; + } + + html += '
'; + if (options.mode === 'keyboard') { + // space_bar icon + html += ''; + } else { + letters = ['#']; + html += mapLetters(letters, vertical).join(''); + } + + letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; + html += mapLetters(letters, vertical).join(''); + + if (options.mode === 'keyboard') { + // backspace icon + html += ''; + html += '
'; + + letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + html += '
'; + html += '
'; + html += mapLetters(letters, vertical).join(''); + html += '
'; + } else { + html += '
'; + } + + element.innerHTML = html; + + element.classList.add('focusable'); + element.focus = focus; } function AlphaPicker(options) { + + var self = this; + this.options = options; + + var element = options.element; + var itemsContainer = options.itemsContainer; + var itemClass = options.itemClass; + + var itemFocusValue; + var itemFocusTimeout; + function onItemFocusTimeout() { - itemFocusTimeout = null, self.value(itemFocusValue) + itemFocusTimeout = null; + self.value(itemFocusValue); } + var alphaFocusedElement; + var alphaFocusTimeout; + function onAlphaFocusTimeout() { - if (alphaFocusTimeout = null, document.activeElement === alphaFocusedElement) { - var value = alphaFocusedElement.getAttribute("data-value"); - self.value(value, !0) + + alphaFocusTimeout = null; + + if (document.activeElement === alphaFocusedElement) { + var value = alphaFocusedElement.getAttribute('data-value'); + self.value(value, true); } } function onAlphaPickerInKeyboardModeClick(e) { - var alphaPickerButton = dom.parentWithClass(e.target, "alphaPickerButton"); + + var alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton'); + if (alphaPickerButton) { - var value = alphaPickerButton.getAttribute("data-value"); + var value = alphaPickerButton.getAttribute('data-value'); + element.dispatchEvent(new CustomEvent("alphavalueclicked", { - cancelable: !1, + cancelable: false, detail: { value: value } - })) + })); } } function onAlphaPickerClick(e) { - var alphaPickerButton = dom.parentWithClass(e.target, "alphaPickerButton"); + + var alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton'); + if (alphaPickerButton) { - var value = alphaPickerButton.getAttribute("data-value"); - (this._currentValue || "").toUpperCase() === value.toUpperCase() ? self.value(null, !0) : self.value(value, !0) + var value = alphaPickerButton.getAttribute('data-value'); + if ((this._currentValue || '').toUpperCase() === value.toUpperCase()) { + self.value(null, true); + } else { + self.value(value, true); + } } } function onAlphaPickerFocusIn(e) { - alphaFocusTimeout && (clearTimeout(alphaFocusTimeout), alphaFocusTimeout = null); - var alphaPickerButton = dom.parentWithClass(e.target, "alphaPickerButton"); - alphaPickerButton && (alphaFocusedElement = alphaPickerButton, alphaFocusTimeout = setTimeout(onAlphaFocusTimeout, 600)) + + if (alphaFocusTimeout) { + clearTimeout(alphaFocusTimeout); + alphaFocusTimeout = null; + } + + var alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton'); + + if (alphaPickerButton) { + alphaFocusedElement = alphaPickerButton; + alphaFocusTimeout = setTimeout(onAlphaFocusTimeout, 600); + } } function onItemsFocusIn(e) { + var item = dom.parentWithClass(e.target, itemClass); + if (item) { - var prefix = item.getAttribute("data-prefix"); - prefix && prefix.length && (itemFocusValue = prefix[0], itemFocusTimeout && clearTimeout(itemFocusTimeout), itemFocusTimeout = setTimeout(onItemFocusTimeout, 100)) + var prefix = item.getAttribute('data-prefix'); + if (prefix && prefix.length) { + + itemFocusValue = prefix[0]; + if (itemFocusTimeout) { + clearTimeout(itemFocusTimeout); + } + itemFocusTimeout = setTimeout(onItemFocusTimeout, 100); + } } } - var self = this; - this.options = options; - var itemFocusValue, itemFocusTimeout, alphaFocusedElement, alphaFocusTimeout, element = options.element, - itemsContainer = options.itemsContainer, - itemClass = options.itemClass; - self.enabled = function(enabled) { - enabled ? (itemsContainer && itemsContainer.addEventListener("focus", onItemsFocusIn, !0), "keyboard" === options.mode && element.addEventListener("click", onAlphaPickerInKeyboardModeClick), "click" !== options.valueChangeEvent ? element.addEventListener("focus", onAlphaPickerFocusIn, !0) : element.addEventListener("click", onAlphaPickerClick.bind(this))) : (itemsContainer && itemsContainer.removeEventListener("focus", onItemsFocusIn, !0), element.removeEventListener("click", onAlphaPickerInKeyboardModeClick), element.removeEventListener("focus", onAlphaPickerFocusIn, !0), element.removeEventListener("click", onAlphaPickerClick.bind(this))) - }, render(element, options), this.enabled(!0), this.visible(!0) - } - var selectedButtonClass = "alphaPickerButton-selected"; - return AlphaPicker.prototype.value = function(value, applyValue) { - var btn, selected, element = this.options.element; - if (void 0 !== value) - if (null != value) { - if (value = value.toUpperCase(), this._currentValue = value, "keyboard" !== this.options.mode) { - selected = element.querySelector("." + selectedButtonClass); - try { - btn = element.querySelector(".alphaPickerButton[data-value='" + value + "']") - } catch (err) { - console.log("Error in querySelector: " + err) - } - btn && btn !== selected && btn.classList.add(selectedButtonClass), selected && selected !== btn && selected.classList.remove(selectedButtonClass) + + self.enabled = function (enabled) { + + if (enabled) { + + if (itemsContainer) { + itemsContainer.addEventListener('focus', onItemsFocusIn, true); } - } else this._currentValue = value, (selected = element.querySelector("." + selectedButtonClass)) && selected.classList.remove(selectedButtonClass); - return applyValue && element.dispatchEvent(new CustomEvent("alphavaluechanged", { - cancelable: !1, - detail: { - value: value + + if (options.mode === 'keyboard') { + element.addEventListener('click', onAlphaPickerInKeyboardModeClick); + } + + if (options.valueChangeEvent !== 'click') { + element.addEventListener('focus', onAlphaPickerFocusIn, true); + } else { + element.addEventListener('click', onAlphaPickerClick.bind(this)); + } + + } else { + + if (itemsContainer) { + itemsContainer.removeEventListener('focus', onItemsFocusIn, true); + } + + element.removeEventListener('click', onAlphaPickerInKeyboardModeClick); + element.removeEventListener('focus', onAlphaPickerFocusIn, true); + element.removeEventListener('click', onAlphaPickerClick.bind(this)); } - })), this._currentValue - }, AlphaPicker.prototype.on = function(name, fn) { - this.options.element.addEventListener(name, fn) - }, AlphaPicker.prototype.off = function(name, fn) { - this.options.element.removeEventListener(name, fn) - }, AlphaPicker.prototype.visible = function(visible) { - this.options.element.style.visibility = visible ? "visible" : "hidden" - }, AlphaPicker.prototype.values = function() { - for (var element = this.options.element, elems = element.querySelectorAll(".alphaPickerButton"), values = [], i = 0, length = elems.length; i < length; i++) values.push(elems[i].getAttribute("data-value")); - return values - }, AlphaPicker.prototype.focus = function() { + }; + + render(element, options); + + this.enabled(true); + this.visible(true); + } + + AlphaPicker.prototype.value = function (value, applyValue) { + var element = this.options.element; - focusManager.autoFocus(element, !0) - }, AlphaPicker.prototype.destroy = function() { + var btn, selected; + + if (value !== undefined) { + if (value != null) { + + value = value.toUpperCase(); + this._currentValue = value; + + if (this.options.mode !== 'keyboard') { + selected = element.querySelector('.' + selectedButtonClass); + + try { + btn = element.querySelector('.alphaPickerButton[data-value=\'' + value + '\']'); + } catch (err) { + console.log('Error in querySelector: ' + err); + } + + if (btn && btn !== selected) { + btn.classList.add(selectedButtonClass); + } + if (selected && selected !== btn) { + selected.classList.remove(selectedButtonClass); + } + } + } else { + this._currentValue = value; + + selected = element.querySelector('.' + selectedButtonClass); + if (selected) { + selected.classList.remove(selectedButtonClass); + } + } + } + + if (applyValue) { + element.dispatchEvent(new CustomEvent("alphavaluechanged", { + cancelable: false, + detail: { + value: value + } + })); + } + + return this._currentValue; + }; + + AlphaPicker.prototype.on = function (name, fn) { var element = this.options.element; - this.enabled(!1), element.classList.remove("focuscontainer-x"), this.options = null - }, AlphaPicker + element.addEventListener(name, fn); + }; + + AlphaPicker.prototype.off = function (name, fn) { + var element = this.options.element; + element.removeEventListener(name, fn); + }; + + AlphaPicker.prototype.visible = function (visible) { + + var element = this.options.element; + element.style.visibility = visible ? 'visible' : 'hidden'; + }; + + AlphaPicker.prototype.values = function () { + + var element = this.options.element; + var elems = element.querySelectorAll('.alphaPickerButton'); + var values = []; + for (var i = 0, length = elems.length; i < length; i++) { + + values.push(elems[i].getAttribute('data-value')); + + } + + return values; + }; + + AlphaPicker.prototype.focus = function () { + + var element = this.options.element; + focusManager.autoFocus(element, true); + }; + + AlphaPicker.prototype.destroy = function () { + + var element = this.options.element; + this.enabled(false); + element.classList.remove('focuscontainer-x'); + this.options = null; + }; + + return AlphaPicker; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/alphapicker/style.css b/src/bower_components/emby-webcomponents/alphapicker/style.css index 6c366e8113..29543421eb 100644 --- a/src/bower_components/emby-webcomponents/alphapicker/style.css +++ b/src/bower_components/emby-webcomponents/alphapicker/style.css @@ -1,59 +1,35 @@ -.alphaPicker, -.alphaPickerRow { - display: -webkit-box; - display: -webkit-flex; - -webkit-box-direction: normal -} - -.alphaPicker, -.alphaPickerRow, -.alphaPickerRow-vertical { - -webkit-box-direction: normal -} - .alphaPicker { text-align: center; display: flex; - -webkit-box-orient: vertical; - -webkit-flex-direction: column; flex-direction: column; - -webkit-align-self: center; - align-self: center + align-self: center; } .alphaPicker-vertical { - line-height: 1 + line-height: 1; } .alphaPicker-fixed { position: fixed; bottom: 5.5em; - z-index: 999999 + z-index: 999999; } .alphaPickerRow { display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; - -webkit-box-orient: horizontal; - -webkit-flex-direction: row; - flex-direction: row + flex-direction: row; } .alphaPickerRow-vertical { - -webkit-box-orient: vertical; - -webkit-flex-direction: column; - flex-direction: column + flex-direction: column; } .alphaPickerButton { border: 0 !important; cursor: pointer; - outline: 0 !important; + outline: none !important; vertical-align: middle; font-family: inherit; font-size: inherit; @@ -61,103 +37,103 @@ margin: 0; padding: .1em .4em; width: auto; - -webkit-border-radius: .1em; border-radius: .1em; - font-weight: 400; - -webkit-flex-shrink: 0; + font-weight: normal; flex-shrink: 0; - -webkit-box-flex: 1; - -webkit-flex-grow: 1; - flex-grow: 1 + flex-grow: 1; } -@media all and (max-height:50em) { +@media all and (max-height: 50em) { + .alphaPicker-fixed { - bottom: 5em + bottom: 5em; } .alphaPickerButton-vertical { padding-top: 1px !important; - padding-bottom: 1px !important + padding-bottom: 1px !important; } } -@media all and (max-height:49em) { +@media all and (max-height: 49em) { + .alphaPicker-vertical { - font-size: 94% + font-size: 94%; } } -@media all and (max-height:44em) { +@media all and (max-height: 44em) { + .alphaPicker-vertical { - font-size: 90% + font-size: 90%; } .alphaPickerButton-vertical { padding-top: 0 !important; - padding-bottom: 0 !important + padding-bottom: 0 !important; } } -@media all and (max-height:37em) { +@media all and (max-height: 37em) { + .alphaPicker-vertical { - font-size: 82% + font-size: 82%; } } -@media all and (max-height:32em) { +@media all and (max-height: 32em) { + .alphaPicker-vertical { - font-size: 74% + font-size: 74%; } } .alphaPicker-vertical.alphaPicker-tv { - font-size: 86% + font-size: 86%; } .alphaPickerButton-tv.alphaPickerButton-vertical { - padding: 0 + padding: 0; } .alphaPickerButton-vertical { + /* Assign a fixed width to ensure they have the same dimensions and avoid throwing off directional navigation */ width: 1.5em; - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; - text-align: center + text-align: center; } .alphaPickerButtonIcon { - font-size: 100% !important + font-size: 100% !important; } .alphaPicker-fixed.alphaPicker-tv { - bottom: 1% + bottom: 1%; } .alphaPicker-fixed-left { - left: .4em + left: .4em; } .alphaPicker-fixed-right { - right: .4em + right: .4em; } -@media all and (min-width:62.5em) { +@media all and (min-width: 62.5em) { + .alphaPicker-fixed-left { - left: 1em + left: 1em; } .alphaPicker-fixed-right { - right: 1em + right: 1em; } } -@media all and (max-height:31.25em) { +@media all and (max-height: 31.25em) { + .alphaPicker-fixed { - display: none !important + display: none !important; } -} \ No newline at end of file +} diff --git a/src/bower_components/emby-webcomponents/appfooter/appfooter.css b/src/bower_components/emby-webcomponents/appfooter/appfooter.css index 7a97db76df..320d34a4a9 100644 --- a/src/bower_components/emby-webcomponents/appfooter/appfooter.css +++ b/src/bower_components/emby-webcomponents/appfooter/appfooter.css @@ -1,16 +1,13 @@ -.appfooter { +.appfooter { position: fixed; left: 0; right: 0; z-index: 1; bottom: 0; - -webkit-transition: -webkit-transform 180ms linear; - -o-transition: transform 180ms linear; transition: transform 180ms linear; - contain: layout style + contain: layout style; } -.appfooter.headroom--unpinned { - -webkit-transform: translateY(100%) !important; - transform: translateY(100%) !important -} \ No newline at end of file + .appfooter.headroom--unpinned { + transform: translateY(100%)!important; + } \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/appfooter/appfooter.js b/src/bower_components/emby-webcomponents/appfooter/appfooter.js index e9dbffe0bb..3f69efc573 100644 --- a/src/bower_components/emby-webcomponents/appfooter/appfooter.js +++ b/src/bower_components/emby-webcomponents/appfooter/appfooter.js @@ -1,20 +1,46 @@ -define(["browser", "css!./appfooter"], function(browser) { - "use strict"; +define(['browser', 'css!./appfooter'], function (browser) { + 'use strict'; function render(options) { - var elem = document.createElement("div"); - return elem.classList.add("appfooter"), browser.chrome || elem.classList.add("appfooter-blurred"), document.body.appendChild(elem), elem + + var elem = document.createElement('div'); + + elem.classList.add('appfooter'); + + if (!browser.chrome) { + // chrome does not display this properly + elem.classList.add('appfooter-blurred'); + } + + document.body.appendChild(elem); + + return elem; } function appFooter(options) { + var self = this; - self.element = render(options), self.add = function(elem) { - self.element.appendChild(elem) - }, self.insert = function(elem) { - "string" == typeof elem ? self.element.insertAdjacentHTML("afterbegin", elem) : self.element.insertBefore(elem, self.element.firstChild) - } + + self.element = render(options); + + self.add = function (elem) { + self.element.appendChild(elem); + }; + + self.insert = function (elem) { + if (typeof elem === 'string') { + self.element.insertAdjacentHTML('afterbegin', elem); + } else { + self.element.insertBefore(elem, self.element.firstChild); + } + }; } - return appFooter.prototype.destroy = function() { - this.element = null - }, appFooter + + appFooter.prototype.destroy = function () { + var self = this; + + self.element = null; + }; + + return appFooter; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/appsettings.js b/src/bower_components/emby-webcomponents/appsettings.js index 722bdf2aec..3fd2b33183 100644 --- a/src/bower_components/emby-webcomponents/appsettings.js +++ b/src/bower_components/emby-webcomponents/appsettings.js @@ -1,39 +1,156 @@ -define(["appStorage", "events"], function(appStorage, events) { - "use strict"; +define(['appStorage', 'events'], function (appStorage, events) { + 'use strict'; function getKey(name, userId) { - return userId && (name = userId + "-" + name), name + + if (userId) { + name = userId + '-' + name; + } + + return name; } - function AppSettings() {} - return AppSettings.prototype.enableAutoLogin = function(val) { - return null != val && this.set("enableAutoLogin", val.toString()), "false" !== this.get("enableAutoLogin") - }, AppSettings.prototype.enableAutomaticBitrateDetection = function(isInNetwork, mediaType, val) { - var key = "enableautobitratebitrate-" + mediaType + "-" + isInNetwork; - return null != val && (isInNetwork && "Audio" === mediaType && (val = !0), this.set(key, val.toString())), !(!isInNetwork || "Audio" !== mediaType) || "false" !== this.get(key) - }, AppSettings.prototype.maxStreamingBitrate = function(isInNetwork, mediaType, val) { - var key = "maxbitrate-" + mediaType + "-" + isInNetwork; - return null != val && (isInNetwork && "Audio" === mediaType || this.set(key, val)), isInNetwork && "Audio" === mediaType ? 15e7 : parseInt(this.get(key) || "0") || 15e5 - }, AppSettings.prototype.maxStaticMusicBitrate = function(val) { - void 0 !== val && this.set("maxStaticMusicBitrate", val); - var defaultValue = 32e4; - return parseInt(this.get("maxStaticMusicBitrate") || defaultValue.toString()) || defaultValue - }, AppSettings.prototype.maxChromecastBitrate = function(val) { - return null != val && this.set("chromecastBitrate1", val), val = this.get("chromecastBitrate1"), val ? parseInt(val) : null - }, AppSettings.prototype.syncOnlyOnWifi = function(val) { - return null != val && this.set("syncOnlyOnWifi", val.toString()), "false" !== this.get("syncOnlyOnWifi") - }, AppSettings.prototype.syncPath = function(val) { - return null != val && this.set("syncPath", val), this.get("syncPath") - }, AppSettings.prototype.cameraUploadServers = function(val) { - return null != val && this.set("cameraUploadServers", val.join(",")), val = this.get("cameraUploadServers"), val ? val.split(",") : [] - }, AppSettings.prototype.runAtStartup = function(val) { - return null != val && this.set("runatstartup", val.toString()), "true" === this.get("runatstartup") - }, AppSettings.prototype.set = function(name, value, userId) { + function AppSettings() { + + } + + AppSettings.prototype.enableAutoLogin = function (val) { + + if (val != null) { + this.set('enableAutoLogin', val.toString()); + } + + return this.get('enableAutoLogin') !== 'false'; + }; + + AppSettings.prototype.enableAutomaticBitrateDetection = function (isInNetwork, mediaType, val) { + + var key = 'enableautobitratebitrate-' + mediaType + '-' + isInNetwork; + + if (val != null) { + + if (isInNetwork && mediaType === 'Audio') { + val = true; + } + + this.set(key, val.toString()); + } + + if (isInNetwork && mediaType === 'Audio') { + return true; + } else { + return this.get(key) !== 'false'; + } + }; + + AppSettings.prototype.maxStreamingBitrate = function (isInNetwork, mediaType, val) { + + var key = 'maxbitrate-' + mediaType + '-' + isInNetwork; + + if (val != null) { + + if (isInNetwork && mediaType === 'Audio') { + // nothing to do, this is always a max value + } else { + this.set(key, val); + } + } + + if (isInNetwork && mediaType === 'Audio') { + // return a huge number so that it always direct plays + return 150000000; + } else { + return parseInt(this.get(key) || '0') || 1500000; + } + }; + + AppSettings.prototype.maxStaticMusicBitrate = function (val) { + + if (val !== undefined) { + this.set('maxStaticMusicBitrate', val); + } + + var defaultValue = 320000; + return parseInt(this.get('maxStaticMusicBitrate') || defaultValue.toString()) || defaultValue; + }; + + AppSettings.prototype.maxChromecastBitrate = function (val) { + + if (val != null) { + this.set('chromecastBitrate1', val); + } + + val = this.get('chromecastBitrate1'); + + return val ? parseInt(val) : null; + }; + + AppSettings.prototype.syncOnlyOnWifi = function (val) { + + if (val != null) { + this.set('syncOnlyOnWifi', val.toString()); + } + + return this.get('syncOnlyOnWifi') !== 'false'; + }; + + AppSettings.prototype.syncPath = function (val) { + + if (val != null) { + this.set('syncPath', val); + } + + return this.get('syncPath'); + }; + + AppSettings.prototype.cameraUploadServers = function (val) { + + if (val != null) { + this.set('cameraUploadServers', val.join(',')); + } + + val = this.get('cameraUploadServers'); + + if (val) { + return val.split(','); + } + + return []; + }; + + AppSettings.prototype.runAtStartup = function (val) { + + if (val != null) { + this.set('runatstartup', val.toString()); + } + + return this.get('runatstartup') === 'true'; + }; + + AppSettings.prototype.set = function (name, value, userId) { + var currentValue = this.get(name, userId); - appStorage.setItem(getKey(name, userId), value), currentValue !== value && events.trigger(this, "change", [name]) - }, AppSettings.prototype.get = function(name, userId) { - return appStorage.getItem(getKey(name, userId)) - }, AppSettings.prototype.enableSystemExternalPlayers = function(val) { - return null != val && this.set("enableSystemExternalPlayers", val.toString()), "true" === this.get("enableSystemExternalPlayers") - }, new AppSettings + + appStorage.setItem(getKey(name, userId), value); + + if (currentValue !== value) { + events.trigger(this, 'change', [name]); + } + }; + + AppSettings.prototype.get = function (name, userId) { + + return appStorage.getItem(getKey(name, userId)); + }; + + AppSettings.prototype.enableSystemExternalPlayers = function (val) { + + if (val != null) { + this.set('enableSystemExternalPlayers', val.toString()); + } + + return this.get('enableSystemExternalPlayers') === 'true'; + }; + + return new AppSettings(); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/backdrop/backdrop.js b/src/bower_components/emby-webcomponents/backdrop/backdrop.js index 2f42ccd347..12a077c68c 100644 --- a/src/bower_components/emby-webcomponents/backdrop/backdrop.js +++ b/src/bower_components/emby-webcomponents/backdrop/backdrop.js @@ -1,150 +1,358 @@ -define(["browser", "connectionManager", "playbackManager", "dom", "css!./style"], function(browser, connectionManager, playbackManager, dom) { - "use strict"; +define(['browser', 'connectionManager', 'playbackManager', 'dom', 'css!./style'], function (browser, connectionManager, playbackManager, dom) { + 'use strict'; function enableAnimation(elem) { - return !browser.slow + + if (browser.slow) { + return false; + } + + return true; } function enableRotation() { - return !browser.tv && !browser.firefox + + if (browser.tv) { + return false; + } + + // Causes high cpu usage + if (browser.firefox) { + return false; + } + + return true; } - function Backdrop() {} + function Backdrop() { + } + + Backdrop.prototype.load = function (url, parent, existingBackdropImage) { + + var img = new Image(); + + var self = this; + + img.onload = function () { + + if (self.isDestroyed) { + return; + } + + var backdropImage = document.createElement('div'); + backdropImage.classList.add('backdropImage'); + backdropImage.classList.add('displayingBackdropImage'); + backdropImage.style.backgroundImage = "url('" + url + "')"; + backdropImage.setAttribute('data-url', url); + + backdropImage.classList.add('backdropImageFadeIn'); + parent.appendChild(backdropImage); + + if (!enableAnimation(backdropImage)) { + if (existingBackdropImage && existingBackdropImage.parentNode) { + existingBackdropImage.parentNode.removeChild(existingBackdropImage); + } + internalBackdrop(true); + return; + } + + var onAnimationComplete = function () { + dom.removeEventListener(backdropImage, dom.whichAnimationEvent(), onAnimationComplete, { + once: true + }); + if (backdropImage === self.currentAnimatingElement) { + self.currentAnimatingElement = null; + } + if (existingBackdropImage && existingBackdropImage.parentNode) { + existingBackdropImage.parentNode.removeChild(existingBackdropImage); + } + }; + + dom.addEventListener(backdropImage, dom.whichAnimationEvent(), onAnimationComplete, { + once: true + }); + + internalBackdrop(true); + }; + img.src = url; + }; + + Backdrop.prototype.cancelAnimation = function () { + var elem = this.currentAnimatingElement; + if (elem) { + elem.classList.remove('backdropImageFadeIn'); + this.currentAnimatingElement = null; + } + }; + + Backdrop.prototype.destroy = function () { + + this.isDestroyed = true; + this.cancelAnimation(); + }; + + var backdropContainer; function getBackdropContainer() { - return backdropContainer || (backdropContainer = document.querySelector(".backdropContainer")), backdropContainer || (backdropContainer = document.createElement("div"), backdropContainer.classList.add("backdropContainer"), document.body.insertBefore(backdropContainer, document.body.firstChild)), backdropContainer + + if (!backdropContainer) { + backdropContainer = document.querySelector('.backdropContainer'); + } + + if (!backdropContainer) { + backdropContainer = document.createElement('div'); + backdropContainer.classList.add('backdropContainer'); + document.body.insertBefore(backdropContainer, document.body.firstChild); + } + + return backdropContainer; } function clearBackdrop(clearAll) { - clearRotation(), currentLoadingBackdrop && (currentLoadingBackdrop.destroy(), currentLoadingBackdrop = null), getBackdropContainer().innerHTML = "", clearAll && (hasExternalBackdrop = !1), internalBackdrop(!1) - } - function getBackgroundContainer() { - return backgroundContainer || (backgroundContainer = document.querySelector(".backgroundContainer")), backgroundContainer - } + clearRotation(); - function setBackgroundContainerBackgroundEnabled() { - hasInternalBackdrop || hasExternalBackdrop ? getBackgroundContainer().classList.add("withBackdrop") : getBackgroundContainer().classList.remove("withBackdrop") - } - - function internalBackdrop(enabled) { - hasInternalBackdrop = enabled, setBackgroundContainerBackgroundEnabled() - } - - function externalBackdrop(enabled) { - hasExternalBackdrop = enabled, setBackgroundContainerBackgroundEnabled() - } - - function setBackdropImage(url) { - currentLoadingBackdrop && (currentLoadingBackdrop.destroy(), currentLoadingBackdrop = null); - var elem = getBackdropContainer(), - existingBackdropImage = elem.querySelector(".displayingBackdropImage"); - if (existingBackdropImage && existingBackdropImage.getAttribute("data-url") === url) { - if (existingBackdropImage.getAttribute("data-url") === url) return; - existingBackdropImage.classList.remove("displayingBackdropImage") + if (currentLoadingBackdrop) { + currentLoadingBackdrop.destroy(); + currentLoadingBackdrop = null; } - var instance = new Backdrop; - instance.load(url, elem, existingBackdropImage), currentLoadingBackdrop = instance + + var elem = getBackdropContainer(); + elem.innerHTML = ''; + + if (clearAll) { + hasExternalBackdrop = false; + } + internalBackdrop(false); } + var backgroundContainer; + function getBackgroundContainer() { + if (!backgroundContainer) { + backgroundContainer = document.querySelector('.backgroundContainer'); + } + return backgroundContainer; + } + function setBackgroundContainerBackgroundEnabled() { + + if (hasInternalBackdrop || hasExternalBackdrop) { + getBackgroundContainer().classList.add('withBackdrop'); + } else { + getBackgroundContainer().classList.remove('withBackdrop'); + } + } + + var hasInternalBackdrop; + function internalBackdrop(enabled) { + hasInternalBackdrop = enabled; + setBackgroundContainerBackgroundEnabled(); + } + + var hasExternalBackdrop; + function externalBackdrop(enabled) { + hasExternalBackdrop = enabled; + setBackgroundContainerBackgroundEnabled(); + } + + function getRandom(min, max) { + return Math.floor(Math.random() * (max - min) + min); + } + + var currentLoadingBackdrop; + function setBackdropImage(url) { + + if (currentLoadingBackdrop) { + currentLoadingBackdrop.destroy(); + currentLoadingBackdrop = null; + } + + var elem = getBackdropContainer(); + var existingBackdropImage = elem.querySelector('.displayingBackdropImage'); + + if (existingBackdropImage && existingBackdropImage.getAttribute('data-url') === url) { + if (existingBackdropImage.getAttribute('data-url') === url) { + return; + } + existingBackdropImage.classList.remove('displayingBackdropImage'); + } + + var instance = new Backdrop(); + instance.load(url, elem, existingBackdropImage); + currentLoadingBackdrop = instance; + } + + var standardWidths = [480, 720, 1280, 1440, 1920]; function getBackdropMaxWidth() { + var width = dom.getWindowSize().innerWidth; - if (-1 !== standardWidths.indexOf(width)) return width; - return width = 100 * Math.floor(width / 100), Math.min(width, 1920) + + if (standardWidths.indexOf(width) !== -1) { + return width; + } + + var roundScreenTo = 100; + width = Math.floor(width / roundScreenTo) * roundScreenTo; + + return Math.min(width, 1920); } function getItemImageUrls(item, imageOptions) { + imageOptions = imageOptions || {}; + var apiClient = connectionManager.getApiClient(item.ServerId); - return item.BackdropImageTags && item.BackdropImageTags.length > 0 ? item.BackdropImageTags.map(function(imgTag, index) { - return apiClient.getScaledImageUrl(item.BackdropItemId || item.Id, Object.assign(imageOptions, { - type: "Backdrop", - tag: imgTag, - maxWidth: getBackdropMaxWidth(), - index: index - })) - }) : item.ParentBackdropItemId && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length ? item.ParentBackdropImageTags.map(function(imgTag, index) { - return apiClient.getScaledImageUrl(item.ParentBackdropItemId, Object.assign(imageOptions, { - type: "Backdrop", - tag: imgTag, - maxWidth: getBackdropMaxWidth(), - index: index - })) - }) : [] + + if (item.BackdropImageTags && item.BackdropImageTags.length > 0) { + + return item.BackdropImageTags.map(function (imgTag, index) { + + return apiClient.getScaledImageUrl(item.BackdropItemId || item.Id, Object.assign(imageOptions, { + type: "Backdrop", + tag: imgTag, + maxWidth: getBackdropMaxWidth(), + index: index + })); + }); + } + + if (item.ParentBackdropItemId && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length) { + + return item.ParentBackdropImageTags.map(function (imgTag, index) { + + return apiClient.getScaledImageUrl(item.ParentBackdropItemId, Object.assign(imageOptions, { + type: "Backdrop", + tag: imgTag, + maxWidth: getBackdropMaxWidth(), + index: index + })); + }); + } + + return []; } function getImageUrls(items, imageOptions) { - for (var list = [], onImg = function(img) { - list.push(img) - }, i = 0, length = items.length; i < length; i++) { - getItemImageUrls(items[i], imageOptions).forEach(onImg) + + var list = []; + + var onImg = function (img) { + list.push(img); + }; + + for (var i = 0, length = items.length; i < length; i++) { + + var itemImages = getItemImageUrls(items[i], imageOptions); + + itemImages.forEach(onImg); } - return list + + return list; } function arraysEqual(a, b) { - if (a === b) return !0; - if (null == a || null == b) return !1; - if (a.length !== b.length) return !1; - for (var i = 0; i < a.length; ++i) - if (a[i] !== b[i]) return !1; - return !0 + if (a === b) { + return true; + } + if (a == null || b == null) { + return false; + } + if (a.length !== b.length) { + return false; + } + + // If you don't care about the order of the elements inside + // the array, you should sort both arrays here. + + for (var i = 0; i < a.length; ++i) { + if (a[i] !== b[i]) { + return false; + } + } + return true; } + var rotationInterval; + var currentRotatingImages = []; + var currentRotationIndex = -1; function setBackdrops(items, imageOptions, enableImageRotation) { + var images = getImageUrls(items, imageOptions); - images.length ? startRotation(images, enableImageRotation) : clearBackdrop() + + if (images.length) { + + startRotation(images, enableImageRotation); + + } else { + clearBackdrop(); + } } function startRotation(images, enableImageRotation) { - arraysEqual(images, currentRotatingImages) || (clearRotation(), currentRotatingImages = images, currentRotationIndex = -1, images.length > 1 && !1 !== enableImageRotation && enableRotation() && (rotationInterval = setInterval(onRotationInterval, 24e3)), onRotationInterval()) + + if (arraysEqual(images, currentRotatingImages)) { + return; + } + + clearRotation(); + + currentRotatingImages = images; + currentRotationIndex = -1; + + if (images.length > 1 && enableImageRotation !== false && enableRotation()) { + rotationInterval = setInterval(onRotationInterval, 24000); + } + onRotationInterval(); } function onRotationInterval() { - if (!playbackManager.isPlayingLocally(["Video"])) { - var newIndex = currentRotationIndex + 1; - newIndex >= currentRotatingImages.length && (newIndex = 0), currentRotationIndex = newIndex, setBackdropImage(currentRotatingImages[newIndex]) + + if (playbackManager.isPlayingLocally(['Video'])) { + return; } + + var newIndex = currentRotationIndex + 1; + if (newIndex >= currentRotatingImages.length) { + newIndex = 0; + } + + currentRotationIndex = newIndex; + setBackdropImage(currentRotatingImages[newIndex]); } function clearRotation() { var interval = rotationInterval; - interval && clearInterval(interval), rotationInterval = null, currentRotatingImages = [], currentRotationIndex = -1 + if (interval) { + clearInterval(interval); + } + rotationInterval = null; + currentRotatingImages = []; + currentRotationIndex = -1; } function setBackdrop(url, imageOptions) { - url && "string" != typeof url && (url = getImageUrls([url], imageOptions)[0]), url ? (clearRotation(), setBackdropImage(url)) : clearBackdrop() - } - Backdrop.prototype.load = function(url, parent, existingBackdropImage) { - var img = new Image, - self = this; - img.onload = function() { - if (!self.isDestroyed) { - var backdropImage = document.createElement("div"); - if (backdropImage.classList.add("backdropImage"), backdropImage.classList.add("displayingBackdropImage"), backdropImage.style.backgroundImage = "url('" + url + "')", backdropImage.setAttribute("data-url", url), backdropImage.classList.add("backdropImageFadeIn"), parent.appendChild(backdropImage), !enableAnimation(backdropImage)) return existingBackdropImage && existingBackdropImage.parentNode && existingBackdropImage.parentNode.removeChild(existingBackdropImage), void internalBackdrop(!0); - var onAnimationComplete = function() { - dom.removeEventListener(backdropImage, dom.whichAnimationEvent(), onAnimationComplete, { - once: !0 - }), backdropImage === self.currentAnimatingElement && (self.currentAnimatingElement = null), existingBackdropImage && existingBackdropImage.parentNode && existingBackdropImage.parentNode.removeChild(existingBackdropImage) - }; - dom.addEventListener(backdropImage, dom.whichAnimationEvent(), onAnimationComplete, { - once: !0 - }), internalBackdrop(!0) + + if (url) { + if (typeof url !== 'string') { + url = getImageUrls([url], imageOptions)[0]; } - }, img.src = url - }, Backdrop.prototype.cancelAnimation = function() { - var elem = this.currentAnimatingElement; - elem && (elem.classList.remove("backdropImageFadeIn"), this.currentAnimatingElement = null) - }, Backdrop.prototype.destroy = function() { - this.isDestroyed = !0, this.cancelAnimation() - }; - var backdropContainer, backgroundContainer, hasInternalBackdrop, hasExternalBackdrop, currentLoadingBackdrop, rotationInterval, standardWidths = [480, 720, 1280, 1440, 1920], - currentRotatingImages = [], - currentRotationIndex = -1; + } + + if (url) { + clearRotation(); + + setBackdropImage(url); + + } else { + clearBackdrop(); + } + } + return { + setBackdrops: setBackdrops, setBackdrop: setBackdrop, clear: clearBackdrop, externalBackdrop: externalBackdrop - } + }; + }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/backdrop/style.css b/src/bower_components/emby-webcomponents/backdrop/style.css index b49ac84c53..743fe72df8 100644 --- a/src/bower_components/emby-webcomponents/backdrop/style.css +++ b/src/bower_components/emby-webcomponents/backdrop/style.css @@ -1,41 +1,29 @@ .backdropContainer { - contain: layout style size + contain: layout style size; } .backdropImage { background-repeat: no-repeat; background-position: center center; - -webkit-background-size: cover; background-size: cover; position: absolute; top: 0; left: 0; right: 0; bottom: 0; - contain: layout style + contain: layout style; } .backdropImageFadeIn { - -webkit-animation: backdrop-fadein .8s ease-in normal both; - animation: backdrop-fadein .8s ease-in normal both -} - -@-webkit-keyframes backdrop-fadein { - from { - opacity: 0 - } - - to { - opacity: 1 - } + animation: backdrop-fadein 800ms ease-in normal both; } @keyframes backdrop-fadein { from { - opacity: 0 + opacity: 0; } to { - opacity: 1 + opacity: 1; } -} \ No newline at end of file +} diff --git a/src/bower_components/emby-webcomponents/browser.js b/src/bower_components/emby-webcomponents/browser.js index c718dfb6d0..cace0cb891 100644 --- a/src/bower_components/emby-webcomponents/browser.js +++ b/src/bower_components/emby-webcomponents/browser.js @@ -1,69 +1,310 @@ -define([], function() { - "use strict"; +define([], function () { + 'use strict'; - function supportsCssAnimation(allowPrefix) { - if (allowPrefix) { - if (!0 === _supportsCssAnimationWithPrefix || !1 === _supportsCssAnimationWithPrefix) return _supportsCssAnimationWithPrefix - } else if (!0 === _supportsCssAnimation || !1 === _supportsCssAnimation) return _supportsCssAnimation; - var animation = !1, - domPrefixes = ["Webkit", "O", "Moz"], - pfx = "", - elm = document.createElement("div"); - if (void 0 !== elm.style.animationName && (animation = !0), !1 === animation && allowPrefix) - for (var i = 0; i < domPrefixes.length; i++) - if (void 0 !== elm.style[domPrefixes[i] + "AnimationName"]) { - pfx = domPrefixes[i], pfx + "Animation", "-" + pfx.toLowerCase() + "-", animation = !0; - break - } return allowPrefix ? _supportsCssAnimationWithPrefix = animation : _supportsCssAnimation = animation + function isTv() { + + // This is going to be really difficult to get right + var userAgent = navigator.userAgent.toLowerCase(); + + if (userAgent.indexOf('tv') !== -1) { + return true; + } + + if (userAgent.indexOf('samsungbrowser') !== -1) { + return true; + } + + if (userAgent.indexOf('nintendo') !== -1) { + return true; + } + + if (userAgent.indexOf('viera') !== -1) { + return true; + } + + if (userAgent.indexOf('webos') !== -1) { + return true; + } + + return false; } - var _supportsCssAnimation, _supportsCssAnimationWithPrefix, userAgent = navigator.userAgent, - matched = function(ua) { - ua = ua.toLowerCase(); - var match = /(edge)[ \/]([\w.]+)/.exec(ua) || /(opera)[ \/]([\w.]+)/.exec(ua) || /(opr)[ \/]([\w.]+)/.exec(ua) || /(chrome)[ \/]([\w.]+)/.exec(ua) || /(safari)[ \/]([\w.]+)/.exec(ua) || /(firefox)[ \/]([\w.]+)/.exec(ua) || /(msie) ([\w.]+)/.exec(ua) || ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || [], - versionMatch = /(version)[ \/]([\w.]+)/.exec(ua), - platform_match = /(ipad)/.exec(ua) || /(iphone)/.exec(ua) || /(windows)/.exec(ua) || /(android)/.exec(ua) || [], - browser = match[1] || ""; - "edge" === browser ? platform_match = [""] : -1 !== ua.indexOf("windows phone") || -1 !== ua.indexOf("iemobile") ? browser = "msie" : -1 !== ua.indexOf("like gecko") && -1 === ua.indexOf("webkit") && -1 === ua.indexOf("opera") && -1 === ua.indexOf("chrome") && -1 === ua.indexOf("safari") && (browser = "msie"), "opr" === browser && (browser = "opera"); - var version; - versionMatch && versionMatch.length > 2 && (version = versionMatch[2]), version = version || match[2] || "0"; - var versionMajor = parseInt(version.split(".")[0]); - return isNaN(versionMajor) && (versionMajor = 0), { - browser: browser, - version: version, - platform: platform_match[0] || "", - versionMajor: versionMajor + + function isMobile(userAgent) { + + var terms = [ + 'mobi', + 'ipad', + 'iphone', + 'ipod', + 'silk', + 'gt-p1000', + 'nexus 7', + 'kindle fire', + 'opera mini' + ]; + + var lower = userAgent.toLowerCase(); + + for (var i = 0, length = terms.length; i < length; i++) { + if (lower.indexOf(terms[i]) !== -1) { + return true; } - }(userAgent), - browser = {}; - return matched.browser && (browser[matched.browser] = !0, browser.version = matched.version, browser.versionMajor = matched.versionMajor), matched.platform && (browser[matched.platform] = !0), browser.chrome || browser.msie || browser.edge || browser.opera || -1 === userAgent.toLowerCase().indexOf("webkit") || (browser.safari = !0), -1 !== userAgent.toLowerCase().indexOf("playstation 4") && (browser.ps4 = !0, browser.tv = !0), - function(userAgent) { - for (var terms = ["mobi", "ipad", "iphone", "ipod", "silk", "gt-p1000", "nexus 7", "kindle fire", "opera mini"], lower = userAgent.toLowerCase(), i = 0, length = terms.length; i < length; i++) - if (-1 !== lower.indexOf(terms[i])) return !0; - return !1 - }(userAgent) && (browser.mobile = !0), browser.xboxOne = -1 !== userAgent.toLowerCase().indexOf("xbox"), browser.animate = "undefined" != typeof document && null != document.documentElement.animate, browser.tizen = -1 !== userAgent.toLowerCase().indexOf("tizen") || null != self.tizen, browser.web0s = -1 !== userAgent.toLowerCase().indexOf("Web0S".toLowerCase()), browser.edgeUwp = browser.edge && (-1 !== userAgent.toLowerCase().indexOf("msapphost") || -1 !== userAgent.toLowerCase().indexOf("webview")), browser.tizen || (browser.orsay = -1 !== userAgent.toLowerCase().indexOf("smarthub")), browser.edgeUwp && (browser.edge = !0), browser.tv = function() { - var userAgent = navigator.userAgent.toLowerCase(); - return -1 !== userAgent.indexOf("tv") || (-1 !== userAgent.indexOf("samsungbrowser") || (-1 !== userAgent.indexOf("nintendo") || (-1 !== userAgent.indexOf("viera") || -1 !== userAgent.indexOf("webos")))) - }(), browser.operaTv = browser.tv && -1 !== userAgent.toLowerCase().indexOf("opr/"), - function(prop, value) { - if ("undefined" == typeof window) return !1; - if (value = 2 === arguments.length ? value : "inherit", "CSS" in window && "supports" in window.CSS) return window.CSS.supports(prop, value); - if ("supportsCSS" in window) return window.supportsCSS(prop, value); - try { - var camel = prop.replace(/-([a-z]|[0-9])/gi, function(all, letter) { - return (letter + "").toUpperCase() - }), - support = camel in el.style, - el = document.createElement("div"); - return el.style.cssText = prop + ":" + value, support && "" !== el.style[camel] - } catch (err) { - return !1 + } + + return false; + } + + function isStyleSupported(prop, value) { + + if (typeof window === 'undefined') { + return false; + } + + // If no value is supplied, use "inherit" + value = arguments.length === 2 ? value : 'inherit'; + // Try the native standard method first + if ('CSS' in window && 'supports' in window.CSS) { + return window.CSS.supports(prop, value); + } + // Check Opera's native method + if ('supportsCSS' in window) { + return window.supportsCSS(prop, value); + } + + // need try/catch because it's failing on tizen + + try { + // Convert to camel-case for DOM interactions + var camel = prop.replace(/-([a-z]|[0-9])/ig, function (all, letter) { + return (letter + '').toUpperCase(); + }); + // Check if the property is supported + var support = (camel in el.style); + // Create test element + var el = document.createElement('div'); + // Assign the property and value to invoke + // the CSS interpreter + el.style.cssText = prop + ':' + value; + // Ensure both the property and value are + // supported and return + return support && (el.style[camel] !== ''); + } catch (err) { + return false; + } + } + + function hasKeyboard(browser) { + + if (browser.touch) { + return true; + } + + if (browser.xboxOne) { + return true; + } + + if (browser.ps4) { + return true; + } + + if (browser.edgeUwp) { + // This is OK for now, but this won't always be true + // Should we use this? + // https://gist.github.com/wagonli/40d8a31bd0d6f0dd7a5d + return true; + } + + if (browser.tv) { + return true; + } + + return false; + } + + function iOSversion() { + if (/iP(hone|od|ad)/.test(navigator.platform)) { + // supports iOS 2.0 and later: + var v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/); + return [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || 0, 10)]; + } + } + + var _supportsCssAnimation; + var _supportsCssAnimationWithPrefix; + function supportsCssAnimation(allowPrefix) { + + if (allowPrefix) { + if (_supportsCssAnimationWithPrefix === true || _supportsCssAnimationWithPrefix === false) { + return _supportsCssAnimationWithPrefix; } - }("display", "flex") || (browser.noFlex = !0), (browser.mobile || browser.tv) && (browser.slow = !0), "undefined" != typeof document && ("ontouchstart" in window || window.DocumentTouch && document instanceof DocumentTouch) && (browser.touch = !0), browser.keyboard = function(browser) { - return !!browser.touch || (!!browser.xboxOne || (!!browser.ps4 || (!!browser.edgeUwp || !!browser.tv))) - }(browser), browser.supportsCssAnimation = supportsCssAnimation, browser.osx = -1 !== userAgent.toLowerCase().indexOf("os x"), browser.iOS = browser.ipad || browser.iphone || browser.ipod, browser.iOS && (browser.iOSVersion = function() { - if (/iP(hone|od|ad)/.test(navigator.platform)) { - var v = navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/); - return [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || 0, 10)] + } else { + if (_supportsCssAnimation === true || _supportsCssAnimation === false) { + return _supportsCssAnimation; } - }(), browser.iOSVersion = browser.iOSVersion[0] + browser.iOSVersion[1] / 10), browser.chromecast = browser.chrome && -1 !== userAgent.toLowerCase().indexOf("crkey"), browser + } + + var animation = false, + animationstring = 'animation', + keyframeprefix = '', + domPrefixes = ['Webkit', 'O', 'Moz'], + pfx = '', + elm = document.createElement('div'); + + if (elm.style.animationName !== undefined) { animation = true; } + + if (animation === false && allowPrefix) { + for (var i = 0; i < domPrefixes.length; i++) { + if (elm.style[domPrefixes[i] + 'AnimationName'] !== undefined) { + pfx = domPrefixes[i]; + animationstring = pfx + 'Animation'; + keyframeprefix = '-' + pfx.toLowerCase() + '-'; + animation = true; + break; + } + } + } + + if (allowPrefix) { + _supportsCssAnimationWithPrefix = animation; + return _supportsCssAnimationWithPrefix; + } else { + _supportsCssAnimation = animation; + return _supportsCssAnimation; + } + } + + var uaMatch = function (ua) { + ua = ua.toLowerCase(); + + var match = /(edge)[ \/]([\w.]+)/.exec(ua) || + /(opera)[ \/]([\w.]+)/.exec(ua) || + /(opr)[ \/]([\w.]+)/.exec(ua) || + /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(safari)[ \/]([\w.]+)/.exec(ua) || + /(firefox)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + var versionMatch = /(version)[ \/]([\w.]+)/.exec(ua); + + var platform_match = /(ipad)/.exec(ua) || + /(iphone)/.exec(ua) || + /(windows)/.exec(ua) || + /(android)/.exec(ua) || + []; + + var browser = match[1] || ""; + + if (browser === "edge") { + platform_match = [""]; + } else { + if (ua.indexOf("windows phone") !== -1 || ua.indexOf("iemobile") !== -1) { + + // http://www.neowin.net/news/ie11-fakes-user-agent-to-fool-gmail-in-windows-phone-81-gdr1-update + browser = "msie"; + } + else if (ua.indexOf("like gecko") !== -1 && ua.indexOf('webkit') === -1 && ua.indexOf('opera') === -1 && ua.indexOf('chrome') === -1 && ua.indexOf('safari') === -1) { + browser = "msie"; + } + } + + if (browser === 'opr') { + browser = 'opera'; + } + + var version; + if (versionMatch && versionMatch.length > 2) { + version = versionMatch[2]; + } + + version = version || match[2] || "0"; + + var versionMajor = parseInt(version.split('.')[0]); + + if (isNaN(versionMajor)) { + versionMajor = 0; + } + + return { + browser: browser, + version: version, + platform: platform_match[0] || "", + versionMajor: versionMajor + }; + }; + + var userAgent = navigator.userAgent; + + var matched = uaMatch(userAgent); + var browser = {}; + + if (matched.browser) { + browser[matched.browser] = true; + browser.version = matched.version; + browser.versionMajor = matched.versionMajor; + } + + if (matched.platform) { + browser[matched.platform] = true; + } + + if (!browser.chrome && !browser.msie && !browser.edge && !browser.opera && userAgent.toLowerCase().indexOf("webkit") !== -1) { + browser.safari = true; + } + + if (userAgent.toLowerCase().indexOf("playstation 4") !== -1) { + browser.ps4 = true; + browser.tv = true; + } + + if (isMobile(userAgent)) { + browser.mobile = true; + } + + browser.xboxOne = userAgent.toLowerCase().indexOf('xbox') !== -1; + browser.animate = typeof document !== 'undefined' && document.documentElement.animate != null; + browser.tizen = userAgent.toLowerCase().indexOf('tizen') !== -1 || self.tizen != null; + browser.web0s = userAgent.toLowerCase().indexOf('Web0S'.toLowerCase()) !== -1; + browser.edgeUwp = browser.edge && (userAgent.toLowerCase().indexOf('msapphost') !== -1 || userAgent.toLowerCase().indexOf('webview') !== -1); + + if (!browser.tizen) { + browser.orsay = userAgent.toLowerCase().indexOf('smarthub') !== -1; + } + + if (browser.edgeUwp) { + browser.edge = true; + } + + browser.tv = isTv(); + browser.operaTv = browser.tv && userAgent.toLowerCase().indexOf('opr/') !== -1; + + if (!isStyleSupported('display', 'flex')) { + browser.noFlex = true; + } + + if (browser.mobile || browser.tv) { + browser.slow = true; + } + + if (typeof document !== 'undefined') { + if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) { + browser.touch = true; + } + } + + browser.keyboard = hasKeyboard(browser); + browser.supportsCssAnimation = supportsCssAnimation; + + browser.osx = userAgent.toLowerCase().indexOf('os x') !== -1; + browser.iOS = browser.ipad || browser.iphone || browser.ipod; + + if (browser.iOS) { + browser.iOSVersion = iOSversion(); + browser.iOSVersion = browser.iOSVersion[0] + (browser.iOSVersion[1] / 10); + } + + browser.chromecast = browser.chrome && userAgent.toLowerCase().indexOf('crkey') !== -1; + + return browser; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/browserdeviceprofile.js b/src/bower_components/emby-webcomponents/browserdeviceprofile.js index f4178a750a..7b37a0cdb1 100644 --- a/src/bower_components/emby-webcomponents/browserdeviceprofile.js +++ b/src/bower_components/emby-webcomponents/browserdeviceprofile.js @@ -1,384 +1,916 @@ -define(["browser"], function(browser) { - "use strict"; +define(['browser'], function (browser) { + 'use strict'; function canPlayH264(videoTestElement) { - return !(!videoTestElement.canPlayType || !videoTestElement.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"').replace(/no/, "")) + return !!(videoTestElement.canPlayType && videoTestElement.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"').replace(/no/, '')); } function canPlayH265(videoTestElement, options) { - if (browser.tizen || browser.orsay || browser.xboxOne || browser.web0s || options.supportsHevc) return !0; - var userAgent = navigator.userAgent.toLowerCase(); - if (browser.chromecast) { - if (-1 !== userAgent.indexOf("aarch64")) return !0 + + if (browser.tizen || browser.orsay || browser.xboxOne || browser.web0s || options.supportsHevc) { + return true; } - return !!(browser.iOS && (browser.iOSVersion || 0) >= 11) || !(!videoTestElement.canPlayType || !videoTestElement.canPlayType('video/hevc; codecs="hevc, aac"').replace(/no/, "")) + + var userAgent = navigator.userAgent.toLowerCase(); + + if (browser.chromecast) { + + var isChromecastUltra = userAgent.indexOf('aarch64') !== -1; + if (isChromecastUltra) { + return true; + } + } + + // Unfortunately haven't yet found a canPlayType for proper detection + if (browser.iOS && (browser.iOSVersion || 0) >= 11) { + return true; + } + + return !!(videoTestElement.canPlayType && videoTestElement.canPlayType('video/hevc; codecs="hevc, aac"').replace(/no/, '')); } + var _supportsTextTracks; function supportsTextTracks() { - return !(!browser.tizen && !browser.orsay) || (null == _supportsTextTracks && (_supportsTextTracks = null != document.createElement("video").textTracks), _supportsTextTracks) + + if (browser.tizen || browser.orsay) { + return true; + } + + if (_supportsTextTracks == null) { + _supportsTextTracks = document.createElement('video').textTracks != null; + } + + // For now, until ready + return _supportsTextTracks; } + var _canPlayHls; function canPlayHls(src) { - return null == _canPlayHls && (_canPlayHls = canPlayNativeHls() || canPlayHlsWithMSE()), _canPlayHls + + if (_canPlayHls == null) { + _canPlayHls = canPlayNativeHls() || canPlayHlsWithMSE(); + } + return _canPlayHls; } function canPlayNativeHls() { - if (browser.tizen || browser.orsay) return !0; - var media = document.createElement("video"); - return !(!media.canPlayType("application/x-mpegURL").replace(/no/, "") && !media.canPlayType("application/vnd.apple.mpegURL").replace(/no/, "")) + + if (browser.tizen || browser.orsay) { + return true; + } + + var media = document.createElement('video'); + if (media.canPlayType('application/x-mpegURL').replace(/no/, '') || + media.canPlayType('application/vnd.apple.mpegURL').replace(/no/, '')) { + return true; + } + + return false; } function canPlayHlsWithMSE() { - return null != window.MediaSource + if (window.MediaSource != null) { + // text tracks don’t work with this in firefox + return true; + } + + return false; } function canPlayAudioFormat(format) { + var typeString; - if ("flac" === format) { - if (browser.tizen || browser.orsay || browser.web0s) return !0; - if (browser.edgeUwp) return !0 - } else if ("wma" === format) { - if (browser.tizen || browser.orsay) return !0; - if (browser.edgeUwp) return !0 - } else { - if ("opus" === format) return typeString = 'audio/ogg; codecs="opus"', !!document.createElement("audio").canPlayType(typeString).replace(/no/, ""); - if ("mp2" === format) return !1 + + if (format === 'flac') { + if (browser.tizen || browser.orsay || browser.web0s) { + return true; + } + if (browser.edgeUwp) { + return true; + } } - if ("webma" === format) typeString = "audio/webm"; - else if ("mp2" === format) typeString = "audio/mpeg"; - else if ("ogg" === format || "oga" === format) { - if (browser.chrome) return !1; - typeString = "audio/" + format - } else typeString = "audio/" + format; - return !!document.createElement("audio").canPlayType(typeString).replace(/no/, "") + + else if (format === 'wma') { + if (browser.tizen || browser.orsay) { + return true; + } + if (browser.edgeUwp) { + return true; + } + } + + else if (format === 'opus') { + typeString = 'audio/ogg; codecs="opus"'; + + if (document.createElement('audio').canPlayType(typeString).replace(/no/, '')) { + return true; + } + + return false; + } + + else if (format === 'mp2') { + + // For now + return false; + } + + if (format === 'webma') { + typeString = 'audio/webm'; + } else if (format === 'mp2') { + typeString = 'audio/mpeg'; + } else if (format === 'ogg' || format === 'oga') { + + // chrome says probably, but seeing failures + if (browser.chrome) { + return false; + } + typeString = 'audio/' + format; + + } else { + typeString = 'audio/' + format; + } + + if (document.createElement('audio').canPlayType(typeString).replace(/no/, '')) { + return true; + } + + return false; } function testCanPlayMkv(videoTestElement) { - if (browser.tizen || browser.orsay || browser.web0s) return !0; - if (videoTestElement.canPlayType("video/x-matroska").replace(/no/, "") || videoTestElement.canPlayType("video/mkv").replace(/no/, "")) return !0; + + if (browser.tizen || browser.orsay || browser.web0s) { + return true; + } + + if (videoTestElement.canPlayType('video/x-matroska').replace(/no/, '') || + videoTestElement.canPlayType('video/mkv').replace(/no/, '')) { + return true; + } + var userAgent = navigator.userAgent.toLowerCase(); - return browser.chrome ? !browser.operaTv && (-1 === userAgent.indexOf("vivaldi") && -1 === userAgent.indexOf("opera")) : !!browser.edgeUwp + + // Unfortunately there's no real way to detect mkv support + if (browser.chrome) { + + // Not supported on opera tv + if (browser.operaTv) { + return false; + } + + // Filter out browsers based on chromium that don't support mkv + if (userAgent.indexOf('vivaldi') !== -1 || userAgent.indexOf('opera') !== -1) { + return false; + } + + return true; + } + + if (browser.edgeUwp) { + + return true; + } + + return false; } function testCanPlayTs() { - return browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp + return browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp; } function supportsMpeg2Video() { - return browser.orsay || browser.tizen || browser.edgeUwp || browser.web0s + return browser.orsay || browser.tizen || browser.edgeUwp || browser.web0s; } function supportsVc1() { - return browser.orsay || browser.tizen || browser.edgeUwp || browser.web0s + return browser.orsay || browser.tizen || browser.edgeUwp || browser.web0s; + } + + function getFlvMseDirectPlayProfile() { + + var videoAudioCodecs = ['aac']; + + if (!browser.edge && !browser.msie) { + videoAudioCodecs.push('mp3'); + } + + return { + Container: 'flv', + Type: 'Video', + VideoCodec: 'h264', + AudioCodec: videoAudioCodecs.join(',') + }; } function getDirectPlayProfileForVideoContainer(container, videoAudioCodecs, videoTestElement, options) { - var supported = !1, - profileContainer = container, - videoCodecs = []; + + var supported = false; + var profileContainer = container; + var videoCodecs = []; + switch (container) { - case "asf": - supported = browser.tizen || browser.orsay || browser.edgeUwp, videoAudioCodecs = []; + + case 'asf': + supported = browser.tizen || browser.orsay || browser.edgeUwp; + videoAudioCodecs = []; break; - case "avi": + case 'avi': supported = browser.tizen || browser.orsay || browser.edgeUwp; break; - case "mpg": - case "mpeg": + case 'mpg': + case 'mpeg': supported = browser.edgeUwp || browser.tizen || browser.orsay; break; - case "flv": + case 'flv': + supported = browser.tizen || browser.orsay; + //if (!supported && window.MediaSource != null && window.MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"')) { + // return getFlvMseDirectPlayProfile(); + //} + break; + case '3gp': + case 'mts': + case 'trp': + case 'vob': + case 'vro': supported = browser.tizen || browser.orsay; break; - case "3gp": - case "mts": - case "trp": - case "vob": - case "vro": - supported = browser.tizen || browser.orsay; + case 'mov': + supported = browser.tizen || browser.orsay || browser.chrome || browser.edgeUwp; + videoCodecs.push('h264'); break; - case "mov": - supported = browser.tizen || browser.orsay || browser.chrome || browser.edgeUwp, videoCodecs.push("h264"); + case 'm2ts': + supported = browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp; + videoCodecs.push('h264'); + if (supportsVc1()) { + videoCodecs.push('vc1'); + } + if (supportsMpeg2Video()) { + videoCodecs.push('mpeg2video'); + } break; - case "m2ts": - supported = browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp, videoCodecs.push("h264"), supportsVc1() && videoCodecs.push("vc1"), supportsMpeg2Video() && videoCodecs.push("mpeg2video"); + case 'wmv': + supported = browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp; + videoAudioCodecs = []; break; - case "wmv": - supported = browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp, videoAudioCodecs = []; + case 'ts': + supported = testCanPlayTs(); + videoCodecs.push('h264'); + if (canPlayH265(videoTestElement, options)) { + videoCodecs.push('h265'); + videoCodecs.push('hevc'); + } + if (supportsVc1()) { + videoCodecs.push('vc1'); + } + if (supportsMpeg2Video()) { + videoCodecs.push('mpeg2video'); + } + profileContainer = 'ts,mpegts'; + break; + default: break; - case "ts": - supported = testCanPlayTs(), videoCodecs.push("h264"), canPlayH265(videoTestElement, options) && (videoCodecs.push("h265"), videoCodecs.push("hevc")), supportsVc1() && videoCodecs.push("vc1"), supportsMpeg2Video() && videoCodecs.push("mpeg2video"), profileContainer = "ts,mpegts" } - return supported ? { + + if (!supported) { + return null; + } + + return { Container: profileContainer, - Type: "Video", - VideoCodec: videoCodecs.join(","), - AudioCodec: videoAudioCodecs.join(",") - } : null + Type: 'Video', + VideoCodec: videoCodecs.join(','), + AudioCodec: videoAudioCodecs.join(',') + }; + } + + function getMaxBitrate() { + + return 120000000; } function getGlobalMaxVideoBitrate() { + var userAgent = navigator.userAgent.toLowerCase(); + if (browser.chromecast) { - return -1 !== userAgent.indexOf("aarch64") ? null : self.screen && self.screen.width >= 3800 ? null : 3e7 + + var isChromecastUltra = userAgent.indexOf('aarch64') !== -1; + if (isChromecastUltra) { + return null; + } + + // This is a hack to try and detect chromecast on vizio + if (self.screen && self.screen.width >= 3800) { + return null; + } + + return 30000000; } - var isTizenFhd = !1; - if (browser.tizen) try { - isTizenFhd = !webapis.productinfo.isUdPanelSupported(), console.log("isTizenFhd = " + isTizenFhd) - } catch (error) { - console.log("isUdPanelSupported() error code = " + error.code) + + var isTizenFhd = false; + if (browser.tizen) { + try { + var isTizenUhd = webapis.productinfo.isUdPanelSupported(); + isTizenFhd = !isTizenUhd; + console.log("isTizenFhd = " + isTizenFhd); + } catch (error) { + console.log("isUdPanelSupported() error code = " + error.code); + } } - return browser.ps4 ? 8e6 : browser.xboxOne ? 12e6 : browser.edgeUwp ? null : browser.tizen && isTizenFhd ? 2e7 : null + + return browser.ps4 ? 8000000 : + (browser.xboxOne ? 12000000 : + (browser.edgeUwp ? null : + (browser.tizen && isTizenFhd ? 20000000 : null))); } function supportsAc3(videoTestElement) { - return !!(browser.edgeUwp || browser.tizen || browser.orsay || browser.web0s) || videoTestElement.canPlayType('audio/mp4; codecs="ac-3"').replace(/no/, "") && !browser.osx && !browser.iOS + + if (browser.edgeUwp || browser.tizen || browser.orsay || browser.web0s) { + return true; + } + + return (videoTestElement.canPlayType('audio/mp4; codecs="ac-3"').replace(/no/, '') && !browser.osx && !browser.iOS); } function supportsEac3(videoTestElement) { - return !!(browser.tizen || browser.orsay || browser.web0s) || videoTestElement.canPlayType('audio/mp4; codecs="ec-3"').replace(/no/, "") + + if (browser.tizen || browser.orsay || browser.web0s) { + return true; + } + + return videoTestElement.canPlayType('audio/mp4; codecs="ec-3"').replace(/no/, ''); } - var _supportsTextTracks, _canPlayHls; - return function(options) { + + return function (options) { + options = options || {}; - var physicalAudioChannels = options.audioChannels || (browser.tv || browser.ps4 || browser.xboxOne ? 6 : 2), - videoTestElement = document.createElement("video"), - canPlayVp8 = videoTestElement.canPlayType('video/webm; codecs="vp8"').replace(/no/, ""), - canPlayVp9 = videoTestElement.canPlayType('video/webm; codecs="vp9"').replace(/no/, ""), - webmAudioCodecs = ["vorbis"], - canPlayMkv = testCanPlayMkv(videoTestElement), - profile = {}; - profile.MaxStreamingBitrate = 12e7, profile.MaxStaticBitrate = 1e8, profile.MusicStreamingTranscodingBitrate = Math.min(12e7, 192e3), profile.DirectPlayProfiles = []; - var videoAudioCodecs = [], - hlsVideoAudioCodecs = [], - supportsMp3VideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.69"').replace(/no/, "") || videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.6B"').replace(/no/, ""), - supportsMp2VideoAudio = browser.edgeUwp || browser.tizen || browser.orsay || browser.web0s, - maxVideoWidth = browser.xboxOne && self.screen ? self.screen.width : null; - options.maxVideoWidth && (maxVideoWidth = options.maxVideoWidth); - var canPlayAacVideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.2"').replace(/no/, ""); - if (canPlayAacVideoAudio && browser.chromecast && physicalAudioChannels <= 2 && videoAudioCodecs.push("aac"), supportsAc3(videoTestElement)) { - videoAudioCodecs.push("ac3"); + var physicalAudioChannels = options.audioChannels || (browser.tv || browser.ps4 || browser.xboxOne ? 6 : 2); + + var bitrateSetting = getMaxBitrate(); + + var videoTestElement = document.createElement('video'); + + var canPlayVp8 = videoTestElement.canPlayType('video/webm; codecs="vp8"').replace(/no/, ''); + var canPlayVp9 = videoTestElement.canPlayType('video/webm; codecs="vp9"').replace(/no/, ''); + var webmAudioCodecs = ['vorbis']; + + var canPlayMkv = testCanPlayMkv(videoTestElement); + + var profile = {}; + + profile.MaxStreamingBitrate = bitrateSetting; + profile.MaxStaticBitrate = 100000000; + profile.MusicStreamingTranscodingBitrate = Math.min(bitrateSetting, 192000); + + profile.DirectPlayProfiles = []; + + var videoAudioCodecs = []; + var hlsVideoAudioCodecs = []; + + var supportsMp3VideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.69"').replace(/no/, '') || + videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.6B"').replace(/no/, ''); + + // Not sure how to test for this + var supportsMp2VideoAudio = browser.edgeUwp || browser.tizen || browser.orsay || browser.web0s; + + var maxVideoWidth = browser.xboxOne ? + (self.screen ? self.screen.width : null) : + null; + + if (options.maxVideoWidth) { + maxVideoWidth = options.maxVideoWidth; + } + + var canPlayAacVideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.2"').replace(/no/, ''); + + if (canPlayAacVideoAudio && browser.chromecast && physicalAudioChannels <= 2) { + // prioritize this first + videoAudioCodecs.push('aac'); + } + + // Only put mp3 first if mkv support is there + // Otherwise with HLS and mp3 audio we're seeing some browsers + // safari is lying + if (supportsAc3(videoTestElement)) { + + videoAudioCodecs.push('ac3'); + var eAc3 = supportsEac3(videoTestElement); - eAc3 && videoAudioCodecs.push("eac3"); - (!browser.edge || !browser.touch || browser.edgeUwp) && (hlsVideoAudioCodecs.push("ac3"), eAc3 && hlsVideoAudioCodecs.push("eac3")) + if (eAc3) { + videoAudioCodecs.push('eac3'); + } + + // This works in edge desktop, but not mobile + // TODO: Retest this on mobile + var supportsAc3InHls = (!browser.edge || !browser.touch || browser.edgeUwp); + if (supportsAc3InHls) { + hlsVideoAudioCodecs.push('ac3'); + if (eAc3) { + hlsVideoAudioCodecs.push('eac3'); + } + } } - canPlayAacVideoAudio && browser.chromecast && -1 === videoAudioCodecs.indexOf("aac") && videoAudioCodecs.push("aac"), supportsMp3VideoAudio && (videoAudioCodecs.push("mp3"), browser.ps4 || physicalAudioChannels <= 2 && hlsVideoAudioCodecs.push("mp3")), canPlayAacVideoAudio && (-1 === videoAudioCodecs.indexOf("aac") && videoAudioCodecs.push("aac"), hlsVideoAudioCodecs.push("aac")), supportsMp3VideoAudio && (browser.ps4 || -1 === hlsVideoAudioCodecs.indexOf("mp3") && hlsVideoAudioCodecs.push("mp3")), supportsMp2VideoAudio && videoAudioCodecs.push("mp2"); + + if (canPlayAacVideoAudio && browser.chromecast && videoAudioCodecs.indexOf('aac') === -1) { + // prioritize this first + videoAudioCodecs.push('aac'); + } + + if (supportsMp3VideoAudio) { + videoAudioCodecs.push('mp3'); + + // PS4 fails to load HLS with mp3 audio + if (!browser.ps4) { + + // mp3 encoder only supports 2 channels, so only make that preferred if we're only requesting 2 channels + // Also apply it for chromecast because it no longer supports AAC 5.1 + if (physicalAudioChannels <= 2) { + hlsVideoAudioCodecs.push('mp3'); + } + } + } + if (canPlayAacVideoAudio) { + + if (videoAudioCodecs.indexOf('aac') === -1) { + videoAudioCodecs.push('aac'); + } + + hlsVideoAudioCodecs.push('aac'); + } + if (supportsMp3VideoAudio) { + // PS4 fails to load HLS with mp3 audio + if (!browser.ps4) { + if (hlsVideoAudioCodecs.indexOf('mp3') === -1) { + hlsVideoAudioCodecs.push('mp3'); + } + } + } + + if (supportsMp2VideoAudio) { + videoAudioCodecs.push('mp2'); + } + var supportsDts = browser.tizen || browser.orsay || browser.web0s || options.supportsDts; + if (self.tizen && self.tizen.systeminfo) { - var v = tizen.systeminfo.getCapability("http://tizen.org/feature/platform.version"); - v && parseFloat(v) >= parseFloat("4.0") && (supportsDts = !1) + var v = tizen.systeminfo.getCapability('http://tizen.org/feature/platform.version'); + + // DTS audio not supported in 2018 models (Tizen 4.0) + if (v && parseFloat(v) >= parseFloat('4.0')) { + supportsDts = false; + } } - supportsDts && (videoAudioCodecs.push("dca"), videoAudioCodecs.push("dts")), (browser.tizen || browser.orsay || browser.web0s) && (videoAudioCodecs.push("pcm_s16le"), videoAudioCodecs.push("pcm_s24le")), options.supportsTrueHd && videoAudioCodecs.push("truehd"), (browser.tizen || browser.orsay) && videoAudioCodecs.push("aac_latm"), canPlayAudioFormat("opus") && (videoAudioCodecs.push("opus"), hlsVideoAudioCodecs.push("opus"), webmAudioCodecs.push("opus")), canPlayAudioFormat("flac") && videoAudioCodecs.push("flac"), videoAudioCodecs = videoAudioCodecs.filter(function(c) { - return -1 === (options.disableVideoAudioCodecs || []).indexOf(c) - }), hlsVideoAudioCodecs = hlsVideoAudioCodecs.filter(function(c) { - return -1 === (options.disableHlsVideoAudioCodecs || []).indexOf(c) + + if (supportsDts) { + videoAudioCodecs.push('dca'); + videoAudioCodecs.push('dts'); + } + + if (browser.tizen || browser.orsay || browser.web0s) { + videoAudioCodecs.push('pcm_s16le'); + videoAudioCodecs.push('pcm_s24le'); + } + + if (options.supportsTrueHd) { + videoAudioCodecs.push('truehd'); + } + + if (browser.tizen || browser.orsay) { + videoAudioCodecs.push('aac_latm'); + } + + if (canPlayAudioFormat('opus')) { + videoAudioCodecs.push('opus'); + hlsVideoAudioCodecs.push('opus'); + webmAudioCodecs.push('opus'); + } + + if (canPlayAudioFormat('flac')) { + videoAudioCodecs.push('flac'); + } + + videoAudioCodecs = videoAudioCodecs.filter(function (c) { + return (options.disableVideoAudioCodecs || []).indexOf(c) === -1; }); - var mp4VideoCodecs = [], - hlsVideoCodecs = []; - canPlayH264(videoTestElement) && (mp4VideoCodecs.push("h264"), hlsVideoCodecs.push("h264")), canPlayH265(videoTestElement, options) && (mp4VideoCodecs.push("h265"), mp4VideoCodecs.push("hevc"), (browser.tizen || browser.web0s) && (hlsVideoCodecs.push("h265"), hlsVideoCodecs.push("hevc"))), supportsMpeg2Video() && mp4VideoCodecs.push("mpeg2video"), supportsVc1() && mp4VideoCodecs.push("vc1"), (browser.tizen || browser.orsay) && mp4VideoCodecs.push("msmpeg4v2"), canPlayVp8 && mp4VideoCodecs.push("vp8"), canPlayVp9 && mp4VideoCodecs.push("vp9"), (canPlayVp8 || browser.tizen || browser.orsay) && videoAudioCodecs.push("vorbis"), mp4VideoCodecs.length && profile.DirectPlayProfiles.push({ - Container: "mp4,m4v", - Type: "Video", - VideoCodec: mp4VideoCodecs.join(","), - AudioCodec: videoAudioCodecs.join(",") - }), canPlayMkv && mp4VideoCodecs.length && profile.DirectPlayProfiles.push({ - Container: "mkv", - Type: "Video", - VideoCodec: mp4VideoCodecs.join(","), - AudioCodec: videoAudioCodecs.join(",") - }), ["m2ts", "wmv", "ts", "asf", "avi", "mpg", "mpeg", "flv", "3gp", "mts", "trp", "vob", "vro", "mov"].map(function(container) { - return getDirectPlayProfileForVideoContainer(container, videoAudioCodecs, videoTestElement, options) - }).filter(function(i) { - return null != i - }).forEach(function(i) { - profile.DirectPlayProfiles.push(i) - }), ["opus", "mp3", "mp2", "aac", "flac", "alac", "webma", "wma", "wav", "ogg", "oga"].filter(canPlayAudioFormat).forEach(function(audioFormat) { - "mp2" === audioFormat ? profile.DirectPlayProfiles.push({ - Container: "mp2,mp3", - Type: "Audio", - AudioCodec: audioFormat - }) : "mp3" === audioFormat ? profile.DirectPlayProfiles.push({ - Container: audioFormat, - Type: "Audio", - AudioCodec: audioFormat - }) : profile.DirectPlayProfiles.push({ - Container: "webma" === audioFormat ? "webma,webm" : audioFormat, - Type: "Audio" - }), "aac" !== audioFormat && "alac" !== audioFormat || profile.DirectPlayProfiles.push({ - Container: "m4a", - AudioCodec: audioFormat, - Type: "Audio" - }) - }), canPlayVp8 && profile.DirectPlayProfiles.push({ - Container: "webm", - Type: "Video", - AudioCodec: webmAudioCodecs.join(","), - VideoCodec: "VP8" - }), canPlayVp9 && profile.DirectPlayProfiles.push({ - Container: "webm", - Type: "Video", - AudioCodec: webmAudioCodecs.join(","), - VideoCodec: "VP9" - }), profile.TranscodingProfiles = []; - var hlsBreakOnNonKeyFrames = !(!(browser.iOS || browser.osx || browser.edge) && canPlayNativeHls()); - canPlayHls() && !1 !== browser.enableHlsAudio && profile.TranscodingProfiles.push({ - Container: !canPlayNativeHls() || browser.edge || browser.android ? "ts" : "aac", - Type: "Audio", - AudioCodec: "aac", - Context: "Streaming", - Protocol: "hls", - MaxAudioChannels: physicalAudioChannels.toString(), - MinSegments: browser.iOS || browser.osx ? "2" : "1", - BreakOnNonKeyFrames: hlsBreakOnNonKeyFrames - }), ["aac", "mp3", "opus", "wav"].filter(canPlayAudioFormat).forEach(function(audioFormat) { + + hlsVideoAudioCodecs = hlsVideoAudioCodecs.filter(function (c) { + return (options.disableHlsVideoAudioCodecs || []).indexOf(c) === -1; + }); + + var mp4VideoCodecs = []; + var hlsVideoCodecs = []; + + if (canPlayH264(videoTestElement)) { + mp4VideoCodecs.push('h264'); + hlsVideoCodecs.push('h264'); + } + if (canPlayH265(videoTestElement, options)) { + mp4VideoCodecs.push('h265'); + mp4VideoCodecs.push('hevc'); + + if (browser.tizen || browser.web0s) { + hlsVideoCodecs.push('h265'); + hlsVideoCodecs.push('hevc'); + } + } + + if (supportsMpeg2Video()) { + mp4VideoCodecs.push('mpeg2video'); + } + + if (supportsVc1()) { + mp4VideoCodecs.push('vc1'); + } + + if (browser.tizen || browser.orsay) { + mp4VideoCodecs.push('msmpeg4v2'); + } + + if (canPlayVp8) { + mp4VideoCodecs.push('vp8'); + } + if (canPlayVp9) { + mp4VideoCodecs.push('vp9'); + } + + if (canPlayVp8 || browser.tizen || browser.orsay) { + videoAudioCodecs.push('vorbis'); + } + + if (mp4VideoCodecs.length) { + profile.DirectPlayProfiles.push({ + Container: 'mp4,m4v', + Type: 'Video', + VideoCodec: mp4VideoCodecs.join(','), + AudioCodec: videoAudioCodecs.join(',') + }); + } + + if (canPlayMkv && mp4VideoCodecs.length) { + profile.DirectPlayProfiles.push({ + Container: 'mkv', + Type: 'Video', + VideoCodec: mp4VideoCodecs.join(','), + AudioCodec: videoAudioCodecs.join(',') + }); + } + + // These are formats we can't test for but some devices will support + ['m2ts', 'wmv', 'ts', 'asf', 'avi', 'mpg', 'mpeg', 'flv', '3gp', 'mts', 'trp', 'vob', 'vro', 'mov'].map(function (container) { + return getDirectPlayProfileForVideoContainer(container, videoAudioCodecs, videoTestElement, options); + }).filter(function (i) { + return i != null; + }).forEach(function (i) { + profile.DirectPlayProfiles.push(i); + }); + + ['opus', 'mp3', 'mp2', 'aac', 'flac', 'alac', 'webma', 'wma', 'wav', 'ogg', 'oga'].filter(canPlayAudioFormat).forEach(function (audioFormat) { + + if (audioFormat === 'mp2') { + + profile.DirectPlayProfiles.push({ + Container: 'mp2,mp3', + Type: 'Audio', + AudioCodec: audioFormat + }); + } + + else if (audioFormat === 'mp3') { + + profile.DirectPlayProfiles.push({ + Container: audioFormat, + Type: 'Audio', + AudioCodec: audioFormat + }); + + } else { + profile.DirectPlayProfiles.push({ + Container: audioFormat === 'webma' ? 'webma,webm' : audioFormat, + Type: 'Audio' + }); + } + + // aac also appears in the m4a container + if (audioFormat === 'aac' || audioFormat === 'alac') { + + profile.DirectPlayProfiles.push({ + Container: 'm4a', + AudioCodec: audioFormat, + Type: 'Audio' + }); + } + }); + + if (canPlayVp8) { + profile.DirectPlayProfiles.push({ + Container: 'webm', + Type: 'Video', + AudioCodec: webmAudioCodecs.join(','), + VideoCodec: 'VP8' + }); + } + + if (canPlayVp9) { + profile.DirectPlayProfiles.push({ + Container: 'webm', + Type: 'Video', + AudioCodec: webmAudioCodecs.join(','), + VideoCodec: 'VP9' + }); + } + + profile.TranscodingProfiles = []; + + var hlsBreakOnNonKeyFrames = browser.iOS || browser.osx || browser.edge || !canPlayNativeHls() ? true : false; + + if (canPlayHls() && browser.enableHlsAudio !== false) { + profile.TranscodingProfiles.push({ + + // hlsjs, edge, and android all seem to require ts container + Container: !canPlayNativeHls() || browser.edge || browser.android ? 'ts' : 'aac', + Type: 'Audio', + AudioCodec: 'aac', + Context: 'Streaming', + Protocol: 'hls', + MaxAudioChannels: physicalAudioChannels.toString(), + MinSegments: browser.iOS || browser.osx ? '2' : '1', + BreakOnNonKeyFrames: hlsBreakOnNonKeyFrames + }); + } + + // For streaming, prioritize opus transcoding after mp3/aac. It is too problematic with random failures + // But for static (offline sync), it will be just fine. + // Prioritize aac higher because the encoder can accept more channels than mp3 + ['aac', 'mp3', 'opus', 'wav'].filter(canPlayAudioFormat).forEach(function (audioFormat) { + profile.TranscodingProfiles.push({ Container: audioFormat, - Type: "Audio", + Type: 'Audio', AudioCodec: audioFormat, - Context: "Streaming", - Protocol: "http", + Context: 'Streaming', + Protocol: 'http', MaxAudioChannels: physicalAudioChannels.toString() - }) - }), ["opus", "mp3", "aac", "wav"].filter(canPlayAudioFormat).forEach(function(audioFormat) { + }); + }); + + ['opus', 'mp3', 'aac', 'wav'].filter(canPlayAudioFormat).forEach(function (audioFormat) { + profile.TranscodingProfiles.push({ Container: audioFormat, - Type: "Audio", + Type: 'Audio', AudioCodec: audioFormat, - Context: "Static", - Protocol: "http", + Context: 'Static', + Protocol: 'http', MaxAudioChannels: physicalAudioChannels.toString() - }) - }), !canPlayMkv || browser.tizen || browser.orsay || !1 === options.enableMkvProgressive || profile.TranscodingProfiles.push({ - Container: "mkv", - Type: "Video", - AudioCodec: videoAudioCodecs.join(","), - VideoCodec: mp4VideoCodecs.join(","), - Context: "Streaming", - MaxAudioChannels: physicalAudioChannels.toString(), - CopyTimestamps: !0 - }), canPlayMkv && profile.TranscodingProfiles.push({ - Container: "mkv", - Type: "Video", - AudioCodec: videoAudioCodecs.join(","), - VideoCodec: mp4VideoCodecs.join(","), - Context: "Static", - MaxAudioChannels: physicalAudioChannels.toString(), - CopyTimestamps: !0 - }), canPlayHls() && !1 !== options.enableHls && profile.TranscodingProfiles.push({ - Container: "ts", - Type: "Video", - AudioCodec: hlsVideoAudioCodecs.join(","), - VideoCodec: hlsVideoCodecs.join(","), - Context: "Streaming", - Protocol: "hls", - MaxAudioChannels: physicalAudioChannels.toString(), - MinSegments: browser.iOS || browser.osx ? "2" : "1", - BreakOnNonKeyFrames: hlsBreakOnNonKeyFrames - }), canPlayVp8 && profile.TranscodingProfiles.push({ - Container: "webm", - Type: "Video", - AudioCodec: "vorbis", - VideoCodec: "vpx", - Context: "Streaming", - Protocol: "http", - MaxAudioChannels: physicalAudioChannels.toString() - }), profile.TranscodingProfiles.push({ - Container: "mp4", - Type: "Video", - AudioCodec: videoAudioCodecs.join(","), - VideoCodec: "h264", - Context: "Static", - Protocol: "http" - }), profile.ContainerProfiles = [], profile.CodecProfiles = []; - var supportsSecondaryAudio = browser.tizen || browser.orsay || videoTestElement.audioTracks, - aacCodecProfileConditions = []; - videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.5"').replace(/no/, "") || aacCodecProfileConditions.push({ - Condition: "NotEquals", - Property: "AudioProfile", - Value: "HE-AAC" - }), supportsSecondaryAudio || aacCodecProfileConditions.push({ - Condition: "Equals", - Property: "IsSecondaryAudio", - Value: "false", - IsRequired: "false" - }), browser.chromecast && aacCodecProfileConditions.push({ - Condition: "LessThanEqual", - Property: "AudioChannels", - Value: "2", - IsRequired: !0 - }), aacCodecProfileConditions.length && profile.CodecProfiles.push({ - Type: "VideoAudio", - Codec: "aac", - Conditions: aacCodecProfileConditions - }), supportsSecondaryAudio || profile.CodecProfiles.push({ - Type: "VideoAudio", - Conditions: [{ - Condition: "Equals", - Property: "IsSecondaryAudio", - Value: "false", - IsRequired: "false" - }] + }); }); - var maxH264Level = browser.chromecast ? 42 : 51, - h264Profiles = "high|main|baseline|constrained baseline"; - maxH264Level >= 51 && browser.chrome && !browser.osx && (h264Profiles += "|high 10"), profile.CodecProfiles.push({ - Type: "Video", - Codec: "h264", - Conditions: [{ - Condition: "NotEquals", - Property: "IsAnamorphic", - Value: "true", - IsRequired: !1 - }, { - Condition: "EqualsAny", - Property: "VideoProfile", - Value: h264Profiles - }, { - Condition: "LessThanEqual", - Property: "VideoLevel", - Value: maxH264Level.toString() - }] - }), browser.edgeUwp || browser.tizen || browser.orsay || browser.web0s, maxVideoWidth && profile.CodecProfiles[profile.CodecProfiles.length - 1].Conditions.push({ - Condition: "LessThanEqual", - Property: "Width", - Value: maxVideoWidth.toString(), - IsRequired: !1 + + if (canPlayMkv && !browser.tizen && !browser.orsay && options.enableMkvProgressive !== false) { + profile.TranscodingProfiles.push({ + Container: 'mkv', + Type: 'Video', + AudioCodec: videoAudioCodecs.join(','), + VideoCodec: mp4VideoCodecs.join(','), + Context: 'Streaming', + MaxAudioChannels: physicalAudioChannels.toString(), + CopyTimestamps: true + }); + } + + if (canPlayMkv) { + profile.TranscodingProfiles.push({ + Container: 'mkv', + Type: 'Video', + AudioCodec: videoAudioCodecs.join(','), + VideoCodec: mp4VideoCodecs.join(','), + Context: 'Static', + MaxAudioChannels: physicalAudioChannels.toString(), + CopyTimestamps: true + }); + } + + if (canPlayHls() && options.enableHls !== false) { + profile.TranscodingProfiles.push({ + Container: 'ts', + Type: 'Video', + AudioCodec: hlsVideoAudioCodecs.join(','), + VideoCodec: hlsVideoCodecs.join(','), + Context: 'Streaming', + Protocol: 'hls', + MaxAudioChannels: physicalAudioChannels.toString(), + MinSegments: browser.iOS || browser.osx ? '2' : '1', + BreakOnNonKeyFrames: hlsBreakOnNonKeyFrames + }); + } + + if (canPlayVp8) { + profile.TranscodingProfiles.push({ + Container: 'webm', + Type: 'Video', + AudioCodec: 'vorbis', + VideoCodec: 'vpx', + Context: 'Streaming', + Protocol: 'http', + // If audio transcoding is needed, limit channels to number of physical audio channels + // Trying to transcode to 5 channels when there are only 2 speakers generally does not sound good + MaxAudioChannels: physicalAudioChannels.toString() + }); + } + + profile.TranscodingProfiles.push({ + Container: 'mp4', + Type: 'Video', + AudioCodec: videoAudioCodecs.join(','), + VideoCodec: 'h264', + Context: 'Static', + Protocol: 'http' }); - var globalMaxVideoBitrate = (getGlobalMaxVideoBitrate() || "").toString(), - h264MaxVideoBitrate = globalMaxVideoBitrate; - h264MaxVideoBitrate && profile.CodecProfiles[profile.CodecProfiles.length - 1].Conditions.push({ - Condition: "LessThanEqual", - Property: "VideoBitrate", - Value: h264MaxVideoBitrate, - IsRequired: !0 + + profile.ContainerProfiles = []; + + profile.CodecProfiles = []; + + var supportsSecondaryAudio = browser.tizen || browser.orsay || videoTestElement.audioTracks; + + var aacCodecProfileConditions = []; + + // Handle he-aac not supported + if (!videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.5"').replace(/no/, '')) { + // TODO: This needs to become part of the stream url in order to prevent stream copy + aacCodecProfileConditions.push({ + Condition: 'NotEquals', + Property: 'AudioProfile', + Value: 'HE-AAC' + }); + } + + if (!supportsSecondaryAudio) { + aacCodecProfileConditions.push({ + Condition: 'Equals', + Property: 'IsSecondaryAudio', + Value: 'false', + IsRequired: 'false' + }); + } + + if (browser.chromecast) { + aacCodecProfileConditions.push({ + Condition: 'LessThanEqual', + Property: 'AudioChannels', + Value: '2', + IsRequired: true + }); + } + + if (aacCodecProfileConditions.length) { + profile.CodecProfiles.push({ + Type: 'VideoAudio', + Codec: 'aac', + Conditions: aacCodecProfileConditions + }); + } + + if (!supportsSecondaryAudio) { + profile.CodecProfiles.push({ + Type: 'VideoAudio', + Conditions: [ + { + Condition: 'Equals', + Property: 'IsSecondaryAudio', + Value: 'false', + IsRequired: 'false' + } + ] + }); + } + + var maxH264Level = browser.chromecast ? 42 : 51; + var h264Profiles = 'high|main|baseline|constrained baseline'; + + if (maxH264Level >= 51 && browser.chrome && !browser.osx) { + h264Profiles += '|high 10'; + } + + profile.CodecProfiles.push({ + Type: 'Video', + Codec: 'h264', + Conditions: [ + { + Condition: 'NotEquals', + Property: 'IsAnamorphic', + Value: 'true', + IsRequired: false + }, + { + Condition: 'EqualsAny', + Property: 'VideoProfile', + Value: h264Profiles + }, + { + Condition: 'LessThanEqual', + Property: 'VideoLevel', + Value: maxH264Level.toString() + }] }); + + if (!browser.edgeUwp && !browser.tizen && !browser.orsay && !browser.web0s) { + //profile.CodecProfiles[profile.CodecProfiles.length - 1].Conditions.push({ + // Condition: 'NotEquals', + // Property: 'IsAVC', + // Value: 'false', + // IsRequired: false + //}); + + //profile.CodecProfiles[profile.CodecProfiles.length - 1].Conditions.push({ + // Condition: 'NotEquals', + // Property: 'IsInterlaced', + // Value: 'true', + // IsRequired: false + //}); + } + + if (maxVideoWidth) { + profile.CodecProfiles[profile.CodecProfiles.length - 1].Conditions.push({ + Condition: 'LessThanEqual', + Property: 'Width', + Value: maxVideoWidth.toString(), + IsRequired: false + }); + } + + var globalMaxVideoBitrate = (getGlobalMaxVideoBitrate() || '').toString(); + + var h264MaxVideoBitrate = globalMaxVideoBitrate; + + if (h264MaxVideoBitrate) { + profile.CodecProfiles[profile.CodecProfiles.length - 1].Conditions.push({ + Condition: 'LessThanEqual', + Property: 'VideoBitrate', + Value: h264MaxVideoBitrate, + IsRequired: true + }); + } + var globalVideoConditions = []; - return globalMaxVideoBitrate && globalVideoConditions.push({ - Condition: "LessThanEqual", - Property: "VideoBitrate", - Value: globalMaxVideoBitrate - }), maxVideoWidth && globalVideoConditions.push({ - Condition: "LessThanEqual", - Property: "Width", - Value: maxVideoWidth.toString(), - IsRequired: !1 - }), globalVideoConditions.length && profile.CodecProfiles.push({ - Type: "Video", - Conditions: globalVideoConditions - }), browser.chromecast && profile.CodecProfiles.push({ - Type: "Audio", - Codec: "flac", - Conditions: [{ - Condition: "LessThanEqual", - Property: "AudioSampleRate", - Value: "96000" - }] - }), profile.SubtitleProfiles = [], supportsTextTracks() && profile.SubtitleProfiles.push({ - Format: "vtt", - Method: "External" - }), profile.ResponseProfiles = [], profile.ResponseProfiles.push({ - Type: "Video", - Container: "m4v", - MimeType: "video/mp4" - }), profile - } + + if (globalMaxVideoBitrate) { + globalVideoConditions.push({ + Condition: 'LessThanEqual', + Property: 'VideoBitrate', + Value: globalMaxVideoBitrate + }); + } + + if (maxVideoWidth) { + globalVideoConditions.push({ + Condition: 'LessThanEqual', + Property: 'Width', + Value: maxVideoWidth.toString(), + IsRequired: false + }); + } + + if (globalVideoConditions.length) { + profile.CodecProfiles.push({ + Type: 'Video', + Conditions: globalVideoConditions + }); + } + + if (browser.chromecast) { + profile.CodecProfiles.push({ + Type: 'Audio', + Codec: 'flac', + Conditions: [ + { + Condition: 'LessThanEqual', + Property: 'AudioSampleRate', + Value: '96000' + }] + }); + } + + // Subtitle profiles + // External vtt or burn in + profile.SubtitleProfiles = []; + if (supportsTextTracks()) { + + profile.SubtitleProfiles.push({ + Format: 'vtt', + Method: 'External' + }); + } + + profile.ResponseProfiles = []; + + profile.ResponseProfiles.push({ + Type: 'Video', + Container: 'm4v', + MimeType: 'video/mp4' + }); + + return profile; + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/cardbuilder/card.css b/src/bower_components/emby-webcomponents/cardbuilder/card.css index dbe41a857a..ebdeffe1df 100644 --- a/src/bower_components/emby-webcomponents/cardbuilder/card.css +++ b/src/bower_components/emby-webcomponents/cardbuilder/card.css @@ -1,23 +1,10 @@ -.card, -.card:focus { - font-weight: inherit !important -} - -.card, -.cardBox, -.cardContent, -.textActionButton { - -webkit-tap-highlight-color: transparent; - outline: 0 !important -} - button::-moz-focus-inner { padding: 0; - border: 0 + border: 0; } button { - -webkit-border-fit: border !important + -webkit-border-fit: border !important; } .card { @@ -25,155 +12,127 @@ button { font-size: inherit !important; font-family: inherit !important; text-transform: none; - background: 0 0 !important; + background-color: transparent !important; + background: none !important; margin: 0; padding: 0; display: block; color: inherit !important; + -webkit-tap-highlight-color: rgba(0,0,0,0); + outline: none !important; cursor: pointer; contain: layout style; - -webkit-flex-shrink: 0; - flex-shrink: 0 -} - -.cardContent-button, -.textActionButton { - cursor: pointer; - vertical-align: middle; - font-family: inherit + flex-shrink: 0; + font-weight: inherit !important; } .card-nofocustransform { - contain: layout style paint + contain: layout style paint; } .itemsContainer { - display: -webkit-box; - display: -webkit-flex; - display: flex -} - -.vertical-list, -.vertical-wrap { - display: -webkit-box; - display: -webkit-flex; - -webkit-box-direction: normal + display: flex; } .vertical-list { display: flex; - -webkit-box-orient: vertical; - -webkit-flex-direction: column; flex-direction: column; - -webkit-flex-wrap: nowrap; - flex-wrap: nowrap + flex-wrap: nowrap; } .vertical-wrap { display: flex; - -webkit-box-orient: horizontal; - -webkit-flex-direction: row; flex-direction: row; - -webkit-flex-wrap: wrap; - flex-wrap: wrap + flex-wrap: wrap; } -.cardImageContainer, -.mediaSourceIndicator { - display: -webkit-box; - -webkit-box-align: center -} - -.vertical-wrap.centered { - -webkit-box-pack: center; - -webkit-justify-content: center; - justify-content: center -} + .vertical-wrap.centered { + justify-content: center; + } .cardScalable { position: relative; - contain: layout style + contain: layout style; } -.cardPadder-backdrop, -.cardPadder-mixedBackdrop, -.cardPadder-overflowBackdrop, -.cardPadder-overflowSmallBackdrop, -.cardPadder-smallBackdrop { +.cardPadder-backdrop, .cardPadder-mixedBackdrop, .cardPadder-smallBackdrop, .cardPadder-overflowBackdrop, .cardPadder-overflowSmallBackdrop { padding-bottom: 56.25%; - contain: strict + contain: strict; } -.cardPadder-mixedSquare, -.cardPadder-overflowSquare, -.cardPadder-square, -.overflowSquareCard-textCardPadder { +.cardPadder-square, .cardPadder-mixedSquare, .cardPadder-overflowSquare, .overflowSquareCard-textCardPadder { padding-bottom: 100%; - contain: strict + contain: strict; } -.cardPadder-mixedPortrait, -.cardPadder-overflowPortrait, -.cardPadder-portrait, -.overflowPortraitCard-textCardPadder { +.cardPadder-portrait, .cardPadder-mixedPortrait, .cardPadder-overflowPortrait, .overflowPortraitCard-textCardPadder { padding-bottom: 150%; - contain: strict + contain: strict; } .cardPadder-banner { padding-bottom: 18.5%; - contain: strict + contain: strict; } .cardBox { padding: 0 !important; margin: .42em; - -webkit-transition: none; - -o-transition: none; transition: none; border: 0 solid transparent; - contain: layout style + /* These both are needed in case cardBox is a button */ + -webkit-tap-highlight-color: rgba(0,0,0,0); + outline: none !important; + contain: layout style; } -@media (min-width:50em) { +/*.cardBox-withfocuscontent { + margin: .68em; +}*/ + +@media (min-width: 50em) { + .cardBox { - margin: .9em + margin: .9em; } } .cardBox-withfocuscontent-large { - margin: .4em + margin: .4em; } +/*.card-focuscontent { + border: .12em solid transparent; +}*/ + .card-focuscontent-large { - border: .5em solid transparent + border: .5em solid transparent; } .cardBox-focustransform { will-change: transform; - -webkit-transition: -webkit-transform .2s ease-out; - -o-transition: transform .2s ease-out; - transition: transform .2s ease-out + transition: transform 200ms ease-out; } -.card:focus>.cardBox-focustransform { - -webkit-transform: scale(1.18, 1.18); - transform: scale(1.18, 1.18) +.card:focus > .cardBox-focustransform { + transform: scale(1.18, 1.18); } .cardBox-bottompadded { - margin-bottom: 1.8em !important + margin-bottom: 1.8em !important; } -@media (max-width:50em) { +@media (max-width: 50em) { + .cardBox-bottompadded { - margin-bottom: 1.2em !important + margin-bottom: 1.2em !important; } } .card:focus { position: relative !important; - z-index: 10 !important + z-index: 10 !important; + font-weight: inherit !important; } .btnCardOptions { @@ -181,17 +140,13 @@ button { bottom: .25em; right: 0; margin: 0 !important; - z-index: 1 + z-index: 1; } .mediaSourceIndicator { - display: -webkit-flex; display: flex; position: absolute; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; top: .3em; left: .3em; @@ -199,186 +154,178 @@ button { vertical-align: middle; width: 1.6em; height: 1.6em; - -webkit-border-radius: 50%; border-radius: 50%; color: #fff; - background: #38c -} - -.cardText, -.innerCardFooter { - overflow: hidden; - text-align: left + background: rgb(51, 136, 204); } .cardImageContainer { - -webkit-background-size: contain; background-size: contain; background-repeat: no-repeat; background-position: center center; - -webkit-align-items: center; + display: -webkit-flex; + display: flex; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; position: relative; - -webkit-background-clip: content-box !important; background-clip: content-box !important; color: inherit; + /* This is only needed for scalable cards */ height: 100%; - contain: strict -} - -.cardContent, -.cardImage { - position: absolute; - right: 0; - top: 0; - left: 0; - bottom: 0 + contain: strict; } .chapterCardImageContainer { background-color: #000; - -webkit-border-radius: 0; - border-radius: 0 + border-radius: 0; } .textCardImageContainer { - background-color: #333 + background-color: #333; } .cardContent { overflow: hidden; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + /* Needed in case this is a button */ display: block; + /* Needed in case this is a button */ margin: 0 !important; + /* Needed in safari */ height: 100%; - contain: strict + -webkit-tap-highlight-color: rgba(0,0,0,0); + outline: none !important; + contain: strict; } .cardContent-button { border: 0 !important; padding: 0 !important; + cursor: pointer; color: inherit; width: 100%; - font-size: inherit + vertical-align: middle; + font-family: inherit; + font-size: inherit; } -.cardContent-button:not(.defaultCardBackground) { - background-color: transparent -} + .cardContent-button:not(.defaultCardBackground) { + background-color: transparent; + } .visualCardBox .cardContent { - -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; - -webkit-border-bottom-right-radius: 0; - border-bottom-right-radius: 0 + border-bottom-right-radius: 0; } .cardContent-shadow { - -webkit-box-shadow: 0 .0725em .29em 0 rgba(0, 0, 0, .37); - box-shadow: 0 .0725em .29em 0 rgba(0, 0, 0, .37) + box-shadow: 0 0.0725em 0.29em 0 rgba(0, 0, 0, 0.37); } +/*.card:focus .cardContent-shadow { + box-shadow: 0 .63em 1.26em rgba(0, 0, 0, 0.37); +}*/ + .cardImageContainer { - display: -webkit-box; - display: -webkit-flex; - display: flex + display: flex; } .cardImage { - -webkit-background-size: contain; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; background-size: contain; background-repeat: no-repeat; - background-position: center bottom + background-position: center bottom; } .cardImage-img { max-height: 100%; max-width: 100%; + /* This is simply for lazy image purposes, to ensure the image is visible sooner when scrolling */ min-height: 70%; min-width: 70%; - margin: auto + margin: auto; } .coveredImage-img { width: 100%; - height: 100% + height: 100%; } .coveredImage-noscale-img { max-height: none; - max-width: none + max-width: none; } .coveredImage { - -webkit-background-size: 100% 100%; background-size: 100% 100%; - background-position: center center + background-position: center center; } .coveredImage-noScale { - -webkit-background-size: cover; - background-size: cover + background-size: cover; } .cardFooter { - padding: .3em .3em .5em; - position: relative + padding: .3em .3em .5em .3em; + position: relative; } .visualCardBox { - -webkit-box-shadow: 0 .0725em .29em 0 rgba(0, 0, 0, .37); - box-shadow: 0 .0725em .29em 0 rgba(0, 0, 0, .37); - -webkit-border-radius: .145em; - border-radius: .145em + box-shadow: 0 0.0725em 0.29em 0 rgba(0, 0, 0, 0.37); + border-radius: .145em; } .innerCardFooter { - background: rgba(0, 0, 0, .7); + background: rgba(0,0,0,.7); position: absolute; bottom: 0; left: 0; + text-align: left; z-index: 1; + overflow: hidden; max-width: 100%; - color: #fff + color: #fff; } .innerCardFooterClear { - background-color: transparent + background-color: transparent; } .fullInnerCardFooter { - right: 0 + right: 0; } .cardText { padding: .06em .5em; white-space: nowrap; - -o-text-overflow: ellipsis; - text-overflow: ellipsis -} - -.cardDefaultText, -.cardTextCentered { - text-align: center + overflow: hidden; + text-overflow: ellipsis; + text-align: left; } .cardText-secondary { - font-size: 86% + font-size: 86%; } .cardText-first { - padding-top: .24em + padding-top: .24em; } -.innerCardFooter>.cardText { - padding: .3em .5em +.innerCardFooter > .cardText { + padding: .3em .5em; } .cardFooter-withlogo { padding-left: 4em; - position: relative + position: relative; } .cardFooterLogo { @@ -387,70 +334,77 @@ button { bottom: 0; left: 0; width: 4.5em; - -webkit-background-size: 70% auto; background-size: 70% auto; background-repeat: no-repeat; - background-position: center center + background-position: center center; +} + +.cardTextCentered { + text-align: center; } .cardText-rightmargin { - margin-right: 2em + margin-right: 2em; } .cardDefaultText { - white-space: normal + white-space: normal; + text-align: center; } .textActionButton { - background: 0 0; + border: 0 !important; + background: transparent; border: 0 !important; padding: 0 !important; + cursor: pointer; + -webkit-tap-highlight-color: rgba(0,0,0,0); + outline: none !important; color: inherit; - font-size: inherit + vertical-align: middle; + font-family: inherit; + font-size: inherit; + /*display: flex; + align-items: center; + justify-content: center;*/ } -.textActionButton:hover { - text-decoration: underline -} + .textActionButton:hover { + text-decoration: underline; + } .cardImageIcon { font-size: 5em; - color: inherit + color: inherit; } .cardImageIcon-small { font-size: 3em; - margin-bottom: .1em + margin-bottom: .1em; } .cardIndicators { right: .225em; top: .225em; position: absolute; - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - contain: layout style + contain: layout style; } .cardProgramAttributeIndicators { top: 0; left: 0; position: absolute; - display: -webkit-box; - display: -webkit-flex; display: flex; text-transform: uppercase; - font-size: 92% + font-size: 92%; } .programAttributeIndicator { padding: .18em .5em; color: #fff; - font-weight: 500 + font-weight: 500; } .cardOverlayButton { @@ -458,31 +412,24 @@ button { margin: 0; z-index: 1; padding: .75em; - font-size: 88% + font-size: 88%; } .cardOverlayButton-br { position: absolute; bottom: 0; - right: 0 + right: 0; } .cardOverlayButtonIcon { - background-color: rgba(0, 0, 0, .7) !important; - -webkit-border-radius: 100em; + background-color: rgba(0,0,0,.7) !important; border-radius: 100em; width: 1.5em !important; height: 1.5em !important; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - display: -webkit-box; - display: -webkit-flex; display: flex; - font-size: 1.66956521739130434em !important + font-size: 1.66956521739130434em !important; } .cardOverlayButton-centered { @@ -490,8 +437,6 @@ button { right: initial; position: static; position: absolute; - display: -webkit-box; - display: -webkit-flex; display: flex; font-size: 112%; margin: -1.3em 0 0 -1.3em; @@ -499,372 +444,358 @@ button { height: 2.6em; top: 50%; left: 50%; - background-color: rgba(0, 0, 0, .5) !important; - border: .06em solid rgba(255, 255, 255, .6); + background-color: rgba(0,0,0,.5) !important; + border: .06em solid rgba(255,255,255,.6); padding: .38em !important; color: rgba(255, 255, 255, .76); - -webkit-transition: -webkit-transform .2s ease-out; - -o-transition: transform .2s ease-out; - transition: transform .2s ease-out + transition: transform 200ms ease-out; } -.cardOverlayButton-centered:hover { - -webkit-transform: scale(1.2, 1.2); - transform: scale(1.2, 1.2) -} + .cardOverlayButton-centered:hover { + transform: scale(1.2, 1.2); + } -.backdropCard, .bannerCard { - width: 100% + width: 100%; +} + +.backdropCard { + width: 100%; +} + +.smallBackdropCard { + width: 50%; } -.smallBackdropCard, .squareCard { - width: 50% + width: 50%; } .portraitCard { - width: 33.333333333333333333333333333333% + width: 33.333333333333333333333333333333%; } .mixedPortraitCard { - width: 12em + width: 12em; } .mixedSquareCard { - width: 18em + width: 18em; } .mixedBackdropCard { - width: 32em + width: 32em; } -@media (min-width:25em) { +@media (min-width: 25em) { + .backdropCard { - width: 50% + width: 50%; } } -@media (min-width:31.25em) { +@media (min-width: 31.25em) { - .portraitCard, - .smallBackdropCard, - .squareCard { - width: 33.333333333333333333333333333333% + .smallBackdropCard { + width: 33.333333333333333333333333333333%; + } + + .squareCard, .portraitCard { + width: 33.333333333333333333333333333333%; } } -@media (min-width:43.75em) { - - .portraitCard, - .squareCard { - width: 25% +@media (min-width: 43.75em) { + .squareCard, .portraitCard { + width: 25%; } } -@media (min-width:48.125em) { +@media (min-width: 48.125em) { .backdropCard { - width: 33.333333333333333333333333333333% + width: 33.333333333333333333333333333333%; } } -@media (min-width:50em) { +@media (min-width: 50em) { + .bannerCard { - width: 50% + width: 50%; } - .portraitCard, - .squareCard { - width: 20% + .squareCard, .portraitCard { + width: 20%; } .smallBackdropCard { - width: 25% + width: 25%; } } -@media (min-width:62.5em) { +@media (min-width: 62.5em) { + + .smallBackdropCard { - width: 20% + width: 20%; } } -@media (min-width:75em) { +@media (min-width: 75em) { + .backdropCard { - width: 25% + width: 25%; } - .portraitCard, - .squareCard { - width: 16.666666666666666666666666666667% + .squareCard, .portraitCard { + width: 16.666666666666666666666666666667%; } .bannerCard { - width: 33.333333333333333333333333333333% + width: 33.333333333333333333333333333333%; } .smallBackdropCard { - width: 16.666666666666666666666666666667% + width: 16.666666666666666666666666666667%; } } -@media (min-width:87.5em) { - .portraitCard, - .smallBackdropCard, - .squareCard { - width: 14.285714285714285714285714285714% +@media (min-width: 87.5em) { + + .squareCard, .portraitCard { + width: 14.285714285714285714285714285714%; } -} -@media (min-width:100em) { .smallBackdropCard { - width: 12.5% + width: 14.285714285714285714285714285714%; + } +} + +@media (min-width: 100em) { + + .smallBackdropCard { + width: 12.5%; } .backdropCard { - width: 20% + width: 20%; } - .portraitCard, - .squareCard { - width: 12.5% + .squareCard, .portraitCard { + width: 12.5%; } } -@media (min-width:120em) { +@media (min-width: 120em) { - .portraitCard, - .squareCard { - width: 11.111111111111111111111111111111% + .squareCard, .portraitCard { + width: 11.111111111111111111111111111111%; } } -@media (min-width:131.25em) { +@media (min-width: 131.25em) { + .bannerCard { - width: 25% + width: 25%; } - .portraitCard, - .squareCard { - width: 10% + .squareCard, .portraitCard { + width: 10%; } } -@media (min-width:156.25em) { +@media (min-width: 156.25em) { + .backdropCard { - width: 16.666666666666666666666666666667% + width: 16.666666666666666666666666666667%; } } -.itemsContainer-tv>.backdropCard { - width: 25% +.itemsContainer-tv > .backdropCard { + width: 25%; } -.itemsContainer-tv>.portraitCard, -.itemsContainer-tv>.squareCard { - width: 16.666666666666666666666666666667% +.itemsContainer-tv > .squareCard { + width: 16.666666666666666666666666666667%; +} + +.itemsContainer-tv > .portraitCard { + width: 16.666666666666666666666666666667%; +} + +.overflowBackdropCard { + width: 72vw; } -.overflowBackdropCard, .overflowSmallBackdropCard { - width: 72vw + width: 72vw; } -.overflowPortraitCard, -.overflowSquareCard { - width: 40vw +.overflowSquareCard, .overflowPortraitCard { + width: 40vw; } -@media (min-width:25em) { +@media (min-width: 25em) { .overflowPortraitCard { - width: 31.2vw + width: 31.2vw; } } -@media (min-width:35em) { +@media (min-width: 35em) { .overflowSquareCard { - width: 31.2vw + width: 31.2vw; } .overflowBackdropCard { - width: 45.5vw + width: 45.5vw; } .overflowSmallBackdropCard { - width: 30vw + width: 30vw; } } -@media (min-width:43.75em) { - - .overflowPortraitCard, - .overflowSquareCard { - width: 23.3vw +@media (min-width: 43.75em) { + .overflowSquareCard, .overflowPortraitCard { + width: 23.3vw; } } -@media (min-width:48.125em) { +@media (min-width: 48.125em) { + .overflowBackdropCard, .overflowSmallBackdropCard { + width: 30vw; + } +} - .overflowBackdropCard, +@media (orientation: landscape) { + .overflowBackdropCard, .overflowSmallBackdropCard { + width: 30vw; + } + + .overflowSquareCard, .overflowPortraitCard { + width: 23.3vw; + } +} + +@media (orientation: landscape) and (min-width: 48.125em) { + .overflowBackdropCard, .overflowSmallBackdropCard { + width: 23.3vw; + } +} + +@media (orientation: landscape) and (min-width: 50em) { .overflowSmallBackdropCard { - width: 30vw + width: 15.5vw; } } -@media (orientation:landscape) { +@media (min-width: 50em) { - .overflowBackdropCard, - .overflowSmallBackdropCard { - width: 30vw - } - - .overflowPortraitCard, - .overflowSquareCard { - width: 23.3vw + .overflowSquareCard, .overflowPortraitCard { + width: 18.4vw; } } -@media (orientation:landscape) and (min-width:48.125em) { +@media (min-width: 75em) { - .overflowBackdropCard, - .overflowSmallBackdropCard { - width: 23.3vw + .overflowBackdropCard, .overflowSmallBackdropCard { + width: 23.3vw; + } + + .overflowSquareCard, .overflowPortraitCard { + width: 15.5vw; } } -@media (orientation:landscape) and (min-width:50em) { - .overflowSmallBackdropCard { - width: 15.5vw +@media (min-width: 87.5em) { + + .overflowSquareCard, .overflowPortraitCard { + width: 13.3vw; } } -@media (min-width:50em) { +@media (min-width: 100em) { - .overflowPortraitCard, - .overflowSquareCard { - width: 18.4vw + .overflowBackdropCard, .overflowSmallBackdropCard { + width: 18.7vw; + } + + .overflowSquareCard, .overflowPortraitCard { + width: 11.6vw; } } -@media (min-width:75em) { +@media (min-width: 120em) { - .overflowBackdropCard, - .overflowSmallBackdropCard { - width: 23.3vw - } - - .overflowPortraitCard, - .overflowSquareCard { - width: 15.5vw + .overflowSquareCard, .overflowPortraitCard { + width: 10.3vw; } } -@media (min-width:87.5em) { +@media (min-width: 131.25em) { - .overflowPortraitCard, - .overflowSquareCard { - width: 13.3vw + .overflowSquareCard, .overflowPortraitCard { + width: 9.3vw; } } -@media (min-width:100em) { +@media (min-width: 156.25em) { - .overflowBackdropCard, - .overflowSmallBackdropCard { - width: 18.7vw - } - - .overflowPortraitCard, - .overflowSquareCard { - width: 11.6vw + .overflowBackdropCard, .overflowSmallBackdropCard { + width: 15.6vw; } } -@media (min-width:120em) { - - .overflowPortraitCard, - .overflowSquareCard { - width: 10.3vw - } -} - -@media (min-width:131.25em) { - - .overflowPortraitCard, - .overflowSquareCard { - width: 9.3vw - } -} - -@media (min-width:156.25em) { - - .overflowBackdropCard, - .overflowSmallBackdropCard { - width: 15.6vw - } -} - -.itemsContainer-tv>.overflowBackdropCard { - width: 23.5vw +.itemsContainer-tv > .overflowBackdropCard { + width: 23.5vw; } .overflowBackdropCard-textCard { - width: 15.5vw !important + width: 15.5vw !important; } .overflowBackdropCard-textCardPadder { - padding-bottom: 87.75% + padding-bottom: 87.75%; } -.itemsContainer-tv>.overflowPortraitCard, -.itemsContainer-tv>.overflowSquareCard { - width: 15.6vw +.itemsContainer-tv > .overflowSquareCard, .itemsContainer-tv > .overflowPortraitCard { + width: 15.6vw; } -.itemsContainer-tv>.overflowSmallBackdropCard { - width: 18.8vw +.itemsContainer-tv > .overflowSmallBackdropCard { + width: 18.8vw; } .cardOverlayContainer { - background: -webkit-radial-gradient(50% 50%, farthest-corner, rgba(30, 30, 30, .5) 50%, #2c2c2c 100%); - background: -o-radial-gradient(50% 50%, farthest-corner, rgba(30, 30, 30, .5) 50%, #2c2c2c 100%); - background: radial-gradient(farthest-corner at 50% 50%, rgba(30, 30, 30, .5) 50%, #2c2c2c 100%); + background: radial-gradient(farthest-corner at 50% 50%,rgba(30,30,30,.5) 50%,#2c2c2c 100%); opacity: 0; - -webkit-transition: opacity .2s; - -o-transition: opacity .2s; transition: opacity .2s; position: absolute; top: 0; left: 0; bottom: 0; right: 0; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none + user-select: none; } .card-hoverable :hover .cardOverlayContainer { - opacity: 1 + opacity: 1; } .cardOverlayButton-hover { opacity: 0; - -webkit-transition: opacity .2s; - -o-transition: opacity .2s; transition: opacity .2s; - background: 0 0; + background: transparent; color: #fff !important; - padding: .5em + padding: .5em; } .cardOverlayButtonIcon-hover { - background: 0 0 !important + background: transparent !important; } .card-hoverable:hover .cardOverlayButton-hover { - opacity: 1 + opacity: 1; } .cardOverlayFab-primary { @@ -876,10 +807,10 @@ button { margin-left: -1.5em; position: absolute; top: 50%; - left: 50% + left: 50%; } -.cardOverlayFab-primary i { - border: .07em solid rgba(255, 255, 255, .9); - color: #fff -} \ No newline at end of file + .cardOverlayFab-primary i { + border: .07em solid rgba(255,255,255,.9); + color: #fff; + } diff --git a/src/bower_components/emby-webcomponents/cardbuilder/cardbuilder.js b/src/bower_components/emby-webcomponents/cardbuilder/cardbuilder.js index ffb42171bc..f5c10ea658 100644 --- a/src/bower_components/emby-webcomponents/cardbuilder/cardbuilder.js +++ b/src/bower_components/emby-webcomponents/cardbuilder/cardbuilder.js @@ -1,515 +1,1826 @@ -define(["datetime", "imageLoader", "connectionManager", "itemHelper", "focusManager", "indicators", "globalize", "layoutManager", "apphost", "dom", "browser", "playbackManager", "itemShortcuts", "css!./card", "paper-icon-button-light", "programStyles"], function(datetime, imageLoader, connectionManager, itemHelper, focusManager, indicators, globalize, layoutManager, appHost, dom, browser, playbackManager, itemShortcuts) { - "use strict"; +define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusManager', 'indicators', 'globalize', 'layoutManager', 'apphost', 'dom', 'browser', 'playbackManager', 'itemShortcuts', 'css!./card', 'paper-icon-button-light', 'programStyles'], + function (datetime, imageLoader, connectionManager, itemHelper, focusManager, indicators, globalize, layoutManager, appHost, dom, browser, playbackManager, itemShortcuts) { + 'use strict'; - function getCardsHtml(items, options) { - return 1 === arguments.length && (options = arguments[0], items = options.items), buildCardsHtmlInternal(items, options) - } + var devicePixelRatio = window.devicePixelRatio || 1; + var enableFocusTransfrom = !browser.slow && !browser.edge; - function getPostersPerRow(shape, screenWidth, isOrientationLandscape) { - switch (shape) { - case "portrait": - return layoutManager.tv ? 5.9999999988 : screenWidth >= 2200 ? 10 : screenWidth >= 1920 ? 9.000000000009 : screenWidth >= 1600 ? 8 : screenWidth >= 1400 ? 7.0000000000021 : screenWidth >= 1200 ? 5.9999999988 : screenWidth >= 800 ? 5 : screenWidth >= 700 ? 4 : 3.0000000003; - case "square": - return layoutManager.tv ? 5.9999999988 : screenWidth >= 2200 ? 10 : screenWidth >= 1920 ? 9.000000000009 : screenWidth >= 1600 ? 8 : screenWidth >= 1400 ? 7.0000000000021 : screenWidth >= 1200 ? 5.9999999988 : screenWidth >= 800 ? 5 : screenWidth >= 700 ? 4 : screenWidth >= 500 ? 3.0000000003 : 2; - case "banner": - return screenWidth >= 2200 ? 4 : screenWidth >= 1200 ? 3.0000000003 : screenWidth >= 800 ? 2 : 1; - case "backdrop": - return layoutManager.tv ? 4 : screenWidth >= 2500 ? 6 : screenWidth >= 1600 ? 5 : screenWidth >= 1200 ? 4 : screenWidth >= 770 ? 3 : screenWidth >= 420 ? 2 : 1; - case "smallBackdrop": - return screenWidth >= 1600 ? 8 : screenWidth >= 1400 ? 7.000000000007001 : screenWidth >= 1200 ? 6 : screenWidth >= 1e3 ? 5 : screenWidth >= 800 ? 4 : screenWidth >= 500 ? 3.0000000003 : 2; - case "overflowSmallBackdrop": - return layoutManager.tv ? 100 / 18.9 : isOrientationLandscape ? screenWidth >= 800 ? 100 / 15.5 : 100 / 23.3 : screenWidth >= 540 ? 100 / 30 : 100 / 72; - case "overflowPortrait": - return layoutManager.tv ? 100 / 15.5 : isOrientationLandscape ? screenWidth >= 1700 ? 100 / 11.6 : 100 / 15.5 : screenWidth >= 1400 ? 100 / 15 : screenWidth >= 1200 ? 100 / 18 : screenWidth >= 760 ? 100 / 23 : screenWidth >= 400 ? 100 / 31.5 : 100 / 42; - case "overflowSquare": - return layoutManager.tv ? 100 / 15.5 : isOrientationLandscape ? screenWidth >= 1700 ? 100 / 11.6 : 100 / 15.5 : screenWidth >= 1400 ? 100 / 15 : screenWidth >= 1200 ? 100 / 18 : screenWidth >= 760 ? 100 / 23 : screenWidth >= 540 ? 100 / 31.5 : 100 / 42; - case "overflowBackdrop": - return layoutManager.tv ? 100 / 23.3 : isOrientationLandscape ? screenWidth >= 1700 ? 100 / 18.5 : 100 / 23.3 : screenWidth >= 1800 ? 100 / 23.5 : screenWidth >= 1400 ? 100 / 30 : screenWidth >= 760 ? 2.5 : screenWidth >= 640 ? 100 / 56 : 100 / 72; - default: - return 4 - } - } + function getCardsHtml(items, options) { - function isResizable(windowWidth) { - var screen = window.screen; - if (screen) { - if (screen.availWidth - windowWidth > 20) return !0 - } - return !1 - } + if (arguments.length === 1) { - function getImageWidth(shape, screenWidth, isOrientationLandscape) { - var imagesPerRow = getPostersPerRow(shape, screenWidth, isOrientationLandscape), - shapeWidth = screenWidth / imagesPerRow; - return Math.round(shapeWidth) - } - - function setCardData(items, options) { - options.shape = options.shape || "auto"; - var primaryImageAspectRatio = imageLoader.getPrimaryImageAspectRatio(items); - if ("auto" === options.shape || "autohome" === options.shape || "autooverflow" === options.shape || "autoVertical" === options.shape) { - var requestedShape = options.shape; - options.shape = null, primaryImageAspectRatio && (primaryImageAspectRatio >= 3 ? (options.shape = "banner", options.coverImage = !0) : options.shape = primaryImageAspectRatio >= 1.33 ? "autooverflow" === requestedShape ? "overflowBackdrop" : "backdrop" : primaryImageAspectRatio > .71 ? "autooverflow" === requestedShape ? "overflowSquare" : "square" : "autooverflow" === requestedShape ? "overflowPortrait" : "portrait"), options.shape || (options.shape = options.defaultShape || ("autooverflow" === requestedShape ? "overflowSquare" : "square")) - } - if ("auto" === options.preferThumb && (options.preferThumb = "backdrop" === options.shape || "overflowBackdrop" === options.shape), options.uiAspect = getDesiredAspect(options.shape), options.primaryImageAspectRatio = primaryImageAspectRatio, !options.width && options.widths && (options.width = options.widths[options.shape]), options.rows && "number" != typeof options.rows && (options.rows = options.rows[options.shape]), !options.width) { - var screenWidth = dom.getWindowSize().innerWidth, - screenHeight = dom.getWindowSize().innerHeight; - if (isResizable(screenWidth)) { - screenWidth = 100 * Math.floor(screenWidth / 100) + options = arguments[0]; + items = options.items; } - options.width = getImageWidth(options.shape, screenWidth, screenWidth > 1.3 * screenHeight) - } - } - function buildCardsHtmlInternal(items, options) { - var isVertical; - "autoVertical" === options.shape && (isVertical = !0), setCardData(items, options); - var currentIndexValue, hasOpenRow, hasOpenSection, apiClient, lastServerId, i, length, html = "", - itemsInRow = 0, - sectionTitleTagName = options.sectionTitleTagName || "div"; - for (i = 0, length = items.length; i < length; i++) { - var item = items[i], - serverId = item.ServerId || options.serverId; - if (serverId !== lastServerId && (lastServerId = serverId, apiClient = connectionManager.getApiClient(lastServerId)), options.indexBy) { - var newIndexValue = ""; - if ("PremiereDate" === options.indexBy) { - if (item.PremiereDate) try { - newIndexValue = datetime.toLocaleDateString(datetime.parseISO8601Date(item.PremiereDate), { - weekday: "long", - month: "long", - day: "numeric" - }) - } catch (err) {} - } else "ProductionYear" === options.indexBy ? newIndexValue = item.ProductionYear : "CommunityRating" === options.indexBy && (newIndexValue = item.CommunityRating ? Math.floor(item.CommunityRating) + (item.CommunityRating % 1 >= .5 ? .5 : 0) + "+" : null); - newIndexValue !== currentIndexValue && (hasOpenRow && (html += "
", hasOpenRow = !1, itemsInRow = 0), hasOpenSection && (html += "
", isVertical && (html += ""), hasOpenSection = !1), html += isVertical ? '
' : '
', html += "<" + sectionTitleTagName + ' class="sectionTitle">' + newIndexValue + "", isVertical && (html += '
'), currentIndexValue = newIndexValue, hasOpenSection = !0) - } - options.rows && 0 === itemsInRow && (hasOpenRow && (html += "
", hasOpenRow = !1), html += '
', hasOpenRow = !0), html += buildCard(i, item, apiClient, options), itemsInRow++, options.rows && itemsInRow >= options.rows && (html += "
", hasOpenRow = !1, itemsInRow = 0) - } - hasOpenRow && (html += "
"), hasOpenSection && (html += "
", isVertical && (html += "")); - var cardFooterHtml = ""; - for (i = 0, length = options.lines || 0; i < length; i++) cardFooterHtml += 0 === i ? '
 
' : '
 
'; - return html - } - - function getDesiredAspect(shape) { - if (shape) { - if (shape = shape.toLowerCase(), -1 !== shape.indexOf("portrait")) return 2 / 3; - if (-1 !== shape.indexOf("backdrop")) return 16 / 9; - if (-1 !== shape.indexOf("square")) return 1; - if (-1 !== shape.indexOf("banner")) return 1e3 / 185 - } - return null - } - - function getCardImageUrl(item, apiClient, options, shape) { - item = item.ProgramInfo || item; - var width = options.width, - height = null, - primaryImageAspectRatio = item.PrimaryImageAspectRatio, - forceName = !1, - imgUrl = null, - coverImage = !1, - uiAspect = null; - return options.preferThumb && item.ImageTags && item.ImageTags.Thumb ? imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Thumb", - maxWidth: width, - tag: item.ImageTags.Thumb - }) : (options.preferBanner || "banner" === shape) && item.ImageTags && item.ImageTags.Banner ? imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Banner", - maxWidth: width, - tag: item.ImageTags.Banner - }) : options.preferDisc && item.ImageTags && item.ImageTags.Disc ? imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Disc", - maxWidth: width, - tag: item.ImageTags.Disc - }) : options.preferLogo && item.ImageTags && item.ImageTags.Logo ? imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Logo", - maxWidth: width, - tag: item.ImageTags.Logo - }) : options.preferLogo && item.ParentLogoImageTag && item.ParentLogoItemId ? imgUrl = apiClient.getScaledImageUrl(item.ParentLogoItemId, { - type: "Logo", - maxWidth: width, - tag: item.ParentLogoImageTag - }) : options.preferThumb && item.SeriesThumbImageTag && !1 !== options.inheritThumb ? imgUrl = apiClient.getScaledImageUrl(item.SeriesId, { - type: "Thumb", - maxWidth: width, - tag: item.SeriesThumbImageTag - }) : options.preferThumb && item.ParentThumbItemId && !1 !== options.inheritThumb && "Photo" !== item.MediaType ? imgUrl = apiClient.getScaledImageUrl(item.ParentThumbItemId, { - type: "Thumb", - maxWidth: width, - tag: item.ParentThumbImageTag - }) : options.preferThumb && item.BackdropImageTags && item.BackdropImageTags.length ? (imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Backdrop", - maxWidth: width, - tag: item.BackdropImageTags[0] - }), forceName = !0) : options.preferThumb && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length && !1 !== options.inheritThumb && "Episode" === item.Type ? imgUrl = apiClient.getScaledImageUrl(item.ParentBackdropItemId, { - type: "Backdrop", - maxWidth: width, - tag: item.ParentBackdropImageTags[0] - }) : item.ImageTags && item.ImageTags.Primary ? (height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null, imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Primary", - maxHeight: height, - maxWidth: width, - tag: item.ImageTags.Primary - }), options.preferThumb && !1 !== options.showTitle && (forceName = !0), primaryImageAspectRatio && (uiAspect = getDesiredAspect(shape)) && (coverImage = Math.abs(primaryImageAspectRatio - uiAspect) / uiAspect <= .2)) : item.PrimaryImageTag ? (height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null, imgUrl = apiClient.getScaledImageUrl(item.PrimaryImageItemId || item.Id || item.ItemId, { - type: "Primary", - maxHeight: height, - maxWidth: width, - tag: item.PrimaryImageTag - }), options.preferThumb && !1 !== options.showTitle && (forceName = !0), primaryImageAspectRatio && (uiAspect = getDesiredAspect(shape)) && (coverImage = Math.abs(primaryImageAspectRatio - uiAspect) / uiAspect <= .2)) : item.ParentPrimaryImageTag ? imgUrl = apiClient.getScaledImageUrl(item.ParentPrimaryImageItemId, { - type: "Primary", - maxWidth: width, - tag: item.ParentPrimaryImageTag - }) : item.SeriesPrimaryImageTag ? imgUrl = apiClient.getScaledImageUrl(item.SeriesId, { - type: "Primary", - maxWidth: width, - tag: item.SeriesPrimaryImageTag - }) : item.AlbumId && item.AlbumPrimaryImageTag ? (width = primaryImageAspectRatio ? Math.round(height * primaryImageAspectRatio) : null, imgUrl = apiClient.getScaledImageUrl(item.AlbumId, { - type: "Primary", - maxHeight: height, - maxWidth: width, - tag: item.AlbumPrimaryImageTag - }), primaryImageAspectRatio && (uiAspect = getDesiredAspect(shape)) && (coverImage = Math.abs(primaryImageAspectRatio - uiAspect) / uiAspect <= .2)) : "Season" === item.Type && item.ImageTags && item.ImageTags.Thumb ? imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Thumb", - maxWidth: width, - tag: item.ImageTags.Thumb - }) : item.BackdropImageTags && item.BackdropImageTags.length ? imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Backdrop", - maxWidth: width, - tag: item.BackdropImageTags[0] - }) : item.ImageTags && item.ImageTags.Thumb ? imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Thumb", - maxWidth: width, - tag: item.ImageTags.Thumb - }) : item.SeriesThumbImageTag && !1 !== options.inheritThumb ? imgUrl = apiClient.getScaledImageUrl(item.SeriesId, { - type: "Thumb", - maxWidth: width, - tag: item.SeriesThumbImageTag - }) : item.ParentThumbItemId && !1 !== options.inheritThumb ? imgUrl = apiClient.getScaledImageUrl(item.ParentThumbItemId, { - type: "Thumb", - maxWidth: width, - tag: item.ParentThumbImageTag - }) : item.ParentBackdropImageTags && item.ParentBackdropImageTags.length && !1 !== options.inheritThumb && (imgUrl = apiClient.getScaledImageUrl(item.ParentBackdropItemId, { - type: "Backdrop", - maxWidth: width, - tag: item.ParentBackdropImageTags[0] - })), { - imgUrl: imgUrl, - forceName: forceName, - coverImage: coverImage - } - } - - function getRandomInt(min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min - } - - function getDefaultColorIndex(str) { - if (str) { - for (var charIndex = Math.floor(str.length / 2), character = String(str.substr(charIndex, 1).charCodeAt()), sum = 0, i = 0; i < character.length; i++) sum += parseInt(character.charAt(i)); - return String(sum).substr(-1) % numRandomColors + 1 - } - return getRandomInt(1, numRandomColors) - } - - function getCardTextLines(lines, cssClass, forceLines, isOuterFooter, cardLayout, addRightMargin, maxLines) { - var i, length, html = "", - valid = 0; - for (i = 0, length = lines.length; i < length; i++) { - var currentCssClass = cssClass, - text = lines[i]; - if (valid > 0 && isOuterFooter ? currentCssClass += " cardText-secondary" : 0 === valid && isOuterFooter && (currentCssClass += " cardText-first"), addRightMargin && (currentCssClass += " cardText-rightmargin"), text && (html += "
", html += text, html += "
", valid++, maxLines && valid >= maxLines)) break - } - if (forceLines) - for (length = maxLines || Math.min(lines.length, maxLines || lines.length); valid < length;) html += "
 
", valid++; - return html - } - - function isUsingLiveTvNaming(item) { - return "Program" === item.Type || "Timer" === item.Type || "Recording" === item.Type - } - - function getAirTimeText(item, showAirDateTime, showAirEndTime) { - var airTimeText = ""; - if (item.StartDate) try { - var date = datetime.parseISO8601Date(item.StartDate); - showAirDateTime && (airTimeText += datetime.toLocaleDateString(date, { - weekday: "short", - month: "short", - day: "numeric" - }) + " "), airTimeText += datetime.getDisplayTime(date), item.EndDate && showAirEndTime && (date = datetime.parseISO8601Date(item.EndDate), airTimeText += " - " + datetime.getDisplayTime(date)) - } catch (e) { - console.log("Error parsing date: " + item.StartDate) - } - return airTimeText - } - - function getCardFooterText(item, apiClient, options, showTitle, forceName, overlayText, imgUrl, footerClass, progressHtml, logoUrl, isOuterFooter) { - var html = ""; - logoUrl && (html += ''); - var showOtherText = isOuterFooter ? !overlayText : overlayText; - isOuterFooter && options.cardLayout && layoutManager.mobile && "none" !== options.cardFooterAside && (html += ''); - var titleAdded, cssClass = options.centerText ? "cardText cardTextCentered" : "cardText", - lines = [], - parentTitleUnderneath = "MusicAlbum" === item.Type || "Audio" === item.Type || "MusicVideo" === item.Type; - if (showOtherText && (options.showParentTitle || options.showParentTitleOrTitle) && !parentTitleUnderneath) - if (isOuterFooter && "Episode" === item.Type && item.SeriesName) item.SeriesId ? lines.push(getTextActionButton({ - Id: item.SeriesId, - ServerId: item.ServerId, - Name: item.SeriesName, - Type: "Series", - IsFolder: !0 - })) : lines.push(item.SeriesName); - else if (isUsingLiveTvNaming(item)) lines.push(item.Name), item.EpisodeTitle || (titleAdded = !0); - else { - var parentTitle = item.SeriesName || item.Series || item.Album || item.AlbumArtist || item.GameSystem || ""; - (parentTitle || showTitle) && lines.push(parentTitle) - } - var showMediaTitle = showTitle && !titleAdded || options.showParentTitleOrTitle && !lines.length; - if (showMediaTitle || titleAdded || !showTitle && !forceName || (showMediaTitle = !0), showMediaTitle) { - var name = "auto" !== options.showTitle || item.IsFolder || "Photo" !== item.MediaType ? itemHelper.getDisplayName(item, { - includeParentInfo: options.includeParentInfoInTitle - }) : ""; - lines.push(name) - } - if (showOtherText) { - if (options.showParentTitle && parentTitleUnderneath && (isOuterFooter && item.AlbumArtists && item.AlbumArtists.length ? (item.AlbumArtists[0].Type = "MusicArtist", item.AlbumArtists[0].IsFolder = !0, lines.push(getTextActionButton(item.AlbumArtists[0], null, item.ServerId))) : lines.push(isUsingLiveTvNaming(item) ? item.Name : item.SeriesName || item.Series || item.Album || item.AlbumArtist || item.GameSystem || "")), options.showItemCounts) { - var itemCountHtml = getItemCountsHtml(options, item); - lines.push(itemCountHtml) - } - if (options.textLines) - for (var additionalLines = options.textLines(item), i = 0, length = additionalLines.length; i < length; i++) lines.push(additionalLines[i]); - if (options.showSongCount) { - var songLine = ""; - item.SongCount && (songLine = 1 === item.SongCount ? globalize.translate("sharedcomponents#ValueOneSong") : globalize.translate("sharedcomponents#ValueSongCount", item.SongCount)), lines.push(songLine) - } - if (options.showPremiereDate) - if (item.PremiereDate) try { - lines.push(getPremiereDateText(item)) - } catch (err) { - lines.push("") - } else lines.push(""); - (options.showYear || options.showSeriesYear) && ("Series" === item.Type ? "Continuing" === item.Status ? lines.push(globalize.translate("sharedcomponents#SeriesYearToPresent", item.ProductionYear || "")) : item.EndDate && item.ProductionYear ? lines.push(item.ProductionYear + " - " + datetime.parseISO8601Date(item.EndDate).getFullYear()) : lines.push(item.ProductionYear || "") : lines.push(item.ProductionYear || "")), options.showRuntime && (item.RunTimeTicks ? lines.push(datetime.getDisplayRunningTime(item.RunTimeTicks)) : lines.push("")), options.showAirTime && lines.push(getAirTimeText(item, options.showAirDateTime, options.showAirEndTime) || ""), options.showChannelName && (item.ChannelId ? lines.push(getTextActionButton({ - Id: item.ChannelId, - ServerId: item.ServerId, - Name: item.ChannelName, - Type: "TvChannel", - MediaType: item.MediaType, - IsFolder: !1 - }, item.ChannelName)) : lines.push(item.ChannelName || " ")), options.showCurrentProgram && "TvChannel" === item.Type && (item.CurrentProgram ? lines.push(item.CurrentProgram.Name) : lines.push("")), options.showCurrentProgramTime && "TvChannel" === item.Type && (item.CurrentProgram ? lines.push(getAirTimeText(item.CurrentProgram, !1, !0) || "") : lines.push("")), options.showSeriesTimerTime && (item.RecordAnyTime ? lines.push(globalize.translate("sharedcomponents#Anytime")) : lines.push(datetime.getDisplayTime(item.StartDate))), options.showSeriesTimerChannel && (item.RecordAnyChannel ? lines.push(globalize.translate("sharedcomponents#AllChannels")) : lines.push(item.ChannelName || globalize.translate("sharedcomponents#OneChannel"))), options.showPersonRoleOrType && (item.Role ? lines.push("as " + item.Role) : item.Type ? lines.push(globalize.translate("sharedcomponents#" + item.Type)) : lines.push("")) - }(showTitle || !imgUrl) && forceName && overlayText && 1 === lines.length && (lines = []); - var addRightTextMargin = isOuterFooter && options.cardLayout && !options.centerText && "none" !== options.cardFooterAside && layoutManager.mobile; - return html += getCardTextLines(lines, cssClass, !options.overlayText, isOuterFooter, options.cardLayout, addRightTextMargin, options.lines), progressHtml && (html += progressHtml), html && (!isOuterFooter || logoUrl || options.cardLayout) && (html = '
' + html, html += "
"), html - } - - function getTextActionButton(item, text, serverId) { - if (text || (text = itemHelper.getDisplayName(item)), layoutManager.tv) return text; - var html = "" - } - - function getItemCountsHtml(options, item) { - var childText, counts = []; - if ("Playlist" === item.Type) { - if (childText = "", item.RunTimeTicks) { - var minutes = item.RunTimeTicks / 6e8; - minutes = minutes || 1, childText += globalize.translate("sharedcomponents#ValueMinutes", Math.round(minutes)) - } else childText += globalize.translate("sharedcomponents#ValueMinutes", 0); - counts.push(childText) - } else "Genre" === item.Type || "Studio" === item.Type ? (item.MovieCount && (childText = 1 === item.MovieCount ? globalize.translate("sharedcomponents#ValueOneMovie") : globalize.translate("sharedcomponents#ValueMovieCount", item.MovieCount), counts.push(childText)), item.SeriesCount && (childText = 1 === item.SeriesCount ? globalize.translate("sharedcomponents#ValueOneSeries") : globalize.translate("sharedcomponents#ValueSeriesCount", item.SeriesCount), counts.push(childText)), item.EpisodeCount && (childText = 1 === item.EpisodeCount ? globalize.translate("sharedcomponents#ValueOneEpisode") : globalize.translate("sharedcomponents#ValueEpisodeCount", item.EpisodeCount), counts.push(childText)), item.GameCount && (childText = 1 === item.GameCount ? globalize.translate("sharedcomponents#ValueOneGame") : globalize.translate("sharedcomponents#ValueGameCount", item.GameCount), counts.push(childText))) : "GameGenre" === item.Type ? item.GameCount && (childText = 1 === item.GameCount ? globalize.translate("sharedcomponents#ValueOneGame") : globalize.translate("sharedcomponents#ValueGameCount", item.GameCount), counts.push(childText)) : "MusicGenre" === item.Type || "MusicArtist" === options.context ? (item.AlbumCount && (childText = 1 === item.AlbumCount ? globalize.translate("sharedcomponents#ValueOneAlbum") : globalize.translate("sharedcomponents#ValueAlbumCount", item.AlbumCount), counts.push(childText)), item.SongCount && (childText = 1 === item.SongCount ? globalize.translate("sharedcomponents#ValueOneSong") : globalize.translate("sharedcomponents#ValueSongCount", item.SongCount), counts.push(childText)), item.MusicVideoCount && (childText = 1 === item.MusicVideoCount ? globalize.translate("sharedcomponents#ValueOneMusicVideo") : globalize.translate("sharedcomponents#ValueMusicVideoCount", item.MusicVideoCount), counts.push(childText))) : "Series" === item.Type && (childText = 1 === item.RecursiveItemCount ? globalize.translate("sharedcomponents#ValueOneEpisode") : globalize.translate("sharedcomponents#ValueEpisodeCount", item.RecursiveItemCount), counts.push(childText)); - return counts.join(", ") - } - - function requireRefreshIndicator() { - refreshIndicatorLoaded || (refreshIndicatorLoaded = !0, require(["emby-itemrefreshindicator"])) - } - - function getDefaultBackgroundClass(str) { - return "defaultCardBackground defaultCardBackground" + getDefaultColorIndex(str) - } - - function buildCard(index, item, apiClient, options) { - var action = options.action || "link"; - "play" === action && item.IsFolder ? action = "link" : "Photo" === item.MediaType && (action = "play"); - var shape = options.shape; - if ("mixed" === shape) { - shape = null; - var primaryImageAspectRatio = item.PrimaryImageAspectRatio; - primaryImageAspectRatio && (shape = primaryImageAspectRatio >= 1.33 ? "mixedBackdrop" : primaryImageAspectRatio > .71 ? "mixedSquare" : "mixedPortrait"), shape = shape || "mixedSquare" - } - var className = "card"; - shape && (className += " " + shape + "Card"), options.cardCssClass && (className += " " + options.cardCssClass), options.cardClass && (className += " " + options.cardClass), layoutManager.desktop && (className += " card-hoverable"), enableFocusTransfrom && layoutManager.tv || (className += " card-nofocustransform"); - var imgInfo = getCardImageUrl(item, apiClient, options, shape), - imgUrl = imgInfo.imgUrl, - forceName = imgInfo.forceName, - showTitle = "auto" === options.showTitle || (options.showTitle || "PhotoAlbum" === item.Type || "Folder" === item.Type), - overlayText = options.overlayText; - forceName && !options.cardLayout && null == overlayText && (overlayText = !0); - var cardImageContainerClass = "cardImageContainer"; - (options.coverImage || imgInfo.coverImage) && (cardImageContainerClass += " coveredImage", ("Photo" === item.MediaType || "PhotoAlbum" === item.Type || "Folder" === item.Type || item.ProgramInfo || "Program" === item.Type || "Recording" === item.Type) && (cardImageContainerClass += " coveredImage-noScale")), imgUrl || (cardImageContainerClass += " " + getDefaultBackgroundClass(item.Name)); - var cardBoxClass = options.cardLayout ? "cardBox visualCardBox" : "cardBox"; - layoutManager.tv && (cardBoxClass += enableFocusTransfrom ? " cardBox-focustransform cardBox-withfocuscontent" : " cardBox-withfocuscontent-large", options.cardLayout && (cardBoxClass += " card-focuscontent", enableFocusTransfrom || (cardBoxClass += " card-focuscontent-large"))); - var footerCssClass, logoUrl, progressHtml = indicators.getProgressBarHtml(item), - innerCardFooter = "", - footerOverlayed = !1; - options.showChannelLogo && item.ChannelPrimaryImageTag ? logoUrl = apiClient.getScaledImageUrl(item.ChannelId, { - type: "Primary", - height: 40, - tag: item.ChannelPrimaryImageTag - }) : options.showLogo && item.ParentLogoImageTag && (logoUrl = apiClient.getScaledImageUrl(item.ParentLogoItemId, { - type: "Logo", - height: 40, - tag: item.ParentLogoImageTag - })), overlayText ? (logoUrl = null, footerCssClass = progressHtml ? "innerCardFooter fullInnerCardFooter" : "innerCardFooter", innerCardFooter += getCardFooterText(item, apiClient, options, showTitle, forceName, overlayText, imgUrl, footerCssClass, progressHtml, logoUrl, !1), footerOverlayed = !0) : progressHtml && (innerCardFooter += '
', innerCardFooter += progressHtml, innerCardFooter += "
", progressHtml = ""); - var mediaSourceCount = item.MediaSourceCount || 1; - mediaSourceCount > 1 && (innerCardFooter += '
' + mediaSourceCount + "
"); - var outerCardFooter = ""; - overlayText || footerOverlayed || (footerCssClass = options.cardLayout ? "cardFooter" : "cardFooter cardFooter-transparent", logoUrl && (footerCssClass += " cardFooter-withlogo"), options.cardLayout || (logoUrl = null), outerCardFooter = getCardFooterText(item, apiClient, options, showTitle, forceName, overlayText, imgUrl, footerCssClass, progressHtml, logoUrl, !0)), outerCardFooter && !options.cardLayout && (cardBoxClass += " cardBox-bottompadded"); - var overlayButtons = ""; - if (layoutManager.mobile) { - var overlayPlayButton = options.overlayPlayButton; - null != overlayPlayButton || options.overlayMoreButton || options.overlayInfoButton || options.cardLayout || (overlayPlayButton = "Video" === item.MediaType); - var btnCssClass = "cardOverlayButton cardOverlayButton-br itemAction"; - options.centerPlayButton && (overlayButtons += ''), !overlayPlayButton || item.IsPlaceHolder || "Virtual" === item.LocationType && item.MediaType && "Program" !== item.Type || "Person" === item.Type || (overlayButtons += ''), options.overlayMoreButton && (overlayButtons += '') - } - options.showChildCountIndicator && item.ChildCount && (className += " groupedCard"); - var cardImageContainerOpen, cardImageContainerClose = "", - cardBoxClose = "", - cardScalableClose = "", - cardContentClass = "cardContent"; - options.cardLayout || (cardContentClass += " cardContent-shadow"), layoutManager.tv ? (cardImageContainerOpen = imgUrl ? '
' : '
', cardImageContainerClose = "
") : (cardImageContainerOpen = imgUrl ? '"); - var cardScalableClass = "cardScalable"; - layoutManager.tv && !options.cardLayout && (cardScalableClass += " card-focuscontent", enableFocusTransfrom || (cardScalableClass += " card-focuscontent-large")), cardImageContainerOpen = '
' + cardImageContainerOpen, cardBoxClose = "
", cardScalableClose = "
"; - var indicatorsHtml = ""; - if (!1 !== options.missingIndicator && (indicatorsHtml += indicators.getMissingIndicator(item)), indicatorsHtml += indicators.getSyncIndicator(item), indicatorsHtml += indicators.getTimerIndicator(item), indicatorsHtml += indicators.getTypeIndicator(item), options.showGroupCount ? indicatorsHtml += indicators.getChildCountIndicatorHtml(item, { - minCount: 1 - }) : indicatorsHtml += indicators.getPlayedIndicatorHtml(item), "CollectionFolder" === item.Type || item.CollectionType) { - indicatorsHtml += '
', requireRefreshIndicator() - } - indicatorsHtml && (cardImageContainerOpen += '
' + indicatorsHtml + "
"), imgUrl || (cardImageContainerOpen += getCardDefaultText(item, options)); - var tagName = layoutManager.tv && !overlayButtons ? "button" : "div", - nameWithPrefix = item.SortName || item.Name || "", - prefix = nameWithPrefix.substring(0, Math.min(3, nameWithPrefix.length)); - prefix && (prefix = prefix.toUpperCase()); - var timerAttributes = ""; - item.TimerId && (timerAttributes += ' data-timerid="' + item.TimerId + '"'), item.SeriesTimerId && (timerAttributes += ' data-seriestimerid="' + item.SeriesTimerId + '"'); - var actionAttribute; - "button" === tagName ? (className += " itemAction", actionAttribute = ' data-action="' + action + '"') : actionAttribute = "", "MusicAlbum" !== item.Type && "MusicArtist" !== item.Type && "Audio" !== item.Type && (className += " card-withuserdata"); - var positionTicksData = item.UserData && item.UserData.PlaybackPositionTicks ? ' data-positionticks="' + item.UserData.PlaybackPositionTicks + '"' : "", - collectionIdData = options.collectionId ? ' data-collectionid="' + options.collectionId + '"' : "", - playlistIdData = options.playlistId ? ' data-playlistid="' + options.playlistId + '"' : "", - mediaTypeData = item.MediaType ? ' data-mediatype="' + item.MediaType + '"' : "", - collectionTypeData = item.CollectionType ? ' data-collectiontype="' + item.CollectionType + '"' : "", - channelIdData = item.ChannelId ? ' data-channelid="' + item.ChannelId + '"' : "", - contextData = options.context ? ' data-context="' + options.context + '"' : "", - parentIdData = options.parentId ? ' data-parentid="' + options.parentId + '"' : "", - additionalCardContent = ""; - return layoutManager.desktop && (additionalCardContent += getHoverMenuHtml(item, action)), "<" + tagName + ' data-index="' + index + '"' + timerAttributes + actionAttribute + ' data-isfolder="' + (item.IsFolder || !1) + '" data-serverid="' + (item.ServerId || options.serverId) + '" data-id="' + (item.Id || item.ItemId) + '" data-type="' + item.Type + '"' + mediaTypeData + collectionTypeData + channelIdData + positionTicksData + collectionIdData + playlistIdData + contextData + parentIdData + ' data-prefix="' + prefix + '" class="' + className + '">' + cardImageContainerOpen + innerCardFooter + cardImageContainerClose + overlayButtons + additionalCardContent + cardScalableClose + outerCardFooter + cardBoxClose + "" - } - - function getHoverMenuHtml(item, action) { - var html = ""; - html += '
'; - var btnCssClass = "cardOverlayButton cardOverlayButton-hover itemAction"; - playbackManager.canPlay(item) && (html += ''), html += '
'; - var userData = item.UserData || {}; - if (itemHelper.canMarkPlayed(item) && (require(["emby-playstatebutton"]), html += ''), itemHelper.canRate(item)) { - var likes = null == userData.Likes ? "" : userData.Likes; - require(["emby-ratingbutton"]), html += '' - } - return html += '', html += "
", html += "
" - } - - function getCardDefaultText(item, options) { - var collectionType = item.CollectionType; - return "livetv" === collectionType ? '' : "homevideos" === collectionType || "photos" === collectionType ? '' : "music" === collectionType ? '' : "MusicAlbum" === item.Type ? '' : "MusicArtist" === item.Type || "Person" === item.Type ? '' : options.defaultCardImageIcon ? '' + options.defaultCardImageIcon + "" : '
' + (isUsingLiveTvNaming(item) ? item.Name : itemHelper.getDisplayName(item)) + "
" - } - - function buildCards(items, options) { - if (document.body.contains(options.itemsContainer)) { - if (options.parentContainer) { - if (!items.length) return void options.parentContainer.classList.add("hide"); - options.parentContainer.classList.remove("hide") - } var html = buildCardsHtmlInternal(items, options); - html ? (options.itemsContainer.cardBuilderHtml !== html && (options.itemsContainer.innerHTML = html, items.length < 50 ? options.itemsContainer.cardBuilderHtml = html : options.itemsContainer.cardBuilderHtml = null), imageLoader.lazyChildren(options.itemsContainer)) : (options.itemsContainer.innerHTML = html, options.itemsContainer.cardBuilderHtml = null), options.autoFocus && focusManager.autoFocus(options.itemsContainer, !0) - } - } - function ensureIndicators(card, indicatorsElem) { - if (indicatorsElem) return indicatorsElem; - if (!(indicatorsElem = card.querySelector(".cardIndicators"))) { - var cardImageContainer = card.querySelector(".cardImageContainer"); - indicatorsElem = document.createElement("div"), - indicatorsElem.classList.add("cardIndicators"), cardImageContainer.appendChild(indicatorsElem) + return html; } - return indicatorsElem - } - function updateUserData(card, userData) { - var type = card.getAttribute("data-type"), - enableCountIndicator = "Series" === type || "BoxSet" === type || "Season" === type, - indicatorsElem = null, - playedIndicator = null, - countIndicator = null, - itemProgressBar = null; - userData.Played ? (playedIndicator = card.querySelector(".playedIndicator"), playedIndicator || (playedIndicator = document.createElement("div"), playedIndicator.classList.add("playedIndicator"), playedIndicator.classList.add("indicator"), indicatorsElem = ensureIndicators(card, indicatorsElem), indicatorsElem.appendChild(playedIndicator)), playedIndicator.innerHTML = '') : (playedIndicator = card.querySelector(".playedIndicator")) && playedIndicator.parentNode.removeChild(playedIndicator), userData.UnplayedItemCount ? (countIndicator = card.querySelector(".countIndicator"), countIndicator || (countIndicator = document.createElement("div"), countIndicator.classList.add("countIndicator"), indicatorsElem = ensureIndicators(card, indicatorsElem), indicatorsElem.appendChild(countIndicator)), countIndicator.innerHTML = userData.UnplayedItemCount) : enableCountIndicator && (countIndicator = card.querySelector(".countIndicator")) && countIndicator.parentNode.removeChild(countIndicator); - var progressHtml = indicators.getProgressBarHtml({ - Type: type, - UserData: userData, - MediaType: "Video" - }); - if (progressHtml) { - if (!(itemProgressBar = card.querySelector(".itemProgressBar"))) { - itemProgressBar = document.createElement("div"), itemProgressBar.classList.add("itemProgressBar"); - var innerCardFooter = card.querySelector(".innerCardFooter"); - if (!innerCardFooter) { - innerCardFooter = document.createElement("div"), innerCardFooter.classList.add("innerCardFooter"); - card.querySelector(".cardImageContainer").appendChild(innerCardFooter) + function getPostersPerRow(shape, screenWidth, isOrientationLandscape) { + + switch (shape) { + + case 'portrait': + if (layoutManager.tv) { + return 100 / 16.66666667; + } + if (screenWidth >= 2200) { + return 100 / 10; + } + if (screenWidth >= 1920) { + return 100 / 11.1111111111; + } + if (screenWidth >= 1600) { + return 100 / 12.5; + } + if (screenWidth >= 1400) { + return 100 / 14.28571428571; + } + if (screenWidth >= 1200) { + return 100 / 16.66666667; + } + if (screenWidth >= 800) { + return 5; + } + if (screenWidth >= 700) { + return 4; + } + if (screenWidth >= 500) { + return 100 / 33.33333333; + } + return 100 / 33.33333333; + case 'square': + if (layoutManager.tv) { + return 100 / 16.66666667; + } + if (screenWidth >= 2200) { + return 100 / 10; + } + if (screenWidth >= 1920) { + return 100 / 11.1111111111; + } + if (screenWidth >= 1600) { + return 100 / 12.5; + } + if (screenWidth >= 1400) { + return 100 / 14.28571428571; + } + if (screenWidth >= 1200) { + return 100 / 16.66666667; + } + if (screenWidth >= 800) { + return 5; + } + if (screenWidth >= 700) { + return 4; + } + if (screenWidth >= 500) { + return 100 / 33.33333333; + } + return 2; + case 'banner': + if (screenWidth >= 2200) { + return 100 / 25; + } + if (screenWidth >= 1200) { + return 100 / 33.33333333; + } + if (screenWidth >= 800) { + return 2; + } + return 1; + case 'backdrop': + if (layoutManager.tv) { + return 100 / 25; + } + if (screenWidth >= 2500) { + return 6; + } + if (screenWidth >= 1600) { + return 5; + } + if (screenWidth >= 1200) { + return 4; + } + if (screenWidth >= 770) { + return 3; + } + if (screenWidth >= 420) { + return 2; + } + return 1; + case 'smallBackdrop': + if (screenWidth >= 1600) { + return 100 / 12.5; + } + if (screenWidth >= 1400) { + return 100 / 14.2857142857; + } + if (screenWidth >= 1200) { + return 100 / 16.666666666666666666; + } + if (screenWidth >= 1000) { + return 5; + } + if (screenWidth >= 800) { + return 4; + } + if (screenWidth >= 500) { + return 100 / 33.33333333; + } + return 2; + case 'overflowSmallBackdrop': + if (layoutManager.tv) { + return 100 / 18.9; + } + if (isOrientationLandscape) { + if (screenWidth >= 800) { + return 100 / 15.5; + } + return 100 / 23.3; + } else { + if (screenWidth >= 540) { + return 100 / 30; + } + return 100 / 72; + } + break; + case 'overflowPortrait': + + if (layoutManager.tv) { + return 100 / 15.5; + } + if (isOrientationLandscape) { + if (screenWidth >= 1700) { + return 100 / 11.6; + } + return 100 / 15.5; + } else { + if (screenWidth >= 1400) { + return 100 / 15; + } + if (screenWidth >= 1200) { + return 100 / 18; + } + if (screenWidth >= 760) { + return 100 / 23; + } + if (screenWidth >= 400) { + return 100 / 31.5; + } + return 100 / 42; + } + break; + case 'overflowSquare': + if (layoutManager.tv) { + return 100 / 15.5; + } + if (isOrientationLandscape) { + if (screenWidth >= 1700) { + return 100 / 11.6; + } + return 100 / 15.5; + } else { + if (screenWidth >= 1400) { + return 100 / 15; + } + if (screenWidth >= 1200) { + return 100 / 18; + } + if (screenWidth >= 760) { + return 100 / 23; + } + if (screenWidth >= 540) { + return 100 / 31.5; + } + return 100 / 42; + } + break; + case 'overflowBackdrop': + if (layoutManager.tv) { + return 100 / 23.3; + } + if (isOrientationLandscape) { + if (screenWidth >= 1700) { + return 100 / 18.5; + } + return 100 / 23.3; + } else { + if (screenWidth >= 1800) { + return 100 / 23.5; + } + if (screenWidth >= 1400) { + return 100 / 30; + } + if (screenWidth >= 760) { + return 100 / 40; + } + if (screenWidth >= 640) { + return 100 / 56; + } + return 100 / 72; + } + break; + default: + return 4; + } + } + + function isResizable(windowWidth) { + + var screen = window.screen; + if (screen) { + var screenWidth = screen.availWidth; + + if ((screenWidth - windowWidth) > 20) { + return true; } - innerCardFooter.appendChild(itemProgressBar) } - itemProgressBar.innerHTML = progressHtml - } else(itemProgressBar = card.querySelector(".itemProgressBar")) && itemProgressBar.parentNode.removeChild(itemProgressBar) - } - function onUserDataChanged(userData, scope) { - for (var cards = (scope || document.body).querySelectorAll('.card-withuserdata[data-id="' + userData.ItemId + '"]'), i = 0, length = cards.length; i < length; i++) updateUserData(cards[i], userData) - } + return false; + } - function onTimerCreated(programId, newTimerId, itemsContainer) { - for (var cells = itemsContainer.querySelectorAll('.card[data-id="' + programId + '"]'), i = 0, length = cells.length; i < length; i++) { - var cell = cells[i]; - if (!cell.querySelector(".timerIndicator")) { - ensureIndicators(cell).insertAdjacentHTML("beforeend", '') + function getImageWidth(shape, screenWidth, isOrientationLandscape) { + + //console.log(screenWidth); + var imagesPerRow = getPostersPerRow(shape, screenWidth, isOrientationLandscape); + //console.log(shape + '--' + imagesPerRow); + + var shapeWidth = screenWidth / imagesPerRow; + + return Math.round(shapeWidth); + } + + function setCardData(items, options) { + + options.shape = options.shape || "auto"; + + var primaryImageAspectRatio = imageLoader.getPrimaryImageAspectRatio(items); + + if (options.shape === 'auto' || options.shape === 'autohome' || options.shape === 'autooverflow' || options.shape === 'autoVertical') { + + var requestedShape = options.shape; + options.shape = null; + + if (primaryImageAspectRatio) { + + if (primaryImageAspectRatio >= 3) { + options.shape = 'banner'; + options.coverImage = true; + } else if (primaryImageAspectRatio >= 1.33) { + options.shape = requestedShape === 'autooverflow' ? 'overflowBackdrop' : 'backdrop'; + } else if (primaryImageAspectRatio > 0.71) { + options.shape = requestedShape === 'autooverflow' ? 'overflowSquare' : 'square'; + } else { + options.shape = requestedShape === 'autooverflow' ? 'overflowPortrait' : 'portrait'; + } + } + + if (!options.shape) { + options.shape = options.defaultShape || (requestedShape === 'autooverflow' ? 'overflowSquare' : 'square'); + } } - cell.setAttribute("data-timerid", newTimerId) - } - } - function onTimerCancelled(id, itemsContainer) { - for (var cells = itemsContainer.querySelectorAll('.card[data-timerid="' + id + '"]'), i = 0, length = cells.length; i < length; i++) { - var cell = cells[i], - icon = cell.querySelector(".timerIndicator"); - icon && icon.parentNode.removeChild(icon), cell.removeAttribute("data-timerid") - } - } + if (options.preferThumb === 'auto') { + options.preferThumb = options.shape === 'backdrop' || options.shape === 'overflowBackdrop'; + } - function onSeriesTimerCancelled(id, itemsContainer) { - for (var cells = itemsContainer.querySelectorAll('.card[data-seriestimerid="' + id + '"]'), i = 0, length = cells.length; i < length; i++) { - var cell = cells[i], - icon = cell.querySelector(".timerIndicator"); - icon && icon.parentNode.removeChild(icon), cell.removeAttribute("data-seriestimerid") + options.uiAspect = getDesiredAspect(options.shape); + options.primaryImageAspectRatio = primaryImageAspectRatio; + + if (!options.width && options.widths) { + options.width = options.widths[options.shape]; + } + + if (options.rows && typeof (options.rows) !== 'number') { + options.rows = options.rows[options.shape]; + } + + if (!options.width) { + var screenWidth = dom.getWindowSize().innerWidth; + var screenHeight = dom.getWindowSize().innerHeight; + + if (isResizable(screenWidth)) { + var roundScreenTo = 100; + screenWidth = Math.floor(screenWidth / roundScreenTo) * roundScreenTo; + } + + options.width = getImageWidth(options.shape, screenWidth, screenWidth > (screenHeight * 1.3)); + } } - } - var refreshIndicatorLoaded, enableFocusTransfrom = (window.devicePixelRatio, !browser.slow && !browser.edge), - numRandomColors = 5; - return { - getCardsHtml: getCardsHtml, - buildCards: buildCards, - onUserDataChanged: onUserDataChanged, - onTimerCreated: onTimerCreated, - onTimerCancelled: onTimerCancelled, - onSeriesTimerCancelled: onSeriesTimerCancelled - } -}); \ No newline at end of file + + function buildCardsHtmlInternal(items, options) { + + var isVertical; + + if (options.shape === 'autoVertical') { + isVertical = true; + } + + setCardData(items, options); + + var html = ''; + var itemsInRow = 0; + + var currentIndexValue; + var hasOpenRow; + var hasOpenSection; + + var sectionTitleTagName = options.sectionTitleTagName || 'div'; + var apiClient; + var lastServerId; + + var i, length; + + for (i = 0, length = items.length; i < length; i++) { + + var item = items[i]; + var serverId = item.ServerId || options.serverId; + + if (serverId !== lastServerId) { + lastServerId = serverId; + apiClient = connectionManager.getApiClient(lastServerId); + } + + if (options.indexBy) { + var newIndexValue = ''; + + if (options.indexBy === 'PremiereDate') { + if (item.PremiereDate) { + try { + + newIndexValue = datetime.toLocaleDateString(datetime.parseISO8601Date(item.PremiereDate), { weekday: 'long', month: 'long', day: 'numeric' }); + + } catch (err) { + } + } + } + + else if (options.indexBy === 'ProductionYear') { + newIndexValue = item.ProductionYear; + } + + else if (options.indexBy === 'CommunityRating') { + newIndexValue = item.CommunityRating ? (Math.floor(item.CommunityRating) + (item.CommunityRating % 1 >= 0.5 ? 0.5 : 0)) + '+' : null; + } + + if (newIndexValue !== currentIndexValue) { + + if (hasOpenRow) { + html += '
'; + hasOpenRow = false; + itemsInRow = 0; + } + + if (hasOpenSection) { + + html += ''; + + if (isVertical) { + html += ''; + } + hasOpenSection = false; + } + + if (isVertical) { + html += '
'; + } else { + html += '
'; + } + html += '<' + sectionTitleTagName + ' class="sectionTitle">' + newIndexValue + ''; + if (isVertical) { + html += '
'; + } + currentIndexValue = newIndexValue; + hasOpenSection = true; + } + } + + if (options.rows && itemsInRow === 0) { + + if (hasOpenRow) { + html += '
'; + hasOpenRow = false; + } + + html += '
'; + hasOpenRow = true; + } + + html += buildCard(i, item, apiClient, options); + + itemsInRow++; + + if (options.rows && itemsInRow >= options.rows) { + html += '
'; + hasOpenRow = false; + itemsInRow = 0; + } + } + + if (hasOpenRow) { + html += '
'; + } + + if (hasOpenSection) { + html += '
'; + + if (isVertical) { + html += ''; + } + } + + var cardFooterHtml = ''; + for (i = 0, length = (options.lines || 0); i < length; i++) { + + if (i === 0) { + cardFooterHtml += '
 
'; + } else { + cardFooterHtml += '
 
'; + } + } + + return html; + } + + function getDesiredAspect(shape) { + + if (shape) { + shape = shape.toLowerCase(); + if (shape.indexOf('portrait') !== -1) { + return (2 / 3); + } + if (shape.indexOf('backdrop') !== -1) { + return (16 / 9); + } + if (shape.indexOf('square') !== -1) { + return 1; + } + if (shape.indexOf('banner') !== -1) { + return (1000 / 185); + } + } + return null; + } + + function getCardImageUrl(item, apiClient, options, shape) { + + var imageItem = item.ProgramInfo || item; + item = imageItem; + + var width = options.width; + var height = null; + var primaryImageAspectRatio = item.PrimaryImageAspectRatio; + var forceName = false; + var imgUrl = null; + var coverImage = false; + var uiAspect = null; + + if (options.preferThumb && item.ImageTags && item.ImageTags.Thumb) { + + imgUrl = apiClient.getScaledImageUrl(item.Id, { + type: "Thumb", + maxWidth: width, + tag: item.ImageTags.Thumb + }); + + } else if ((options.preferBanner || shape === 'banner') && item.ImageTags && item.ImageTags.Banner) { + + imgUrl = apiClient.getScaledImageUrl(item.Id, { + type: "Banner", + maxWidth: width, + tag: item.ImageTags.Banner + }); + + } else if (options.preferDisc && item.ImageTags && item.ImageTags.Disc) { + + imgUrl = apiClient.getScaledImageUrl(item.Id, { + type: "Disc", + maxWidth: width, + tag: item.ImageTags.Disc + }); + + } else if (options.preferLogo && item.ImageTags && item.ImageTags.Logo) { + + imgUrl = apiClient.getScaledImageUrl(item.Id, { + type: "Logo", + maxWidth: width, + tag: item.ImageTags.Logo + }); + + } else if (options.preferLogo && item.ParentLogoImageTag && item.ParentLogoItemId) { + + imgUrl = apiClient.getScaledImageUrl(item.ParentLogoItemId, { + type: "Logo", + maxWidth: width, + tag: item.ParentLogoImageTag + }); + + } else if (options.preferThumb && item.SeriesThumbImageTag && options.inheritThumb !== false) { + + imgUrl = apiClient.getScaledImageUrl(item.SeriesId, { + type: "Thumb", + maxWidth: width, + tag: item.SeriesThumbImageTag + }); + + } else if (options.preferThumb && item.ParentThumbItemId && options.inheritThumb !== false && item.MediaType !== 'Photo') { + + imgUrl = apiClient.getScaledImageUrl(item.ParentThumbItemId, { + type: "Thumb", + maxWidth: width, + tag: item.ParentThumbImageTag + }); + + } else if (options.preferThumb && item.BackdropImageTags && item.BackdropImageTags.length) { + + imgUrl = apiClient.getScaledImageUrl(item.Id, { + type: "Backdrop", + maxWidth: width, + tag: item.BackdropImageTags[0] + }); + + forceName = true; + + } else if (options.preferThumb && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length && options.inheritThumb !== false && item.Type === 'Episode') { + + imgUrl = apiClient.getScaledImageUrl(item.ParentBackdropItemId, { + type: "Backdrop", + maxWidth: width, + tag: item.ParentBackdropImageTags[0] + }); + + } else if (item.ImageTags && item.ImageTags.Primary) { + + height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null; + + imgUrl = apiClient.getScaledImageUrl(item.Id, { + type: "Primary", + maxHeight: height, + maxWidth: width, + tag: item.ImageTags.Primary + }); + + if (options.preferThumb && options.showTitle !== false) { + forceName = true; + } + + if (primaryImageAspectRatio) { + uiAspect = getDesiredAspect(shape); + if (uiAspect) { + coverImage = (Math.abs(primaryImageAspectRatio - uiAspect) / uiAspect) <= 0.2; + } + } + + } else if (item.PrimaryImageTag) { + + height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null; + + imgUrl = apiClient.getScaledImageUrl(item.PrimaryImageItemId || item.Id || item.ItemId, { + type: "Primary", + maxHeight: height, + maxWidth: width, + tag: item.PrimaryImageTag + }); + + if (options.preferThumb && options.showTitle !== false) { + forceName = true; + } + + if (primaryImageAspectRatio) { + uiAspect = getDesiredAspect(shape); + if (uiAspect) { + coverImage = (Math.abs(primaryImageAspectRatio - uiAspect) / uiAspect) <= 0.2; + } + } + } + else if (item.ParentPrimaryImageTag) { + + imgUrl = apiClient.getScaledImageUrl(item.ParentPrimaryImageItemId, { + type: "Primary", + maxWidth: width, + tag: item.ParentPrimaryImageTag + }); + } + else if (item.SeriesPrimaryImageTag) { + + imgUrl = apiClient.getScaledImageUrl(item.SeriesId, { + type: "Primary", + maxWidth: width, + tag: item.SeriesPrimaryImageTag + }); + } + else if (item.AlbumId && item.AlbumPrimaryImageTag) { + + width = primaryImageAspectRatio ? Math.round(height * primaryImageAspectRatio) : null; + + imgUrl = apiClient.getScaledImageUrl(item.AlbumId, { + type: "Primary", + maxHeight: height, + maxWidth: width, + tag: item.AlbumPrimaryImageTag + }); + + if (primaryImageAspectRatio) { + uiAspect = getDesiredAspect(shape); + if (uiAspect) { + coverImage = (Math.abs(primaryImageAspectRatio - uiAspect) / uiAspect) <= 0.2; + } + } + } + else if (item.Type === 'Season' && item.ImageTags && item.ImageTags.Thumb) { + + imgUrl = apiClient.getScaledImageUrl(item.Id, { + type: "Thumb", + maxWidth: width, + tag: item.ImageTags.Thumb + }); + + } + else if (item.BackdropImageTags && item.BackdropImageTags.length) { + + imgUrl = apiClient.getScaledImageUrl(item.Id, { + type: "Backdrop", + maxWidth: width, + tag: item.BackdropImageTags[0] + }); + + } else if (item.ImageTags && item.ImageTags.Thumb) { + + imgUrl = apiClient.getScaledImageUrl(item.Id, { + type: "Thumb", + maxWidth: width, + tag: item.ImageTags.Thumb + }); + + } else if (item.SeriesThumbImageTag && options.inheritThumb !== false) { + + imgUrl = apiClient.getScaledImageUrl(item.SeriesId, { + type: "Thumb", + maxWidth: width, + tag: item.SeriesThumbImageTag + }); + + } else if (item.ParentThumbItemId && options.inheritThumb !== false) { + + imgUrl = apiClient.getScaledImageUrl(item.ParentThumbItemId, { + type: "Thumb", + maxWidth: width, + tag: item.ParentThumbImageTag + }); + + } else if (item.ParentBackdropImageTags && item.ParentBackdropImageTags.length && options.inheritThumb !== false) { + + imgUrl = apiClient.getScaledImageUrl(item.ParentBackdropItemId, { + type: "Backdrop", + maxWidth: width, + tag: item.ParentBackdropImageTags[0] + }); + + } + + return { + imgUrl: imgUrl, + forceName: forceName, + coverImage: coverImage + }; + } + + function getRandomInt(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; + } + + var numRandomColors = 5; + function getDefaultColorIndex(str) { + + if (str) { + var charIndex = Math.floor(str.length / 2); + var character = String(str.substr(charIndex, 1).charCodeAt()); + var sum = 0; + for (var i = 0; i < character.length; i++) { + sum += parseInt(character.charAt(i)); + } + var index = String(sum).substr(-1); + + return (index % numRandomColors) + 1; + } else { + return getRandomInt(1, numRandomColors); + } + } + + function getCardTextLines(lines, cssClass, forceLines, isOuterFooter, cardLayout, addRightMargin, maxLines) { + + var html = ''; + + var valid = 0; + var i, length; + + for (i = 0, length = lines.length; i < length; i++) { + + var currentCssClass = cssClass; + var text = lines[i]; + + if (valid > 0 && isOuterFooter) { + currentCssClass += ' cardText-secondary'; + } else if (valid === 0 && isOuterFooter) { + currentCssClass += ' cardText-first'; + } + + if (addRightMargin) { + currentCssClass += ' cardText-rightmargin'; + } + + if (text) { + html += "
"; + html += text; + html += "
"; + valid++; + + if (maxLines && valid >= maxLines) { + break; + } + } + } + + if (forceLines) { + + length = maxLines || Math.min(lines.length, maxLines || lines.length); + + while (valid < length) { + html += "
 
"; + valid++; + } + } + + return html; + } + + function isUsingLiveTvNaming(item) { + return item.Type === 'Program' || item.Type === 'Timer' || item.Type === 'Recording'; + } + + function getAirTimeText(item, showAirDateTime, showAirEndTime) { + + var airTimeText = ''; + if (item.StartDate) { + + try { + var date = datetime.parseISO8601Date(item.StartDate); + + if (showAirDateTime) { + airTimeText += datetime.toLocaleDateString(date, { weekday: 'short', month: 'short', day: 'numeric' }) + ' '; + } + + airTimeText += datetime.getDisplayTime(date); + + if (item.EndDate && showAirEndTime) { + date = datetime.parseISO8601Date(item.EndDate); + airTimeText += ' - ' + datetime.getDisplayTime(date); + } + } + catch (e) { + console.log("Error parsing date: " + item.StartDate); + } + } + + return airTimeText; + } + + function getCardFooterText(item, apiClient, options, showTitle, forceName, overlayText, imgUrl, footerClass, progressHtml, logoUrl, isOuterFooter) { + + var html = ''; + + if (logoUrl) { + html += ''; + } + + var showOtherText = isOuterFooter ? !overlayText : overlayText; + + if (isOuterFooter && options.cardLayout && layoutManager.mobile) { + + if (options.cardFooterAside !== 'none') { + html += ''; + } + } + + var cssClass = options.centerText ? "cardText cardTextCentered" : "cardText"; + var serverId = item.ServerId || options.serverId; + + var lines = []; + var parentTitleUnderneath = item.Type === 'MusicAlbum' || item.Type === 'Audio' || item.Type === 'MusicVideo'; + var titleAdded; + + if (showOtherText) { + if ((options.showParentTitle || options.showParentTitleOrTitle) && !parentTitleUnderneath) { + + if (isOuterFooter && item.Type === 'Episode' && item.SeriesName) { + + if (item.SeriesId) { + lines.push(getTextActionButton({ + Id: item.SeriesId, + ServerId: serverId, + Name: item.SeriesName, + Type: 'Series', + IsFolder: true + })); + } else { + lines.push(item.SeriesName); + } + } + else { + + if (isUsingLiveTvNaming(item)) { + + lines.push(item.Name); + + if (!item.EpisodeTitle) { + titleAdded = true; + } + + } else { + var parentTitle = item.SeriesName || item.Series || item.Album || item.AlbumArtist || item.GameSystem || ""; + + if (parentTitle || showTitle) { + lines.push(parentTitle); + } + } + } + } + } + + var showMediaTitle = (showTitle && !titleAdded) || (options.showParentTitleOrTitle && !lines.length); + if (!showMediaTitle && !titleAdded && (showTitle || forceName)) { + showMediaTitle = true; + } + + if (showMediaTitle) { + + var name = options.showTitle === 'auto' && !item.IsFolder && item.MediaType === 'Photo' ? '' : itemHelper.getDisplayName(item, { + includeParentInfo: options.includeParentInfoInTitle + }); + + lines.push(getTextActionButton({ + Id: item.Id, + ServerId: serverId, + Name: name, + Type: item.Type, + CollectionType: item.CollectionType, + IsFolder: item.IsFolder + })); + } + + if (showOtherText) { + if (options.showParentTitle && parentTitleUnderneath) { + + if (isOuterFooter && item.AlbumArtists && item.AlbumArtists.length) { + item.AlbumArtists[0].Type = 'MusicArtist'; + item.AlbumArtists[0].IsFolder = true; + lines.push(getTextActionButton(item.AlbumArtists[0], null, serverId)); + } else { + lines.push(isUsingLiveTvNaming(item) ? item.Name : (item.SeriesName || item.Series || item.Album || item.AlbumArtist || item.GameSystem || "")); + } + } + + if (options.showItemCounts) { + + var itemCountHtml = getItemCountsHtml(options, item); + + lines.push(itemCountHtml); + } + + if (options.textLines) { + var additionalLines = options.textLines(item); + for (var i = 0, length = additionalLines.length; i < length; i++) { + lines.push(additionalLines[i]); + } + } + + if (options.showSongCount) { + + var songLine = ''; + + if (item.SongCount) { + songLine = item.SongCount === 1 ? + globalize.translate('sharedcomponents#ValueOneSong') : + globalize.translate('sharedcomponents#ValueSongCount', item.SongCount); + } + + lines.push(songLine); + } + + if (options.showPremiereDate) { + + if (item.PremiereDate) { + try { + + lines.push(getPremiereDateText(item)); + + } catch (err) { + lines.push(''); + + } + } else { + lines.push(''); + } + } + + if (options.showYear || options.showSeriesYear) { + + if (item.Type === 'Series') { + if (item.Status === "Continuing") { + + lines.push(globalize.translate('sharedcomponents#SeriesYearToPresent', item.ProductionYear || '')); + + } else { + + if (item.EndDate && item.ProductionYear) { + lines.push(item.ProductionYear + ' - ' + datetime.parseISO8601Date(item.EndDate).getFullYear()); + } else { + lines.push(item.ProductionYear || ''); + } + } + } else { + lines.push(item.ProductionYear || ''); + } + } + + if (options.showRuntime) { + + if (item.RunTimeTicks) { + + lines.push(datetime.getDisplayRunningTime(item.RunTimeTicks)); + } else { + lines.push(''); + } + } + + if (options.showAirTime) { + + lines.push(getAirTimeText(item, options.showAirDateTime, options.showAirEndTime) || ''); + } + + if (options.showChannelName) { + + if (item.ChannelId) { + + lines.push(getTextActionButton({ + + Id: item.ChannelId, + ServerId: serverId, + Name: item.ChannelName, + Type: 'TvChannel', + MediaType: item.MediaType, + IsFolder: false + + }, item.ChannelName)); + } else { + lines.push(item.ChannelName || ' '); + } + } + + if (options.showCurrentProgram && item.Type === 'TvChannel') { + + if (item.CurrentProgram) { + lines.push(item.CurrentProgram.Name); + } else { + lines.push(''); + } + } + + if (options.showCurrentProgramTime && item.Type === 'TvChannel') { + + if (item.CurrentProgram) { + lines.push(getAirTimeText(item.CurrentProgram, false, true) || ''); + } else { + lines.push(''); + } + } + + if (options.showSeriesTimerTime) { + if (item.RecordAnyTime) { + + lines.push(globalize.translate('sharedcomponents#Anytime')); + } else { + lines.push(datetime.getDisplayTime(item.StartDate)); + } + } + + if (options.showSeriesTimerChannel) { + if (item.RecordAnyChannel) { + lines.push(globalize.translate('sharedcomponents#AllChannels')); + } + else { + lines.push(item.ChannelName || globalize.translate('sharedcomponents#OneChannel')); + } + } + + if (options.showPersonRoleOrType) { + if (item.Role) { + lines.push('as ' + item.Role); + } + else if (item.Type) { + lines.push(globalize.translate('sharedcomponents#' + item.Type)); + } else { + lines.push(''); + } + } + } + + if ((showTitle || !imgUrl) && forceName && overlayText && lines.length === 1) { + lines = []; + } + + var addRightTextMargin = isOuterFooter && options.cardLayout && !options.centerText && options.cardFooterAside !== 'none' && layoutManager.mobile; + + html += getCardTextLines(lines, cssClass, !options.overlayText, isOuterFooter, options.cardLayout, addRightTextMargin, options.lines); + + if (progressHtml) { + html += progressHtml; + } + + if (html) { + + if (!isOuterFooter || logoUrl || options.cardLayout) { + html = '
' + html; + + //cardFooter + html += "
"; + } + } + + return html; + } + + function getTextActionButton(item, text, serverId) { + + if (!text) { + text = itemHelper.getDisplayName(item); + } + + if (layoutManager.tv) { + return text; + } + + var html = ''; + + return html; + } + + function getItemCountsHtml(options, item) { + + var counts = []; + + var childText; + + if (item.Type === 'Playlist') { + + childText = ''; + + if (item.RunTimeTicks) { + + var minutes = item.RunTimeTicks / 600000000; + + minutes = minutes || 1; + + childText += globalize.translate('sharedcomponents#ValueMinutes', Math.round(minutes)); + + } else { + childText += globalize.translate('sharedcomponents#ValueMinutes', 0); + } + + counts.push(childText); + + } + else if (item.Type === 'Genre' || item.Type === 'Studio') { + + if (item.MovieCount) { + + childText = item.MovieCount === 1 ? + globalize.translate('sharedcomponents#ValueOneMovie') : + globalize.translate('sharedcomponents#ValueMovieCount', item.MovieCount); + + counts.push(childText); + } + + if (item.SeriesCount) { + + childText = item.SeriesCount === 1 ? + globalize.translate('sharedcomponents#ValueOneSeries') : + globalize.translate('sharedcomponents#ValueSeriesCount', item.SeriesCount); + + counts.push(childText); + } + if (item.EpisodeCount) { + + childText = item.EpisodeCount === 1 ? + globalize.translate('sharedcomponents#ValueOneEpisode') : + globalize.translate('sharedcomponents#ValueEpisodeCount', item.EpisodeCount); + + counts.push(childText); + } + if (item.GameCount) { + + childText = item.GameCount === 1 ? + globalize.translate('sharedcomponents#ValueOneGame') : + globalize.translate('sharedcomponents#ValueGameCount', item.GameCount); + + counts.push(childText); + } + + } else if (item.Type === 'GameGenre') { + + if (item.GameCount) { + + childText = item.GameCount === 1 ? + globalize.translate('sharedcomponents#ValueOneGame') : + globalize.translate('sharedcomponents#ValueGameCount', item.GameCount); + + counts.push(childText); + } + } else if (item.Type === 'MusicGenre' || options.context === "MusicArtist") { + + if (item.AlbumCount) { + + childText = item.AlbumCount === 1 ? + globalize.translate('sharedcomponents#ValueOneAlbum') : + globalize.translate('sharedcomponents#ValueAlbumCount', item.AlbumCount); + + counts.push(childText); + } + if (item.SongCount) { + + childText = item.SongCount === 1 ? + globalize.translate('sharedcomponents#ValueOneSong') : + globalize.translate('sharedcomponents#ValueSongCount', item.SongCount); + + counts.push(childText); + } + if (item.MusicVideoCount) { + + childText = item.MusicVideoCount === 1 ? + globalize.translate('sharedcomponents#ValueOneMusicVideo') : + globalize.translate('sharedcomponents#ValueMusicVideoCount', item.MusicVideoCount); + + counts.push(childText); + } + + } else if (item.Type === 'Series') { + + childText = item.RecursiveItemCount === 1 ? + globalize.translate('sharedcomponents#ValueOneEpisode') : + globalize.translate('sharedcomponents#ValueEpisodeCount', item.RecursiveItemCount); + + counts.push(childText); + } + + return counts.join(', '); + } + + function getProgramIndicators(item) { + + item = item.ProgramInfo || item; + + var html = ''; + + if (item.IsLive) { + html += '
' + globalize.translate('sharedcomponents#Live') + '
'; + } + + if (item.IsPremiere) { + html += '
' + globalize.translate('sharedcomponents#Premiere') + '
'; + } + else if (item.IsSeries && !item.IsRepeat) { + html += '
' + globalize.translate('sharedcomponents#AttributeNew') + '
'; + } + //else if (item.IsRepeat) { + // html += '
' + globalize.translate('sharedcomponents#Repeat') + '
'; + //} + + if (html) { + html = '
' + html; + html += '
'; + } + + return html; + } + + var refreshIndicatorLoaded; + function requireRefreshIndicator() { + + if (!refreshIndicatorLoaded) { + refreshIndicatorLoaded = true; + require(['emby-itemrefreshindicator']); + } + } + + function getDefaultBackgroundClass(str) { + return 'defaultCardBackground defaultCardBackground' + getDefaultColorIndex(str); + } + + function buildCard(index, item, apiClient, options) { + + var action = options.action || 'link'; + + if (action === 'play' && item.IsFolder) { + // If this hard-coding is ever removed make sure to test nested photo albums + action = 'link'; + } + else if (item.MediaType === 'Photo') { + action = 'play'; + } + + var shape = options.shape; + + if (shape === 'mixed') { + + shape = null; + + var primaryImageAspectRatio = item.PrimaryImageAspectRatio; + + if (primaryImageAspectRatio) { + + if (primaryImageAspectRatio >= 1.33) { + shape = 'mixedBackdrop'; + } else if (primaryImageAspectRatio > 0.71) { + shape = 'mixedSquare'; + } else { + shape = 'mixedPortrait'; + } + } + + shape = shape || 'mixedSquare'; + } + + var className = 'card'; + + if (shape) { + className += ' ' + shape + 'Card'; + } + + if (options.cardCssClass) { + className += ' ' + options.cardCssClass; + } + + if (options.cardClass) { + className += " " + options.cardClass; + } + + if (layoutManager.desktop) { + className += ' card-hoverable'; + } + + if (!enableFocusTransfrom || !layoutManager.tv) { + className += ' card-nofocustransform'; + } + + var imgInfo = getCardImageUrl(item, apiClient, options, shape); + var imgUrl = imgInfo.imgUrl; + + var forceName = imgInfo.forceName; + + var showTitle = options.showTitle === 'auto' ? true : (options.showTitle || item.Type === 'PhotoAlbum' || item.Type === 'Folder'); + var overlayText = options.overlayText; + + if (forceName && !options.cardLayout) { + + if (overlayText == null) { + overlayText = true; + } + } + + var cardImageContainerClass = 'cardImageContainer'; + var coveredImage = options.coverImage || imgInfo.coverImage; + + if (coveredImage) { + cardImageContainerClass += ' coveredImage'; + + if (item.MediaType === 'Photo' || item.Type === 'PhotoAlbum' || item.Type === 'Folder' || item.ProgramInfo || item.Type === 'Program' || item.Type === 'Recording') { + cardImageContainerClass += ' coveredImage-noScale'; + } + } + + if (!imgUrl) { + cardImageContainerClass += ' ' + getDefaultBackgroundClass(item.Name); + } + + var cardBoxClass = options.cardLayout ? 'cardBox visualCardBox' : 'cardBox'; + + if (layoutManager.tv) { + + if (enableFocusTransfrom) { + cardBoxClass += ' cardBox-focustransform cardBox-withfocuscontent'; + } else { + cardBoxClass += ' cardBox-withfocuscontent-large'; + } + + if (options.cardLayout) { + cardBoxClass += ' card-focuscontent'; + + if (!enableFocusTransfrom) { + cardBoxClass += ' card-focuscontent-large'; + } + } + } + + var footerCssClass; + var progressHtml = indicators.getProgressBarHtml(item); + + var innerCardFooter = ''; + + var footerOverlayed = false; + + var logoUrl; + var logoHeight = 40; + + if (options.showChannelLogo && item.ChannelPrimaryImageTag) { + logoUrl = apiClient.getScaledImageUrl(item.ChannelId, { + type: "Primary", + height: logoHeight, + tag: item.ChannelPrimaryImageTag + }); + } + else if (options.showLogo && item.ParentLogoImageTag) { + logoUrl = apiClient.getScaledImageUrl(item.ParentLogoItemId, { + type: "Logo", + height: logoHeight, + tag: item.ParentLogoImageTag + }); + } + + if (overlayText) { + + logoUrl = null; + + footerCssClass = progressHtml ? 'innerCardFooter fullInnerCardFooter' : 'innerCardFooter'; + innerCardFooter += getCardFooterText(item, apiClient, options, showTitle, forceName, overlayText, imgUrl, footerCssClass, progressHtml, logoUrl, false); + footerOverlayed = true; + } + else if (progressHtml) { + innerCardFooter += '
'; + innerCardFooter += progressHtml; + innerCardFooter += '
'; + + progressHtml = ''; + } + + var mediaSourceCount = item.MediaSourceCount || 1; + if (mediaSourceCount > 1) { + innerCardFooter += '
' + mediaSourceCount + '
'; + } + + var outerCardFooter = ''; + if (!overlayText && !footerOverlayed) { + footerCssClass = options.cardLayout ? 'cardFooter' : 'cardFooter cardFooter-transparent'; + + if (logoUrl) { + footerCssClass += ' cardFooter-withlogo'; + } + + if (!options.cardLayout) { + logoUrl = null; + } + + outerCardFooter = getCardFooterText(item, apiClient, options, showTitle, forceName, overlayText, imgUrl, footerCssClass, progressHtml, logoUrl, true); + } + + if (outerCardFooter && !options.cardLayout /*&& options.allowBottomPadding !== false*/) { + cardBoxClass += ' cardBox-bottompadded'; + } + + var overlayButtons = ''; + if (layoutManager.mobile) { + + var overlayPlayButton = options.overlayPlayButton; + + if (overlayPlayButton == null && !options.overlayMoreButton && !options.overlayInfoButton && !options.cardLayout) { + overlayPlayButton = item.MediaType === 'Video'; + } + + var btnCssClass = 'cardOverlayButton cardOverlayButton-br itemAction'; + + if (options.centerPlayButton) { + overlayButtons += ''; + } + + if (overlayPlayButton && !item.IsPlaceHolder && (item.LocationType !== 'Virtual' || !item.MediaType || item.Type === 'Program') && item.Type !== 'Person') { + overlayButtons += ''; + } + + if (options.overlayMoreButton) { + + overlayButtons += ''; + } + } + + if (options.showChildCountIndicator && item.ChildCount) { + className += ' groupedCard'; + } + + // cardBox can be it's own separate element if an outer footer is ever needed + var cardImageContainerOpen; + var cardImageContainerClose = ''; + var cardBoxClose = ''; + var cardScalableClose = ''; + + var cardContentClass = 'cardContent'; + if (!options.cardLayout) { + cardContentClass += ' cardContent-shadow'; + } + + if (layoutManager.tv) { + + // Don't use the IMG tag with safari because it puts a white border around it + cardImageContainerOpen = imgUrl ? ('
') : ('
'); + + cardImageContainerClose = '
'; + } else { + // Don't use the IMG tag with safari because it puts a white border around it + cardImageContainerOpen = imgUrl ? (''; + } + + var cardScalableClass = 'cardScalable'; + + if (layoutManager.tv && !options.cardLayout) { + + cardScalableClass += ' card-focuscontent'; + + if (!enableFocusTransfrom) { + cardScalableClass += ' card-focuscontent-large'; + } + } + + cardImageContainerOpen = '
' + cardImageContainerOpen; + cardBoxClose = '
'; + cardScalableClose = '
'; + + var indicatorsHtml = ''; + + if (options.missingIndicator !== false) { + indicatorsHtml += indicators.getMissingIndicator(item); + } + + indicatorsHtml += indicators.getSyncIndicator(item); + indicatorsHtml += indicators.getTimerIndicator(item); + + indicatorsHtml += indicators.getTypeIndicator(item); + + if (options.showGroupCount) { + + indicatorsHtml += indicators.getChildCountIndicatorHtml(item, { + minCount: 1 + }); + } + else { + indicatorsHtml += indicators.getPlayedIndicatorHtml(item); + } + + if (item.Type === 'CollectionFolder' || item.CollectionType) { + var refreshClass = item.RefreshProgress || (item.RefreshStatus && virtualFolder.item !== 'Idle') ? '' : ' class="hide"'; + indicatorsHtml += '
'; + requireRefreshIndicator(); + } + + if (indicatorsHtml) { + cardImageContainerOpen += '
' + indicatorsHtml + '
'; + } + + //if (item.Type === 'Program' || item.Type === 'Timer') { + // cardImageContainerOpen += getProgramIndicators(item); + //} + + if (!imgUrl) { + cardImageContainerOpen += getCardDefaultText(item, options); + } + + var tagName = (layoutManager.tv) && !overlayButtons ? 'button' : 'div'; + + var nameWithPrefix = (item.SortName || item.Name || ''); + var prefix = nameWithPrefix.substring(0, Math.min(3, nameWithPrefix.length)); + + if (prefix) { + prefix = prefix.toUpperCase(); + } + + var timerAttributes = ''; + if (item.TimerId) { + timerAttributes += ' data-timerid="' + item.TimerId + '"'; + } + if (item.SeriesTimerId) { + timerAttributes += ' data-seriestimerid="' + item.SeriesTimerId + '"'; + } + + var actionAttribute; + + if (tagName === 'button') { + className += " itemAction"; + actionAttribute = ' data-action="' + action + '"'; + } else { + actionAttribute = ''; + } + + if (item.Type !== 'MusicAlbum' && item.Type !== 'MusicArtist' && item.Type !== 'Audio') { + className += ' card-withuserdata'; + } + + var positionTicksData = item.UserData && item.UserData.PlaybackPositionTicks ? (' data-positionticks="' + item.UserData.PlaybackPositionTicks + '"') : ''; + var collectionIdData = options.collectionId ? (' data-collectionid="' + options.collectionId + '"') : ''; + var playlistIdData = options.playlistId ? (' data-playlistid="' + options.playlistId + '"') : ''; + var mediaTypeData = item.MediaType ? (' data-mediatype="' + item.MediaType + '"') : ''; + var collectionTypeData = item.CollectionType ? (' data-collectiontype="' + item.CollectionType + '"') : ''; + var channelIdData = item.ChannelId ? (' data-channelid="' + item.ChannelId + '"') : ''; + var contextData = options.context ? (' data-context="' + options.context + '"') : ''; + var parentIdData = options.parentId ? (' data-parentid="' + options.parentId + '"') : ''; + + var additionalCardContent = ''; + + if (layoutManager.desktop) { + additionalCardContent += getHoverMenuHtml(item, action); + } + + return '<' + tagName + ' data-index="' + index + '"' + timerAttributes + actionAttribute + ' data-isfolder="' + (item.IsFolder || false) + '" data-serverid="' + (item.ServerId || options.serverId) + '" data-id="' + (item.Id || item.ItemId) + '" data-type="' + item.Type + '"' + mediaTypeData + collectionTypeData + channelIdData + positionTicksData + collectionIdData + playlistIdData + contextData + parentIdData + ' data-prefix="' + prefix + '" class="' + className + '">' + cardImageContainerOpen + innerCardFooter + cardImageContainerClose + overlayButtons + additionalCardContent + cardScalableClose + outerCardFooter + cardBoxClose + ''; + } + + function getHoverMenuHtml(item, action) { + + var html = ''; + + html += '
'; + + var btnCssClass = 'cardOverlayButton cardOverlayButton-hover itemAction'; + + if (playbackManager.canPlay(item)) { + + html += ''; + } + + html += '
'; + + //if (itemHelper.canEdit({ Policy: { IsAdministrator: true } }, item)) { + + // //require(['emby-playstatebutton']); + // html += ''; + //} + + var userData = item.UserData || {}; + + if (itemHelper.canMarkPlayed(item)) { + + require(['emby-playstatebutton']); + html += ''; + } + + if (itemHelper.canRate(item)) { + + var likes = userData.Likes == null ? '' : userData.Likes; + + require(['emby-ratingbutton']); + html += ''; + } + + html += ''; + + html += '
'; + html += '
'; + + return html; + } + + function getCardDefaultText(item, options) { + + var collectionType = item.CollectionType; + if (collectionType === 'livetv') { + return ''; + } + if (collectionType === 'homevideos' || collectionType === 'photos') { + return ''; + } + if (collectionType === 'music') { + return ''; + } + if (item.Type === 'MusicAlbum') { + return ''; + } + if (item.Type === 'MusicArtist' || item.Type === 'Person') { + return ''; + } + if (options.defaultCardImageIcon) { + return '' + options.defaultCardImageIcon + ''; + } + + var defaultName = isUsingLiveTvNaming(item) ? item.Name : itemHelper.getDisplayName(item); + return '
' + defaultName + '
'; + } + + function buildCards(items, options) { + + // Abort if the container has been disposed + if (!document.body.contains(options.itemsContainer)) { + return; + } + + if (options.parentContainer) { + if (items.length) { + options.parentContainer.classList.remove('hide'); + } else { + options.parentContainer.classList.add('hide'); + return; + } + } + + var html = buildCardsHtmlInternal(items, options); + + if (html) { + + if (options.itemsContainer.cardBuilderHtml !== html) { + options.itemsContainer.innerHTML = html; + + if (items.length < 50) { + options.itemsContainer.cardBuilderHtml = html; + } else { + options.itemsContainer.cardBuilderHtml = null; + } + } + + imageLoader.lazyChildren(options.itemsContainer); + } else { + + options.itemsContainer.innerHTML = html; + options.itemsContainer.cardBuilderHtml = null; + } + + if (options.autoFocus) { + focusManager.autoFocus(options.itemsContainer, true); + } + } + + function ensureIndicators(card, indicatorsElem) { + + if (indicatorsElem) { + return indicatorsElem; + } + + indicatorsElem = card.querySelector('.cardIndicators'); + + if (!indicatorsElem) { + + var cardImageContainer = card.querySelector('.cardImageContainer'); + indicatorsElem = document.createElement('div'); + indicatorsElem.classList.add('cardIndicators'); + cardImageContainer.appendChild(indicatorsElem); + } + + return indicatorsElem; + } + + function updateUserData(card, userData) { + + var type = card.getAttribute('data-type'); + var enableCountIndicator = type === 'Series' || type === 'BoxSet' || type === 'Season'; + var indicatorsElem = null; + var playedIndicator = null; + var countIndicator = null; + var itemProgressBar = null; + + if (userData.Played) { + + playedIndicator = card.querySelector('.playedIndicator'); + + if (!playedIndicator) { + + playedIndicator = document.createElement('div'); + playedIndicator.classList.add('playedIndicator'); + playedIndicator.classList.add('indicator'); + indicatorsElem = ensureIndicators(card, indicatorsElem); + indicatorsElem.appendChild(playedIndicator); + } + playedIndicator.innerHTML = ''; + } else { + + playedIndicator = card.querySelector('.playedIndicator'); + if (playedIndicator) { + + playedIndicator.parentNode.removeChild(playedIndicator); + } + } + if (userData.UnplayedItemCount) { + countIndicator = card.querySelector('.countIndicator'); + + if (!countIndicator) { + + countIndicator = document.createElement('div'); + countIndicator.classList.add('countIndicator'); + indicatorsElem = ensureIndicators(card, indicatorsElem); + indicatorsElem.appendChild(countIndicator); + } + countIndicator.innerHTML = userData.UnplayedItemCount; + } else if (enableCountIndicator) { + + countIndicator = card.querySelector('.countIndicator'); + if (countIndicator) { + + countIndicator.parentNode.removeChild(countIndicator); + } + } + + var progressHtml = indicators.getProgressBarHtml({ + Type: type, + UserData: userData, + MediaType: 'Video' + }); + + if (progressHtml) { + + itemProgressBar = card.querySelector('.itemProgressBar'); + + if (!itemProgressBar) { + itemProgressBar = document.createElement('div'); + itemProgressBar.classList.add('itemProgressBar'); + + var innerCardFooter = card.querySelector('.innerCardFooter'); + if (!innerCardFooter) { + innerCardFooter = document.createElement('div'); + innerCardFooter.classList.add('innerCardFooter'); + var cardImageContainer = card.querySelector('.cardImageContainer'); + cardImageContainer.appendChild(innerCardFooter); + } + innerCardFooter.appendChild(itemProgressBar); + } + + itemProgressBar.innerHTML = progressHtml; + } + else { + + itemProgressBar = card.querySelector('.itemProgressBar'); + if (itemProgressBar) { + itemProgressBar.parentNode.removeChild(itemProgressBar); + } + } + } + + function onUserDataChanged(userData, scope) { + + var cards = (scope || document.body).querySelectorAll('.card-withuserdata[data-id="' + userData.ItemId + '"]'); + + for (var i = 0, length = cards.length; i < length; i++) { + updateUserData(cards[i], userData); + } + } + + function onTimerCreated(programId, newTimerId, itemsContainer) { + + var cells = itemsContainer.querySelectorAll('.card[data-id="' + programId + '"]'); + + for (var i = 0, length = cells.length; i < length; i++) { + var cell = cells[i]; + var icon = cell.querySelector('.timerIndicator'); + if (!icon) { + var indicatorsElem = ensureIndicators(cell); + indicatorsElem.insertAdjacentHTML('beforeend', ''); + } + cell.setAttribute('data-timerid', newTimerId); + } + } + + function onTimerCancelled(id, itemsContainer) { + + var cells = itemsContainer.querySelectorAll('.card[data-timerid="' + id + '"]'); + + for (var i = 0, length = cells.length; i < length; i++) { + var cell = cells[i]; + var icon = cell.querySelector('.timerIndicator'); + if (icon) { + icon.parentNode.removeChild(icon); + } + cell.removeAttribute('data-timerid'); + } + } + + function onSeriesTimerCancelled(id, itemsContainer) { + + var cells = itemsContainer.querySelectorAll('.card[data-seriestimerid="' + id + '"]'); + + for (var i = 0, length = cells.length; i < length; i++) { + var cell = cells[i]; + var icon = cell.querySelector('.timerIndicator'); + if (icon) { + icon.parentNode.removeChild(icon); + } + cell.removeAttribute('data-seriestimerid'); + } + } + + return { + getCardsHtml: getCardsHtml, + buildCards: buildCards, + onUserDataChanged: onUserDataChanged, + onTimerCreated: onTimerCreated, + onTimerCancelled: onTimerCancelled, + onSeriesTimerCancelled: onSeriesTimerCancelled + }; + }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/cardbuilder/chaptercardbuilder.js b/src/bower_components/emby-webcomponents/cardbuilder/chaptercardbuilder.js index 55ff68eb5a..900f4befc1 100644 --- a/src/bower_components/emby-webcomponents/cardbuilder/chaptercardbuilder.js +++ b/src/bower_components/emby-webcomponents/cardbuilder/chaptercardbuilder.js @@ -1,59 +1,140 @@ -define(["datetime", "imageLoader", "connectionManager", "layoutManager", "browser"], function(datetime, imageLoader, connectionManager, layoutManager, browser) { - "use strict"; +define(['datetime', 'imageLoader', 'connectionManager', 'layoutManager', 'browser'], function (datetime, imageLoader, connectionManager, layoutManager, browser) { + 'use strict'; function buildChapterCardsHtml(item, chapters, options) { - var className = "card itemAction chapterCard"; - layoutManager.tv && (browser.animate || browser.edge) && (className += " card-focusscale"); - var mediaStreams = ((item.MediaSources || [])[0] || {}).MediaStreams || [], - videoStream = mediaStreams.filter(function(i) { - return "Video" === i.Type - })[0] || {}, - shape = options.backdropShape || "backdrop"; - videoStream.Width && videoStream.Height && videoStream.Width / videoStream.Height <= 1.2 && (shape = options.squareShape || "square"), className += " " + shape + "Card", (options.block || options.rows) && (className += " block"); - for (var html = "", itemsInRow = 0, apiClient = connectionManager.getApiClient(item.ServerId), i = 0, length = chapters.length; i < length; i++) { - options.rows && 0 === itemsInRow && (html += '
'); - html += buildChapterCard(item, apiClient, chapters[i], i, options, className, shape), itemsInRow++, options.rows && itemsInRow >= options.rows && (itemsInRow = 0, html += "
") + + var className = 'card itemAction chapterCard'; + + if (layoutManager.tv && (browser.animate || browser.edge)) { + className += ' card-focusscale'; } - return html + + var mediaStreams = ((item.MediaSources || [])[0] || {}).MediaStreams || []; + var videoStream = mediaStreams.filter(function (i) { + return i.Type === 'Video'; + })[0] || {}; + + var shape = (options.backdropShape || 'backdrop'); + + if (videoStream.Width && videoStream.Height) { + + if ((videoStream.Width / videoStream.Height) <= 1.2) { + shape = (options.squareShape || 'square'); + } + } + + className += ' ' + shape + 'Card'; + + if (options.block || options.rows) { + className += ' block'; + } + + var html = ''; + var itemsInRow = 0; + + var apiClient = connectionManager.getApiClient(item.ServerId); + + for (var i = 0, length = chapters.length; i < length; i++) { + + if (options.rows && itemsInRow === 0) { + html += '
'; + } + + var chapter = chapters[i]; + + html += buildChapterCard(item, apiClient, chapter, i, options, className, shape); + itemsInRow++; + + if (options.rows && itemsInRow >= options.rows) { + itemsInRow = 0; + html += '
'; + } + } + + return html; } function getImgUrl(item, chapter, index, maxWidth, apiClient) { - return chapter.ImageTag ? apiClient.getScaledImageUrl(item.Id, { - maxWidth: maxWidth, - tag: chapter.ImageTag, - type: "Chapter", - index: index - }) : null + + if (chapter.ImageTag) { + + return apiClient.getScaledImageUrl(item.Id, { + + maxWidth: maxWidth, + tag: chapter.ImageTag, + type: "Chapter", + index: index + }); + } + + return null; } function buildChapterCard(item, apiClient, chapter, index, options, className, shape) { - var imgUrl = getImgUrl(item, chapter, index, options.width || 400, apiClient), - cardImageContainerClass = "cardContent cardContent-shadow cardImageContainer chapterCardImageContainer"; - options.coverImage && (cardImageContainerClass += " coveredImage"); - var dataAttributes = ' data-action="play" data-isfolder="' + item.IsFolder + '" data-id="' + item.Id + '" data-serverid="' + item.ServerId + '" data-type="' + item.Type + '" data-mediatype="' + item.MediaType + '" data-positionticks="' + chapter.StartPositionTicks + '"', - cardImageContainer = imgUrl ? '
' : '
'; - imgUrl || (cardImageContainer += 'local_movies'); - var nameHtml = ""; - nameHtml += '
' + chapter.Name + "
", nameHtml += '
' + datetime.getDisplayRunningTime(chapter.StartPositionTicks) + "
"; - var cardBoxCssClass = "cardBox", - cardScalableClass = "cardScalable"; + + var imgUrl = getImgUrl(item, chapter, index, options.width || 400, apiClient); + + var cardImageContainerClass = 'cardContent cardContent-shadow cardImageContainer chapterCardImageContainer'; + if (options.coverImage) { + cardImageContainerClass += ' coveredImage'; + } + var dataAttributes = ' data-action="play" data-isfolder="' + item.IsFolder + '" data-id="' + item.Id + '" data-serverid="' + item.ServerId + '" data-type="' + item.Type + '" data-mediatype="' + item.MediaType + '" data-positionticks="' + chapter.StartPositionTicks + '"'; + var cardImageContainer = imgUrl ? ('
') : ('
'); + + if (!imgUrl) { + cardImageContainer += 'local_movies'; + } + + var nameHtml = ''; + nameHtml += '
' + chapter.Name + '
'; + nameHtml += '
' + datetime.getDisplayRunningTime(chapter.StartPositionTicks) + '
'; + + var cardBoxCssClass = 'cardBox'; + var cardScalableClass = 'cardScalable'; + if (layoutManager.tv) { var enableFocusTransfrom = !browser.slow && !browser.edge; - cardScalableClass += " card-focuscontent", enableFocusTransfrom ? cardBoxCssClass += " cardBox-focustransform cardBox-withfocuscontent" : (cardBoxCssClass += " cardBox-withfocuscontent-large", cardScalableClass += " card-focuscontent-large") + + cardScalableClass += ' card-focuscontent'; + + if (enableFocusTransfrom) { + cardBoxCssClass += ' cardBox-focustransform cardBox-withfocuscontent'; + } else { + cardBoxCssClass += ' cardBox-withfocuscontent-large'; + cardScalableClass += ' card-focuscontent-large'; + } } - return '
" + + var html = '
'; + + return html; } function buildChapterCards(item, chapters, options) { + if (options.parentContainer) { - if (!document.body.contains(options.parentContainer)) return; - if (!chapters.length) return void options.parentContainer.classList.add("hide"); - options.parentContainer.classList.remove("hide") + // Abort if the container has been disposed + if (!document.body.contains(options.parentContainer)) { + return; + } + + if (chapters.length) { + options.parentContainer.classList.remove('hide'); + } else { + options.parentContainer.classList.add('hide'); + return; + } } + var html = buildChapterCardsHtml(item, chapters, options); - options.itemsContainer.innerHTML = html, imageLoader.lazyChildren(options.itemsContainer) + + options.itemsContainer.innerHTML = html; + + imageLoader.lazyChildren(options.itemsContainer); } + return { buildChapterCards: buildChapterCards - } + }; + }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/cardbuilder/peoplecardbuilder.js b/src/bower_components/emby-webcomponents/cardbuilder/peoplecardbuilder.js index 4dc70f7ed4..e0a5050dc5 100644 --- a/src/bower_components/emby-webcomponents/cardbuilder/peoplecardbuilder.js +++ b/src/bower_components/emby-webcomponents/cardbuilder/peoplecardbuilder.js @@ -1,18 +1,22 @@ -define(["cardBuilder"], function(cardBuilder) { - "use strict"; +define(['cardBuilder'], function (cardBuilder) { + 'use strict'; function buildPeopleCards(items, options) { + options = Object.assign(options || {}, { - cardLayout: !1, - centerText: !0, - showTitle: !0, - cardFooterAside: "none", - showPersonRoleOrType: !0, - cardCssClass: "personCard", - defaultCardImageIcon: "" - }), cardBuilder.buildCards(items, options) + cardLayout: false, + centerText: true, + showTitle: true, + cardFooterAside: 'none', + showPersonRoleOrType: true, + cardCssClass: 'personCard', + defaultCardImageIcon: '' + }); + cardBuilder.buildCards(items, options); } + return { buildPeopleCards: buildPeopleCards - } + }; + }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/cardbuilder/roundcard.css b/src/bower_components/emby-webcomponents/cardbuilder/roundcard.css index dec5e8d58f..d0bd950972 100644 --- a/src/bower_components/emby-webcomponents/cardbuilder/roundcard.css +++ b/src/bower_components/emby-webcomponents/cardbuilder/roundcard.css @@ -1,10 +1,7 @@ -.card-round:focus>.cardBox-focustransform { - -webkit-transform: scale(1.26, 1.26); - transform: scale(1.26, 1.26) +.card-round:focus > .cardBox-focustransform { + transform: scale(1.26, 1.26); } -.cardImage-round, -.cardImageContainer-round { - -webkit-border-radius: 1000px; - border-radius: 1000px -} \ No newline at end of file +.cardImageContainer-round, .cardImage-round { + border-radius: 1000px; +} diff --git a/src/bower_components/emby-webcomponents/chromecast/chromecasthelpers.js b/src/bower_components/emby-webcomponents/chromecast/chromecasthelpers.js index e3330bf37e..4950f6540e 100644 --- a/src/bower_components/emby-webcomponents/chromecast/chromecasthelpers.js +++ b/src/bower_components/emby-webcomponents/chromecast/chromecasthelpers.js @@ -1,67 +1,228 @@ -define(["events"], function(events) { - "use strict"; +define(['events'], function (events) { + 'use strict'; + + // LinkParser + // + // https://github.com/ravisorg/LinkParser + // + // Locate and extract almost any URL within a string. Handles protocol-less domains, IPv4 and + // IPv6, unrecognised TLDs, and more. + // + // This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. + // http://creativecommons.org/licenses/by-sa/4.0/ + (function () { + + // Original URL regex from the Android android.text.util.Linkify function, found here: + // http://stackoverflow.com/a/19696443 + // + // However there were problems with it, most probably related to the fact it was + // written in 2007, and it's been highly modified. + // + // 1) I didn't like the fact that it was tied to specific TLDs, since new ones + // are being added all the time it wouldn't be reasonable to expect developer to + // be continually updating their regular expressions. + // + // 2) It didn't allow unicode characters in the domains which are now allowed in + // many languages, (including some IDN TLDs). Again these are constantly being + // added to and it doesn't seem reasonable to hard-code them. Note this ended up + // not being possible in standard JS due to the way it handles multibyte strings. + // It is possible using XRegExp, however a big performance hit results. Disabled + // for now. + // + // 3) It didn't allow for IPv6 hostnames + // IPv6 regex from http://stackoverflow.com/a/17871737 + // + // 4) It was very poorly commented + // + // 5) It wasn't as smart as it could have been about what should be part of a + // URL and what should be part of human language. + + var protocols = "(?:(?:http|https|rtsp|ftp):\\/\\/)"; + var credentials = "(?:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,64}" // username (1-64 normal or url escaped characters) + + "(?:\\:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,25})?" // followed by optional password (: + 1-25 normal or url escaped characters) + + "\\@)"; + + // IPv6 Regex http://forums.intermapper.com/viewtopic.php?t=452 + // by Dartware, LLC is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License + // http://intermapper.com/ + var ipv6 = "(" + + "(([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))" + + "|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))" + + "|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))" + + "|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))" + + "|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))" + + "|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))" + + "|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))" + + "|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))" + + ")(%.+)?"; + + var ipv4 = "(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\." + + "(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\." + + "(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\." + + "(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])"; + + // This would have been a lot cleaner if JS RegExp supported conditionals... + var linkRegExpString = + + // begin match for protocol / username / password / host + "(?:" + + // ============================ + // If we have a recognized protocol at the beginning of the URL, we're + // more relaxed about what we accept, because we assume the user wants + // this to be a URL, and we're not accidentally matching human language + + protocols + "?" + + // optional username:password@ + + credentials + "?" + + // IP address (both v4 and v6) + + "(?:" + + // IPv6 + + ipv6 + + // IPv4 + + "|" + ipv4 + + + ")" + + // end match for protocol / username / password / host + + ")" + + // optional port number + + "(?:\\:\\d{1,5})?" + + // plus optional path and query params (no unicode allowed here?) + + "(?:" + + "\\/(?:" + // some characters we'll accept because it's unlikely human language + // would use them after a URL unless they were part of the url + + "(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])" + + "|(?:\\%[a-f0-9]{2})" + // some characters are much more likely to be used AFTER a url and + // were not intended to be included in the url itself. Mostly end + // of sentence type things. It's also likely that the URL would + // still work if any of these characters were missing from the end + // because we parsed it incorrectly. For these characters to be accepted + // they must be followed by another character that we're reasonably + // sure is part of the url + + "|(?:[\\;\\?\\:\\.\\!\\'\\(\\)\\,\\=]+(?=(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])|(?:\\%[a-f0-9]{2})))" + + ")*" + + "|\\b|\$" + + ")"; + + // regex = XRegExp(regex,'gi'); + var linkRegExp = RegExp(linkRegExpString, 'gi'); + + var protocolRegExp = RegExp('^' + protocols, 'i'); + + // if url doesn't begin with a known protocol, add http by default + function ensureProtocol(url) { + if (!url.match(protocolRegExp)) { + url = "http://" + url; + } + return url; + } + + // look for links in the text + var LinkParser = { + parse: function (text) { + var links = []; + var match; + + while (match = linkRegExp.exec(text)) { + // console.log(matches); + var txt = match[0]; + var pos = match.index; + var len = txt.length; + var url = ensureProtocol(text); + links.push({ 'pos': pos, 'text': txt, 'len': len, 'url': url }); + } + + return links; + } + + }; + + window.LinkParser = LinkParser; + })(); + + var cache = {}; function isValidIpAddress(address) { - return 1 == LinkParser.parse(address).length + + var links = LinkParser.parse(address); + + return links.length == 1; } function isLocalIpAddress(address) { - return address = address.toLowerCase(), -1 !== address.indexOf("127.0.0.1") || -1 !== address.indexOf("localhost") + + address = address.toLowerCase(); + + if (address.indexOf('127.0.0.1') !== -1) { + return true; + } + if (address.indexOf('localhost') !== -1) { + return true; + } + + return false; } function getServerAddress(apiClient) { + var serverAddress = apiClient.serverAddress(); - if (isValidIpAddress(serverAddress) && !isLocalIpAddress(serverAddress)) return Promise.resolve(serverAddress); + + if (isValidIpAddress(serverAddress) && !isLocalIpAddress(serverAddress)) { + return Promise.resolve(serverAddress); + } + var cachedValue = getCachedValue(serverAddress); - return cachedValue ? Promise.resolve(cachedValue) : apiClient.getEndpointInfo().then(function(endpoint) { - return endpoint.IsInNetwork ? apiClient.getPublicSystemInfo().then(function(info) { - return addToCache(serverAddress, info.LocalAddress), info.LocalAddress - }) : (addToCache(serverAddress, serverAddress), serverAddress) - }) + if (cachedValue) { + return Promise.resolve(cachedValue); + } + + return apiClient.getEndpointInfo().then(function (endpoint) { + if (endpoint.IsInNetwork) { + return apiClient.getPublicSystemInfo().then(function (info) { + addToCache(serverAddress, info.LocalAddress); + return info.LocalAddress; + }); + } else { + addToCache(serverAddress, serverAddress); + return serverAddress; + } + }); } function clearCache() { - cache = {} + cache = {}; } function addToCache(key, value) { cache[key] = { value: value, - time: (new Date).getTime() - } + time: new Date().getTime() + }; } function getCachedValue(key) { + var obj = cache[key]; - return obj && (new Date).getTime() - obj.time < 18e4 ? obj.value : null - }! function() { - function ensureProtocol(url) { - return url.match(protocolRegExp) || (url = "http://" + url), url + + if (obj && (new Date().getTime() - obj.time) < 180000) { + return obj.value; } - var protocols = "(?:(?:http|https|rtsp|ftp):\\/\\/)", - linkRegExp = RegExp("(?:(?:(?:http|https|rtsp|ftp):\\/\\/)?(?:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,64}(?:\\:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,25})?\\@)?(?:((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?|(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])))(?:\\:\\d{1,5})?(?:\\/(?:(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])|(?:\\%[a-f0-9]{2})|(?:[\\;\\?\\:\\.\\!\\'\\(\\)\\,\\=]+(?=(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])|(?:\\%[a-f0-9]{2}))))*|\\b|$)", "gi"), - protocolRegExp = RegExp("^" + protocols, "i"), - LinkParser = { - parse: function(text) { - for (var match, links = []; match = linkRegExp.exec(text);) { - var txt = match[0], - pos = match.index, - len = txt.length, - url = ensureProtocol(text); - links.push({ - pos: pos, - text: txt, - len: len, - url: url - }) - } - return links - } - }; - window.LinkParser = LinkParser - }(); - var cache = {}; - return events.on(ConnectionManager, "localusersignedin", clearCache), events.on(ConnectionManager, "localusersignedout", clearCache), { - getServerAddress: getServerAddress + + return null; } + + events.on(ConnectionManager, 'localusersignedin', clearCache); + events.on(ConnectionManager, 'localusersignedout', clearCache); + + return { + getServerAddress: getServerAddress + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/chromecast/chromecastplayer.js b/src/bower_components/emby-webcomponents/chromecast/chromecastplayer.js index 08b28d1aec..4f8b4cf952 100644 --- a/src/bower_components/emby-webcomponents/chromecast/chromecastplayer.js +++ b/src/bower_components/emby-webcomponents/chromecast/chromecastplayer.js @@ -1,139 +1,329 @@ -define(["appSettings", "userSettings", "playbackManager", "connectionManager", "globalize", "events", "require", "castSenderApiLoader"], function(appSettings, userSettings, playbackManager, connectionManager, globalize, events, require, castSenderApiLoader) { - "use strict"; +define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', 'globalize', 'events', 'require', 'castSenderApiLoader'], function (appSettings, userSettings, playbackManager, connectionManager, globalize, events, require, castSenderApiLoader) { + 'use strict'; + + // Based on https://github.com/googlecast/CastVideos-chrome/blob/master/CastVideos.js + var currentResolve; + var currentReject; + + var PlayerName = 'Chromecast'; function sendConnectionResult(isOk) { - var resolve = currentResolve, - reject = currentReject; - currentResolve = null, currentReject = null, isOk ? resolve && resolve() : reject ? reject() : playbackManager.removeActivePlayer(PlayerName) + + var resolve = currentResolve; + var reject = currentReject; + + currentResolve = null; + currentReject = null; + + if (isOk) { + if (resolve) { + resolve(); + } + } else { + if (reject) { + reject(); + } else { + playbackManager.removeActivePlayer(PlayerName); + } + } } + /** + * Constants of states for Chromecast device + **/ + var DEVICE_STATE = { + 'IDLE': 0, + 'ACTIVE': 1, + 'WARNING': 2, + 'ERROR': 3 + }; + + /** + * Constants of states for CastPlayer + **/ + var PLAYER_STATE = { + 'IDLE': 'IDLE', + 'LOADING': 'LOADING', + 'LOADED': 'LOADED', + 'PLAYING': 'PLAYING', + 'PAUSED': 'PAUSED', + 'STOPPED': 'STOPPED', + 'SEEKING': 'SEEKING', + 'ERROR': 'ERROR' + }; + + var applicationID = "F007D354"; + + // This is the beta version used for testing new changes + + //applicationID = '27C4EB5B'; + + var messageNamespace = 'urn:x-cast:com.connectsdk'; + + var CastPlayer = function () { + + /* device variables */ + // @type {DEVICE_STATE} A state for device + this.deviceState = DEVICE_STATE.IDLE; + + /* Cast player variables */ + // @type {Object} a chrome.cast.media.Media object + this.currentMediaSession = null; + + // @type {string} a chrome.cast.Session object + this.session = null; + // @type {PLAYER_STATE} A state for Cast media player + this.castPlayerState = PLAYER_STATE.IDLE; + + this.hasReceivers = false; + + // bind once - commit 2ebffc2271da0bc5e8b13821586aee2a2e3c7753 + this.errorHandler = this.onError.bind(this); + this.mediaStatusUpdateHandler = this.onMediaStatusUpdate.bind(this); + + this.initializeCastPlayer(); + }; + + /** + * Initialize Cast media player + * Initializes the API. Note that either successCallback and errorCallback will be + * invoked once the API has finished initialization. The sessionListener and + * receiverListener may be invoked at any time afterwards, and possibly more than once. + */ + CastPlayer.prototype.initializeCastPlayer = function () { + + var chrome = window.chrome; + + if (!chrome) { + return; + } + + if (!chrome.cast || !chrome.cast.isAvailable) { + + setTimeout(this.initializeCastPlayer.bind(this), 1000); + return; + } + + // request session + var sessionRequest = new chrome.cast.SessionRequest(applicationID); + var apiConfig = new chrome.cast.ApiConfig(sessionRequest, + this.sessionListener.bind(this), + this.receiverListener.bind(this), + "origin_scoped"); + + console.log('chromecast.initialize'); + + chrome.cast.initialize(apiConfig, this.onInitSuccess.bind(this), this.errorHandler); + + }; + + /** + * Callback function for init success + */ + CastPlayer.prototype.onInitSuccess = function () { + this.isInitialized = true; + console.log("chromecast init success"); + }; + + /** + * Generic error callback function + */ + CastPlayer.prototype.onError = function () { + console.log("chromecast error"); + }; + + /** + * @param {!Object} e A new session + * This handles auto-join when a page is reloaded + * When active session is detected, playback will automatically + * join existing session and occur in Cast mode and media + * status gets synced up with current media of the session + */ + CastPlayer.prototype.sessionListener = function (e) { + + this.session = e; + if (this.session) { + + //console.log('sessionListener ' + JSON.stringify(e)); + + if (this.session.media[0]) { + this.onMediaDiscovered('activeSession', this.session.media[0]); + } + + this.onSessionConnected(e); + } + }; + function alertText(text, title) { - require(["alert"], function(alert) { + require(['alert'], function (alert) { alert({ text: text, title: title - }) - }) + }); + }); } - function normalizeImages(state) { - if (state && state.NowPlayingItem) { - var item = state.NowPlayingItem; - item.ImageTags && item.ImageTags.Primary || item.PrimaryImageTag && (item.ImageTags = item.ImageTags || {}, item.ImageTags.Primary = item.PrimaryImageTag), item.BackdropImageTag && item.BackdropItemId === item.Id && (item.BackdropImageTags = [item.BackdropImageTag]), item.BackdropImageTag && item.BackdropItemId !== item.Id && (item.ParentBackdropImageTags = [item.BackdropImageTag], item.ParentBackdropItemId = item.BackdropItemId) + CastPlayer.prototype.messageListener = function (namespace, message) { + + if (typeof (message) === 'string') { + message = JSON.parse(message); } - } - function getItemsForPlayback(apiClient, query) { - var userId = apiClient.getCurrentUserId(); - return query.Ids && 1 === query.Ids.split(",").length ? apiClient.getItem(userId, query.Ids.split(",")).then(function(item) { - return { - Items: [item], - TotalRecordCount: 1 - } - }) : (query.Limit = query.Limit || 100, query.ExcludeLocationTypes = "Virtual", query.EnableTotalRecordCount = !1, apiClient.getItems(userId, query)) - } + if (message.type === 'playbackerror') { - function bindEventForRelay(instance, eventName) { - events.on(instance._castPlayer, eventName, function(e, data) { - var state = instance.getPlayerStateInternal(data); - events.trigger(instance, eventName, [state]) - }) - } - - function initializeChromecast() { - var instance = this; - instance._castPlayer = new CastPlayer, document.dispatchEvent(new CustomEvent("chromecastloaded", { - detail: { - player: instance - } - })), events.on(instance._castPlayer, "connect", function(e) { - currentResolve ? sendConnectionResult(!0) : playbackManager.setActivePlayer(PlayerName, instance.getCurrentTargetInfo()), console.log("cc: connect"), instance.lastPlayerData = null - }), events.on(instance._castPlayer, "playbackstart", function(e, data) { - console.log("cc: playbackstart"), instance._castPlayer.initializeCastPlayer(); - var state = instance.getPlayerStateInternal(data); - events.trigger(instance, "playbackstart", [state]) - }), events.on(instance._castPlayer, "playbackstop", function(e, data) { - console.log("cc: playbackstop"); - var state = instance.getPlayerStateInternal(data); - events.trigger(instance, "playbackstop", [state]), instance.lastPlayerData = {} - }), events.on(instance._castPlayer, "playbackprogress", function(e, data) { - var state = instance.getPlayerStateInternal(data); - events.trigger(instance, "timeupdate", [state]) - }), bindEventForRelay(instance, "timeupdate"), bindEventForRelay(instance, "pause"), bindEventForRelay(instance, "unpause"), bindEventForRelay(instance, "volumechange"), bindEventForRelay(instance, "repeatmodechange"), events.on(instance._castPlayer, "playstatechange", function(e, data) { - var state = instance.getPlayerStateInternal(data); - events.trigger(instance, "pause", [state]) - }) - } - - function ChromecastPlayer() { - this.name = PlayerName, this.type = "mediaplayer", this.id = "chromecast", this.isLocalPlayer = !1, this.lastPlayerData = {}, castSenderApiLoader.load().then(initializeChromecast.bind(this)) - } - var currentResolve, currentReject, PlayerName = "Chromecast", - DEVICE_STATE = { - IDLE: 0, - ACTIVE: 1, - WARNING: 2, - ERROR: 3 - }, - PLAYER_STATE = { - IDLE: "IDLE", - LOADING: "LOADING", - LOADED: "LOADED", - PLAYING: "PLAYING", - PAUSED: "PAUSED", - STOPPED: "STOPPED", - SEEKING: "SEEKING", - ERROR: "ERROR" - }, - CastPlayer = function() { - this.deviceState = DEVICE_STATE.IDLE, this.currentMediaSession = null, this.session = null, this.castPlayerState = PLAYER_STATE.IDLE, this.hasReceivers = !1, this.errorHandler = this.onError.bind(this), this.mediaStatusUpdateHandler = this.onMediaStatusUpdate.bind(this), this.initializeCastPlayer() - }; - return CastPlayer.prototype.initializeCastPlayer = function() { - var chrome = window.chrome; - if (chrome) { - if (!chrome.cast || !chrome.cast.isAvailable) return void setTimeout(this.initializeCastPlayer.bind(this), 1e3); - var sessionRequest = new chrome.cast.SessionRequest("2D4B1DA3"), - apiConfig = new chrome.cast.ApiConfig(sessionRequest, this.sessionListener.bind(this), this.receiverListener.bind(this), "origin_scoped"); - console.log("chromecast.initialize"), chrome.cast.initialize(apiConfig, this.onInitSuccess.bind(this), this.errorHandler) - } - }, CastPlayer.prototype.onInitSuccess = function() { - this.isInitialized = !0, console.log("chromecast init success") - }, CastPlayer.prototype.onError = function() { - console.log("chromecast error") - }, CastPlayer.prototype.sessionListener = function(e) { - this.session = e, this.session && (this.session.media[0] && this.onMediaDiscovered("activeSession", this.session.media[0]), this.onSessionConnected(e)) - }, CastPlayer.prototype.messageListener = function(namespace, message) { - if ("string" == typeof message && (message = JSON.parse(message)), "playbackerror" === message.type) { var errorCode = message.data; - setTimeout(function() { - alertText(globalize.translate("MessagePlaybackError" + errorCode), globalize.translate("HeaderPlaybackError")) - }, 300) - } else "connectionerror" === message.type ? setTimeout(function() { - alertText(globalize.translate("MessageChromecastConnectionError"), globalize.translate("HeaderError")) - }, 300) : message.type && events.trigger(this, message.type, [message.data]) - }, CastPlayer.prototype.receiverListener = function(e) { - this.hasReceivers = "available" === e - }, CastPlayer.prototype.sessionUpdateListener = function(isAlive) { - isAlive || (this.session = null, this.deviceState = DEVICE_STATE.IDLE, this.castPlayerState = PLAYER_STATE.IDLE, this.currentMediaSession = null, sendConnectionResult(!1)) - }, CastPlayer.prototype.launchApp = function() { - chrome.cast.requestSession(this.onRequestSessionSuccess.bind(this), this.onLaunchError.bind(this)) - }, CastPlayer.prototype.onRequestSessionSuccess = function(e) { - this.onSessionConnected(e) - }, CastPlayer.prototype.onSessionConnected = function(session) { - this.session = session, this.deviceState = DEVICE_STATE.ACTIVE, this.session.addMessageListener("urn:x-cast:com.connectsdk", this.messageListener.bind(this)), this.session.addMediaListener(this.sessionMediaListener.bind(this)), this.session.addUpdateListener(this.sessionUpdateListener.bind(this)), events.trigger(this, "connect"), this.sendMessage({ + + setTimeout(function () { + alertText(globalize.translate('MessagePlaybackError' + errorCode), globalize.translate('HeaderPlaybackError')); + }, 300); + + } + else if (message.type === 'connectionerror') { + + setTimeout(function () { + alertText(globalize.translate('MessageChromecastConnectionError'), globalize.translate('HeaderError')); + }, 300); + + } + else if (message.type) { + events.trigger(this, message.type, [message.data]); + } + }; + + /** + * @param {string} e Receiver availability + * This indicates availability of receivers but + * does not provide a list of device IDs + */ + CastPlayer.prototype.receiverListener = function (e) { + + if (e === 'available') { + //console.log("chromecast receiver found"); + this.hasReceivers = true; + } + else { + //console.log("chromecast receiver list empty"); + this.hasReceivers = false; + } + }; + + /** + * session update listener + */ + CastPlayer.prototype.sessionUpdateListener = function (isAlive) { + + //console.log('sessionUpdateListener alive: ' + isAlive); + + if (isAlive) { + } + else { + this.session = null; + this.deviceState = DEVICE_STATE.IDLE; + this.castPlayerState = PLAYER_STATE.IDLE; + + //console.log('sessionUpdateListener: setting currentMediaSession to null'); + this.currentMediaSession = null; + + sendConnectionResult(false); + } + }; + + /** + * Requests that a receiver application session be created or joined. By default, the SessionRequest + * passed to the API at initialization time is used; this may be overridden by passing a different + * session request in opt_sessionRequest. + */ + CastPlayer.prototype.launchApp = function () { + //console.log("chromecast launching app..."); + chrome.cast.requestSession(this.onRequestSessionSuccess.bind(this), this.onLaunchError.bind(this)); + }; + + /** + * Callback function for request session success + * @param {Object} e A chrome.cast.Session object + */ + CastPlayer.prototype.onRequestSessionSuccess = function (e) { + + //console.log("chromecast session success: " + e.sessionId); + this.onSessionConnected(e); + }; + + CastPlayer.prototype.onSessionConnected = function (session) { + + this.session = session; + + this.deviceState = DEVICE_STATE.ACTIVE; + + this.session.addMessageListener(messageNamespace, this.messageListener.bind(this)); + this.session.addMediaListener(this.sessionMediaListener.bind(this)); + this.session.addUpdateListener(this.sessionUpdateListener.bind(this)); + + events.trigger(this, 'connect'); + + this.sendMessage({ options: {}, - command: "Identify" - }) - }, CastPlayer.prototype.sessionMediaListener = function(e) { - this.currentMediaSession = e, this.currentMediaSession.addUpdateListener(this.mediaStatusUpdateHandler) - }, CastPlayer.prototype.onLaunchError = function() { - this.deviceState = DEVICE_STATE.ERROR, sendConnectionResult(!1) - }, CastPlayer.prototype.stopApp = function() { - this.session && this.session.stop(this.onStopAppSuccess.bind(this, "Session stopped"), this.errorHandler) - }, CastPlayer.prototype.onStopAppSuccess = function(message) { - this.deviceState = DEVICE_STATE.IDLE, this.castPlayerState = PLAYER_STATE.IDLE, this.currentMediaSession = null - }, CastPlayer.prototype.loadMedia = function(options, command) { - return this.session ? (options.items = options.items.map(function(i) { + command: 'Identify' + }); + }; + + /** + * session update listener + */ + CastPlayer.prototype.sessionMediaListener = function (e) { + + //console.log('sessionMediaListener'); + this.currentMediaSession = e; + this.currentMediaSession.addUpdateListener(this.mediaStatusUpdateHandler); + }; + + /** + * Callback function for launch error + */ + CastPlayer.prototype.onLaunchError = function () { + //console.log("chromecast launch error"); + this.deviceState = DEVICE_STATE.ERROR; + + sendConnectionResult(false); + }; + + /** + * Stops the running receiver application associated with the session. + */ + CastPlayer.prototype.stopApp = function () { + + if (this.session) { + this.session.stop(this.onStopAppSuccess.bind(this, 'Session stopped'), + this.errorHandler); + } + + }; + + /** + * Callback function for stop app success + */ + CastPlayer.prototype.onStopAppSuccess = function (message) { + //console.log(message); + this.deviceState = DEVICE_STATE.IDLE; + this.castPlayerState = PLAYER_STATE.IDLE; + + //console.log('onStopAppSuccess: setting currentMediaSession to null'); + this.currentMediaSession = null; + }; + + /** + * Loads media into a running receiver application + * @param {Number} mediaIndex An index number to indicate current media content + */ + CastPlayer.prototype.loadMedia = function (options, command) { + + if (!this.session) { + //console.log("no session"); + return Promise.reject(); + } + + // Convert the items to smaller stubs to send the minimal amount of information + options.items = options.items.map(function (i) { + return { Id: i.Id, ServerId: i.ServerId, @@ -141,18 +331,37 @@ define(["appSettings", "userSettings", "playbackManager", "connectionManager", " Type: i.Type, MediaType: i.MediaType, IsFolder: i.IsFolder - } - }), this.sendMessage({ + }; + }); + + return this.sendMessage({ options: options, command: command - })) : Promise.reject() - }, CastPlayer.prototype.sendMessage = function(message) { - var player = this, - receiverName = null, - session = player.session; - session && session.receiver && session.receiver.friendlyName && (receiverName = session.receiver.friendlyName); + }); + }; + + CastPlayer.prototype.sendMessage = function (message) { + + var player = this; + + var receiverName = null; + + var session = player.session; + + if (session && session.receiver && session.receiver.friendlyName) { + receiverName = session.receiver.friendlyName; + } + var apiClient; - apiClient = message.options && message.options.ServerId ? connectionManager.getApiClient(message.options.ServerId) : message.options && message.options.items && message.options.items.length ? connectionManager.getApiClient(message.options.items[0].ServerId) : connectionManager.currentApiClient(), message = Object.assign(message, { + if (message.options && message.options.ServerId) { + apiClient = connectionManager.getApiClient(message.options.ServerId); + } else if (message.options && message.options.items && message.options.items.length) { + apiClient = connectionManager.getApiClient(message.options.items[0].ServerId); + } else { + apiClient = connectionManager.currentApiClient(); + } + + message = Object.assign(message, { userId: apiClient.getCurrentUserId(), deviceId: apiClient.deviceId(), accessToken: apiClient.accessToken(), @@ -161,265 +370,755 @@ define(["appSettings", "userSettings", "playbackManager", "connectionManager", " serverVersion: apiClient.serverVersion(), receiverName: receiverName }); + var bitrateSetting = appSettings.maxChromecastBitrate(); - return bitrateSetting && (message.maxBitrate = bitrateSetting), message.options && message.options.items && (message.subtitleAppearance = userSettings.getSubtitleAppearanceSettings(), message.subtitleBurnIn = appSettings.get("subtitleburnin") || ""), new Promise(function(resolve, reject) { - require(["chromecastHelper"], function(chromecastHelper) { - chromecastHelper.getServerAddress(apiClient).then(function(serverAddress) { - message.serverAddress = serverAddress, player.sendMessageInternal(message).then(resolve, reject) - }, reject) - }) - }) - }, CastPlayer.prototype.sendMessageInternal = function(message) { - return message = JSON.stringify(message), this.session.sendMessage("urn:x-cast:com.connectsdk", message, this.onPlayCommandSuccess.bind(this), this.errorHandler), Promise.resolve() - }, CastPlayer.prototype.onPlayCommandSuccess = function() {}, CastPlayer.prototype.onMediaDiscovered = function(how, mediaSession) { - this.currentMediaSession = mediaSession, "loadMedia" === how && (this.castPlayerState = PLAYER_STATE.PLAYING), "activeSession" === how && (this.castPlayerState = mediaSession.playerState), this.currentMediaSession.addUpdateListener(this.mediaStatusUpdateHandler) - }, CastPlayer.prototype.onMediaStatusUpdate = function(e) { - !1 === e && (this.castPlayerState = PLAYER_STATE.IDLE) - }, CastPlayer.prototype.setReceiverVolume = function(mute, vol) { - this.currentMediaSession && (mute ? this.session.setReceiverMuted(!0, this.mediaCommandSuccessCallback.bind(this), this.errorHandler) : this.session.setReceiverVolumeLevel(vol || 1, this.mediaCommandSuccessCallback.bind(this), this.errorHandler)) - }, CastPlayer.prototype.mute = function() { - this.setReceiverVolume(!0) - }, CastPlayer.prototype.mediaCommandSuccessCallback = function(info, e) {}, ChromecastPlayer.prototype.tryPair = function(target) { + if (bitrateSetting) { + message.maxBitrate = bitrateSetting; + } + + if (message.options && message.options.items) { + message.subtitleAppearance = userSettings.getSubtitleAppearanceSettings(); + message.subtitleBurnIn = appSettings.get('subtitleburnin') || ''; + } + + return new Promise(function (resolve, reject) { + + require(['chromecastHelper'], function (chromecastHelper) { + + chromecastHelper.getServerAddress(apiClient).then(function (serverAddress) { + message.serverAddress = serverAddress; + player.sendMessageInternal(message).then(resolve, reject); + + }, reject); + }); + }); + }; + + CastPlayer.prototype.sendMessageInternal = function (message) { + + message = JSON.stringify(message); + //console.log(message); + + this.session.sendMessage(messageNamespace, message, this.onPlayCommandSuccess.bind(this), this.errorHandler); + return Promise.resolve(); + }; + + CastPlayer.prototype.onPlayCommandSuccess = function () { + //console.log('Message was sent to receiver ok.'); + }; + + /** + * Callback function for loadMedia success + * @param {Object} mediaSession A new media object. + */ + CastPlayer.prototype.onMediaDiscovered = function (how, mediaSession) { + + //console.log("chromecast new media session ID:" + mediaSession.mediaSessionId + ' (' + how + ')'); + this.currentMediaSession = mediaSession; + + if (how === 'loadMedia') { + this.castPlayerState = PLAYER_STATE.PLAYING; + } + + if (how === 'activeSession') { + this.castPlayerState = mediaSession.playerState; + } + + this.currentMediaSession.addUpdateListener(this.mediaStatusUpdateHandler); + }; + + /** + * Callback function for media status update from receiver + * @param {!Boolean} e true/false + */ + CastPlayer.prototype.onMediaStatusUpdate = function (e) { + + if (e === false) { + this.castPlayerState = PLAYER_STATE.IDLE; + } + //console.log("chromecast updating media: " + e); + }; + + /** + * Set media volume in Cast mode + * @param {Boolean} mute A boolean + */ + CastPlayer.prototype.setReceiverVolume = function (mute, vol) { + + if (!this.currentMediaSession) { + //console.log('this.currentMediaSession is null'); + return; + } + + if (!mute) { + + this.session.setReceiverVolumeLevel((vol || 1), + this.mediaCommandSuccessCallback.bind(this), + this.errorHandler); + } + else { + this.session.setReceiverMuted(true, + this.mediaCommandSuccessCallback.bind(this), + this.errorHandler); + } + }; + + /** + * Mute CC + */ + CastPlayer.prototype.mute = function () { + this.setReceiverVolume(true); + }; + + /** + * Callback function for media command success + */ + CastPlayer.prototype.mediaCommandSuccessCallback = function (info, e) { + //console.log(info); + }; + + function normalizeImages(state) { + + if (state && state.NowPlayingItem) { + + var item = state.NowPlayingItem; + + if (!item.ImageTags || !item.ImageTags.Primary) { + if (item.PrimaryImageTag) { + item.ImageTags = item.ImageTags || {}; + item.ImageTags.Primary = item.PrimaryImageTag; + } + } + if (item.BackdropImageTag && item.BackdropItemId === item.Id) { + item.BackdropImageTags = [item.BackdropImageTag]; + } + if (item.BackdropImageTag && item.BackdropItemId !== item.Id) { + item.ParentBackdropImageTags = [item.BackdropImageTag]; + item.ParentBackdropItemId = item.BackdropItemId; + } + } + } + + function getItemsForPlayback(apiClient, query) { + + var userId = apiClient.getCurrentUserId(); + + if (query.Ids && query.Ids.split(',').length === 1) { + return apiClient.getItem(userId, query.Ids.split(',')).then(function (item) { + return { + Items: [item], + TotalRecordCount: 1 + }; + }); + } + else { + + query.Limit = query.Limit || 100; + query.ExcludeLocationTypes = "Virtual"; + query.EnableTotalRecordCount = false; + + return apiClient.getItems(userId, query); + } + } + + function bindEventForRelay(instance, eventName) { + + events.on(instance._castPlayer, eventName, function (e, data) { + + //console.log('cc: ' + eventName); + var state = instance.getPlayerStateInternal(data); + + events.trigger(instance, eventName, [state]); + }); + } + + function initializeChromecast() { + + var instance = this; + instance._castPlayer = new CastPlayer(); + + // To allow the native android app to override + document.dispatchEvent(new CustomEvent("chromecastloaded", { + detail: { + player: instance + } + })); + + events.on(instance._castPlayer, "connect", function (e) { + + if (currentResolve) { + sendConnectionResult(true); + } else { + playbackManager.setActivePlayer(PlayerName, instance.getCurrentTargetInfo()); + } + + console.log('cc: connect'); + // Reset this so that statechange will fire + instance.lastPlayerData = null; + }); + + events.on(instance._castPlayer, "playbackstart", function (e, data) { + + console.log('cc: playbackstart'); + + instance._castPlayer.initializeCastPlayer(); + + var state = instance.getPlayerStateInternal(data); + events.trigger(instance, "playbackstart", [state]); + }); + + events.on(instance._castPlayer, "playbackstop", function (e, data) { + + console.log('cc: playbackstop'); + var state = instance.getPlayerStateInternal(data); + + events.trigger(instance, "playbackstop", [state]); + + // Reset this so the next query doesn't make it appear like content is playing. + instance.lastPlayerData = {}; + }); + + events.on(instance._castPlayer, "playbackprogress", function (e, data) { + + //console.log('cc: positionchange'); + var state = instance.getPlayerStateInternal(data); + + events.trigger(instance, "timeupdate", [state]); + }); + + bindEventForRelay(instance, 'timeupdate'); + bindEventForRelay(instance, 'pause'); + bindEventForRelay(instance, 'unpause'); + bindEventForRelay(instance, 'volumechange'); + bindEventForRelay(instance, 'repeatmodechange'); + + events.on(instance._castPlayer, "playstatechange", function (e, data) { + + //console.log('cc: playstatechange'); + var state = instance.getPlayerStateInternal(data); + + events.trigger(instance, "pause", [state]); + }); + } + + function ChromecastPlayer() { + + // playbackManager needs this + this.name = PlayerName; + this.type = 'mediaplayer'; + this.id = 'chromecast'; + this.isLocalPlayer = false; + this.lastPlayerData = {}; + + castSenderApiLoader.load().then(initializeChromecast.bind(this)); + } + + ChromecastPlayer.prototype.tryPair = function (target) { + var castPlayer = this._castPlayer; - return castPlayer.deviceState !== DEVICE_STATE.ACTIVE && castPlayer.isInitialized ? new Promise(function(resolve, reject) { - currentResolve = resolve, currentReject = reject, castPlayer.launchApp() - }) : (currentResolve = null, currentReject = null, Promise.reject()) - }, ChromecastPlayer.prototype.getTargets = function() { + + if (castPlayer.deviceState !== DEVICE_STATE.ACTIVE && castPlayer.isInitialized) { + + return new Promise(function (resolve, reject) { + currentResolve = resolve; + currentReject = reject; + + castPlayer.launchApp(); + }); + } else { + + currentResolve = null; + currentReject = null; + + return Promise.reject(); + } + }; + + ChromecastPlayer.prototype.getTargets = function () { + var targets = []; - return this._castPlayer && this._castPlayer.hasReceivers && targets.push(this.getCurrentTargetInfo()), Promise.resolve(targets) - }, ChromecastPlayer.prototype.getCurrentTargetInfo = function() { - var appName = null, - castPlayer = this._castPlayer; - return castPlayer.session && castPlayer.session.receiver && castPlayer.session.receiver.friendlyName && (appName = castPlayer.session.receiver.friendlyName), { + + if (this._castPlayer && this._castPlayer.hasReceivers) { + targets.push(this.getCurrentTargetInfo()); + } + + return Promise.resolve(targets); + }; + + // This is a privately used method + ChromecastPlayer.prototype.getCurrentTargetInfo = function () { + + var appName = null; + + var castPlayer = this._castPlayer; + + if (castPlayer.session && castPlayer.session.receiver && castPlayer.session.receiver.friendlyName) { + appName = castPlayer.session.receiver.friendlyName; + } + + return { name: PlayerName, id: PlayerName, playerName: PlayerName, playableMediaTypes: ["Audio", "Video"], - isLocalPlayer: !1, + isLocalPlayer: false, appName: PlayerName, deviceName: appName, - supportedCommands: ["VolumeUp", "VolumeDown", "Mute", "Unmute", "ToggleMute", "SetVolume", "SetAudioStreamIndex", "SetSubtitleStreamIndex", "DisplayContent", "SetRepeatMode", "EndSession", "PlayMediaSource", "PlayTrailers"] + supportedCommands: [ + "VolumeUp", + "VolumeDown", + "Mute", + "Unmute", + "ToggleMute", + "SetVolume", + "SetAudioStreamIndex", + "SetSubtitleStreamIndex", + "DisplayContent", + "SetRepeatMode", + "EndSession", + "PlayMediaSource", + "PlayTrailers" + ] + }; + }; + + ChromecastPlayer.prototype.getPlayerStateInternal = function (data) { + + var triggerStateChange = false; + if (data && !this.lastPlayerData) { + triggerStateChange = true; } - }, ChromecastPlayer.prototype.getPlayerStateInternal = function(data) { - var triggerStateChange = !1; - return data && !this.lastPlayerData && (triggerStateChange = !0), data = data || this.lastPlayerData, this.lastPlayerData = data, normalizeImages(data), triggerStateChange && events.trigger(this, "statechange", [data]), data - }, ChromecastPlayer.prototype.playWithCommand = function(options, command) { + + data = data || this.lastPlayerData; + this.lastPlayerData = data; + + normalizeImages(data); + + //console.log(JSON.stringify(data)); + + if (triggerStateChange) { + events.trigger(this, "statechange", [data]); + } + + return data; + }; + + ChromecastPlayer.prototype.playWithCommand = function (options, command) { + if (!options.items) { - var apiClient = connectionManager.getApiClient(options.serverId), - instance = this; - return apiClient.getItem(apiClient.getCurrentUserId(), options.ids[0]).then(function(item) { - return options.items = [item], instance.playWithCommand(options, command) - }) + var apiClient = connectionManager.getApiClient(options.serverId); + var instance = this; + + return apiClient.getItem(apiClient.getCurrentUserId(), options.ids[0]).then(function (item) { + + options.items = [item]; + return instance.playWithCommand(options, command); + }); } - return this._castPlayer.loadMedia(options, command) - }, ChromecastPlayer.prototype.seek = function(position) { - position = parseInt(position), position /= 1e7, this._castPlayer.sendMessage({ + + return this._castPlayer.loadMedia(options, command); + }; + + ChromecastPlayer.prototype.seek = function (position) { + + position = parseInt(position); + + position = position / 10000000; + + this._castPlayer.sendMessage({ options: { position: position }, - command: "Seek" - }) - }, ChromecastPlayer.prototype.setAudioStreamIndex = function(index) { + command: 'Seek' + }); + }; + + ChromecastPlayer.prototype.setAudioStreamIndex = function (index) { this._castPlayer.sendMessage({ options: { index: index }, - command: "SetAudioStreamIndex" - }) - }, ChromecastPlayer.prototype.setSubtitleStreamIndex = function(index) { + command: 'SetAudioStreamIndex' + }); + }; + + ChromecastPlayer.prototype.setSubtitleStreamIndex = function (index) { this._castPlayer.sendMessage({ options: { index: index }, - command: "SetSubtitleStreamIndex" - }) - }, ChromecastPlayer.prototype.setMaxStreamingBitrate = function(options) { + command: 'SetSubtitleStreamIndex' + }); + }; + + ChromecastPlayer.prototype.setMaxStreamingBitrate = function (options) { + this._castPlayer.sendMessage({ options: options, - command: "SetMaxStreamingBitrate" - }) - }, ChromecastPlayer.prototype.isFullscreen = function() { + command: 'SetMaxStreamingBitrate' + }); + }; + + ChromecastPlayer.prototype.isFullscreen = function () { var state = this.lastPlayerData || {}; - return state = state.PlayState || {}, state.IsFullscreen - }, ChromecastPlayer.prototype.nextTrack = function() { + state = state.PlayState || {}; + return state.IsFullscreen; + }; + + ChromecastPlayer.prototype.nextTrack = function () { this._castPlayer.sendMessage({ options: {}, - command: "NextTrack" - }) - }, ChromecastPlayer.prototype.previousTrack = function() { + command: 'NextTrack' + }); + }; + + ChromecastPlayer.prototype.previousTrack = function () { this._castPlayer.sendMessage({ options: {}, - command: "PreviousTrack" - }) - }, ChromecastPlayer.prototype.volumeDown = function() { + command: 'PreviousTrack' + }); + }; + + ChromecastPlayer.prototype.volumeDown = function () { + this._castPlayer.sendMessage({ options: {}, - command: "VolumeDown" - }) - }, ChromecastPlayer.prototype.endSession = function() { + command: 'VolumeDown' + }); + }; + + ChromecastPlayer.prototype.endSession = function () { + var instance = this; - this.stop().then(function() { - setTimeout(function() { - instance._castPlayer.stopApp() - }, 1e3) - }) - }, ChromecastPlayer.prototype.volumeUp = function() { + + this.stop().then(function () { + setTimeout(function () { + instance._castPlayer.stopApp(); + }, 1000); + }); + }; + + ChromecastPlayer.prototype.volumeUp = function () { + this._castPlayer.sendMessage({ options: {}, - command: "VolumeUp" - }) - }, ChromecastPlayer.prototype.setVolume = function(vol) { - vol = Math.min(vol, 100), vol = Math.max(vol, 0), this._castPlayer.sendMessage({ + command: 'VolumeUp' + }); + }; + + ChromecastPlayer.prototype.setVolume = function (vol) { + + vol = Math.min(vol, 100); + vol = Math.max(vol, 0); + + this._castPlayer.sendMessage({ options: { volume: vol }, - command: "SetVolume" - }) - }, ChromecastPlayer.prototype.unpause = function() { + command: 'SetVolume' + }); + }; + + ChromecastPlayer.prototype.unpause = function () { this._castPlayer.sendMessage({ options: {}, - command: "Unpause" - }) - }, ChromecastPlayer.prototype.playPause = function() { + command: 'Unpause' + }); + }; + + ChromecastPlayer.prototype.playPause = function () { this._castPlayer.sendMessage({ options: {}, - command: "PlayPause" - }) - }, ChromecastPlayer.prototype.pause = function() { + command: 'PlayPause' + }); + }; + + ChromecastPlayer.prototype.pause = function () { this._castPlayer.sendMessage({ options: {}, - command: "Pause" - }) - }, ChromecastPlayer.prototype.stop = function() { + command: 'Pause' + }); + }; + + ChromecastPlayer.prototype.stop = function () { return this._castPlayer.sendMessage({ options: {}, - command: "Stop" - }) - }, ChromecastPlayer.prototype.displayContent = function(options) { + command: 'Stop' + }); + }; + + ChromecastPlayer.prototype.displayContent = function (options) { + this._castPlayer.sendMessage({ options: options, - command: "DisplayContent" - }) - }, ChromecastPlayer.prototype.setMute = function(isMuted) { + command: 'DisplayContent' + }); + }; + + ChromecastPlayer.prototype.setMute = function (isMuted) { + var castPlayer = this._castPlayer; - isMuted ? castPlayer.sendMessage({ - options: {}, - command: "Mute" - }) : castPlayer.sendMessage({ - options: {}, - command: "Unmute" - }) - }, ChromecastPlayer.prototype.getRepeatMode = function() { + + if (isMuted) { + castPlayer.sendMessage({ + options: {}, + command: 'Mute' + }); + } else { + castPlayer.sendMessage({ + options: {}, + command: 'Unmute' + }); + } + }; + + ChromecastPlayer.prototype.getRepeatMode = function () { var state = this.lastPlayerData || {}; - return state = state.PlayState || {}, state.RepeatMode - }, ChromecastPlayer.prototype.playTrailers = function(item) { + state = state.PlayState || {}; + return state.RepeatMode; + }; + + ChromecastPlayer.prototype.playTrailers = function (item) { + this._castPlayer.sendMessage({ options: { ItemId: item.Id, ServerId: item.ServerId }, - command: "PlayTrailers" - }) - }, ChromecastPlayer.prototype.setRepeatMode = function(mode) { + command: 'PlayTrailers' + }); + }; + + ChromecastPlayer.prototype.setRepeatMode = function (mode) { this._castPlayer.sendMessage({ options: { RepeatMode: mode }, - command: "SetRepeatMode" - }) - }, ChromecastPlayer.prototype.toggleMute = function() { + command: 'SetRepeatMode' + }); + }; + + ChromecastPlayer.prototype.toggleMute = function () { + this._castPlayer.sendMessage({ options: {}, - command: "ToggleMute" - }) - }, ChromecastPlayer.prototype.audioTracks = function() { + command: 'ToggleMute' + }); + }; + + ChromecastPlayer.prototype.audioTracks = function () { var state = this.lastPlayerData || {}; - return state = state.NowPlayingItem || {}, (state.MediaStreams || []).filter(function(s) { - return "Audio" === s.Type - }) - }, ChromecastPlayer.prototype.getAudioStreamIndex = function() { + state = state.NowPlayingItem || {}; + var streams = state.MediaStreams || []; + return streams.filter(function (s) { + return s.Type === 'Audio'; + }); + }; + + ChromecastPlayer.prototype.getAudioStreamIndex = function () { var state = this.lastPlayerData || {}; - return state = state.PlayState || {}, state.AudioStreamIndex - }, ChromecastPlayer.prototype.subtitleTracks = function() { + state = state.PlayState || {}; + return state.AudioStreamIndex; + }; + + ChromecastPlayer.prototype.subtitleTracks = function () { var state = this.lastPlayerData || {}; - return state = state.NowPlayingItem || {}, (state.MediaStreams || []).filter(function(s) { - return "Subtitle" === s.Type - }) - }, ChromecastPlayer.prototype.getSubtitleStreamIndex = function() { + state = state.NowPlayingItem || {}; + var streams = state.MediaStreams || []; + return streams.filter(function (s) { + return s.Type === 'Subtitle'; + }); + }; + + ChromecastPlayer.prototype.getSubtitleStreamIndex = function () { var state = this.lastPlayerData || {}; - return state = state.PlayState || {}, state.SubtitleStreamIndex - }, ChromecastPlayer.prototype.getMaxStreamingBitrate = function() { + state = state.PlayState || {}; + return state.SubtitleStreamIndex; + }; + + ChromecastPlayer.prototype.getMaxStreamingBitrate = function () { var state = this.lastPlayerData || {}; - return state = state.PlayState || {}, state.MaxStreamingBitrate - }, ChromecastPlayer.prototype.getVolume = function() { + state = state.PlayState || {}; + return state.MaxStreamingBitrate; + }; + + ChromecastPlayer.prototype.getVolume = function () { + var state = this.lastPlayerData || {}; - return state = state.PlayState || {}, null == state.VolumeLevel ? 100 : state.VolumeLevel - }, ChromecastPlayer.prototype.isPlaying = function() { - return null != (this.lastPlayerData || {}).NowPlayingItem - }, ChromecastPlayer.prototype.isPlayingVideo = function() { + state = state.PlayState || {}; + + return state.VolumeLevel == null ? 100 : state.VolumeLevel; + }; + + ChromecastPlayer.prototype.isPlaying = function () { var state = this.lastPlayerData || {}; - return state = state.NowPlayingItem || {}, "Video" === state.MediaType - }, ChromecastPlayer.prototype.isPlayingAudio = function() { + return state.NowPlayingItem != null; + }; + + ChromecastPlayer.prototype.isPlayingVideo = function () { var state = this.lastPlayerData || {}; - return state = state.NowPlayingItem || {}, "Audio" === state.MediaType - }, ChromecastPlayer.prototype.currentTime = function(val) { - if (null != val) return this.seek(val); + state = state.NowPlayingItem || {}; + return state.MediaType === 'Video'; + }; + + ChromecastPlayer.prototype.isPlayingAudio = function () { var state = this.lastPlayerData || {}; - return state = state.PlayState || {}, state.PositionTicks - }, ChromecastPlayer.prototype.duration = function() { + state = state.NowPlayingItem || {}; + return state.MediaType === 'Audio'; + }; + + ChromecastPlayer.prototype.currentTime = function (val) { + + if (val != null) { + return this.seek(val); + } + var state = this.lastPlayerData || {}; - return state = state.NowPlayingItem || {}, state.RunTimeTicks - }, ChromecastPlayer.prototype.getBufferedRanges = function() { + state = state.PlayState || {}; + return state.PositionTicks; + }; + + ChromecastPlayer.prototype.duration = function () { var state = this.lastPlayerData || {}; - return state = state.PlayState || {}, state.BufferedRanges || [] - }, ChromecastPlayer.prototype.paused = function() { + state = state.NowPlayingItem || {}; + return state.RunTimeTicks; + }; + + ChromecastPlayer.prototype.getBufferedRanges = function () { var state = this.lastPlayerData || {}; - return state = state.PlayState || {}, state.IsPaused - }, ChromecastPlayer.prototype.isMuted = function() { + state = state.PlayState || {}; + return state.BufferedRanges || []; + }; + + ChromecastPlayer.prototype.paused = function () { var state = this.lastPlayerData || {}; - return state = state.PlayState || {}, state.IsMuted - }, ChromecastPlayer.prototype.shuffle = function(item) { - var apiClient = connectionManager.getApiClient(item.ServerId), - userId = apiClient.getCurrentUserId(), - instance = this; - apiClient.getItem(userId, item.Id).then(function(item) { - instance.playWithCommand({ - items: [item] - }, "Shuffle") - }) - }, ChromecastPlayer.prototype.instantMix = function(item) { - var apiClient = connectionManager.getApiClient(item.ServerId), - userId = apiClient.getCurrentUserId(), - instance = this; - apiClient.getItem(userId, item.Id).then(function(item) { - instance.playWithCommand({ - items: [item] - }, "InstantMix") - }) - }, ChromecastPlayer.prototype.canPlayMediaType = function(mediaType) { - return "audio" === (mediaType = (mediaType || "").toLowerCase()) || "video" === mediaType - }, ChromecastPlayer.prototype.canQueueMediaType = function(mediaType) { - return this.canPlayMediaType(mediaType) - }, ChromecastPlayer.prototype.queue = function(options) { - this.playWithCommand(options, "PlayLast") - }, ChromecastPlayer.prototype.queueNext = function(options) { - this.playWithCommand(options, "PlayNext") - }, ChromecastPlayer.prototype.play = function(options) { - if (options.items) return this.playWithCommand(options, "PlayNow"); - if (!options.serverId) throw new Error("serverId required!"); + state = state.PlayState || {}; + + return state.IsPaused; + }; + + ChromecastPlayer.prototype.isMuted = function () { + var state = this.lastPlayerData || {}; + state = state.PlayState || {}; + + return state.IsMuted; + }; + + ChromecastPlayer.prototype.shuffle = function (item) { + + var apiClient = connectionManager.getApiClient(item.ServerId); + var userId = apiClient.getCurrentUserId(); + var instance = this; - return getItemsForPlayback(connectionManager.getApiClient(options.serverId), { - Ids: options.ids.join(",") - }).then(function(result) { - return options.items = result.Items, instance.playWithCommand(options, "PlayNow") - }) - }, ChromecastPlayer.prototype.toggleFullscreen = function() {}, ChromecastPlayer.prototype.beginPlayerUpdates = function() {}, ChromecastPlayer.prototype.endPlayerUpdates = function() {}, ChromecastPlayer.prototype.getPlaylist = function() { - return Promise.resolve([]) - }, ChromecastPlayer.prototype.getCurrentPlaylistItemId = function() {}, ChromecastPlayer.prototype.setCurrentPlaylistItem = function(playlistItemId) { - return Promise.resolve() - }, ChromecastPlayer.prototype.removeFromPlaylist = function(playlistItemIds) { - return Promise.resolve() - }, ChromecastPlayer.prototype.getPlayerState = function() { - return this.getPlayerStateInternal() || {} - }, ChromecastPlayer -}); \ No newline at end of file + + apiClient.getItem(userId, item.Id).then(function (item) { + + instance.playWithCommand({ + + items: [item] + + }, 'Shuffle'); + + }); + + }; + + ChromecastPlayer.prototype.instantMix = function (item) { + + var apiClient = connectionManager.getApiClient(item.ServerId); + var userId = apiClient.getCurrentUserId(); + + var instance = this; + + apiClient.getItem(userId, item.Id).then(function (item) { + + instance.playWithCommand({ + + items: [item] + + }, 'InstantMix'); + + }); + + }; + + ChromecastPlayer.prototype.canPlayMediaType = function (mediaType) { + + mediaType = (mediaType || '').toLowerCase(); + return mediaType === 'audio' || mediaType === 'video'; + }; + + ChromecastPlayer.prototype.canQueueMediaType = function (mediaType) { + return this.canPlayMediaType(mediaType); + }; + + ChromecastPlayer.prototype.queue = function (options) { + this.playWithCommand(options, 'PlayLast'); + }; + + ChromecastPlayer.prototype.queueNext = function (options) { + this.playWithCommand(options, 'PlayNext'); + }; + + ChromecastPlayer.prototype.play = function (options) { + + if (options.items) { + + return this.playWithCommand(options, 'PlayNow'); + + } else { + + if (!options.serverId) { + throw new Error('serverId required!'); + } + + var instance = this; + var apiClient = connectionManager.getApiClient(options.serverId); + + return getItemsForPlayback(apiClient, { + + Ids: options.ids.join(',') + + }).then(function (result) { + + options.items = result.Items; + return instance.playWithCommand(options, 'PlayNow'); + + }); + } + }; + + ChromecastPlayer.prototype.toggleFullscreen = function () { + // not supported + }; + + ChromecastPlayer.prototype.beginPlayerUpdates = function () { + // Setup polling here + }; + + ChromecastPlayer.prototype.endPlayerUpdates = function () { + // Stop polling here + }; + + ChromecastPlayer.prototype.getPlaylist = function () { + return Promise.resolve([]); + }; + + ChromecastPlayer.prototype.getCurrentPlaylistItemId = function () { + }; + + ChromecastPlayer.prototype.setCurrentPlaylistItem = function (playlistItemId) { + return Promise.resolve(); + }; + + ChromecastPlayer.prototype.removeFromPlaylist = function (playlistItemIds) { + return Promise.resolve(); + }; + + ChromecastPlayer.prototype.getPlayerState = function () { + + return this.getPlayerStateInternal() || {}; + }; + + return ChromecastPlayer; +}); diff --git a/src/bower_components/emby-webcomponents/clearbutton.css b/src/bower_components/emby-webcomponents/clearbutton.css index e41da78a3e..2d3f8d6800 100644 --- a/src/bower_components/emby-webcomponents/clearbutton.css +++ b/src/bower_components/emby-webcomponents/clearbutton.css @@ -1,12 +1,12 @@ .clearButton { - background: 0 0; + background: transparent; border: 0 !important; padding: 0 !important; cursor: pointer; - outline: 0 !important; + outline: none !important; color: inherit; width: 100%; vertical-align: middle; font-family: inherit; - font-size: inherit + font-size: inherit; } \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/collectioneditor/collectioneditor.js b/src/bower_components/emby-webcomponents/collectioneditor/collectioneditor.js index d1aeec3c8e..8e526fa114 100644 --- a/src/bower_components/emby-webcomponents/collectioneditor/collectioneditor.js +++ b/src/bower_components/emby-webcomponents/collectioneditor/collectioneditor.js @@ -1,119 +1,287 @@ -define(["dialogHelper", "loading", "apphost", "layoutManager", "connectionManager", "appRouter", "globalize", "emby-checkbox", "emby-input", "paper-icon-button-light", "emby-select", "material-icons", "css!./../formdialog", "emby-button", "emby-linkbutton", "flexStyles"], function(dialogHelper, loading, appHost, layoutManager, connectionManager, appRouter, globalize) { - "use strict"; +define(['dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectionManager', 'appRouter', 'globalize', 'emby-checkbox', 'emby-input', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button', 'emby-linkbutton', 'flexStyles'], function (dialogHelper, loading, appHost, layoutManager, connectionManager, appRouter, globalize) { + 'use strict'; + + var currentServerId; function parentWithClass(elem, className) { - for (; !elem.classList || !elem.classList.contains(className);) - if (!(elem = elem.parentNode)) return null; - return elem + + while (!elem.classList || !elem.classList.contains(className)) { + elem = elem.parentNode; + + if (!elem) { + return null; + } + } + + return elem; } function onSubmit(e) { loading.show(); - var panel = parentWithClass(this, "dialog"), - collectionId = panel.querySelector("#selectCollectionToAddTo").value, - apiClient = connectionManager.getApiClient(currentServerId); - return collectionId ? addToCollection(apiClient, panel, collectionId) : createCollection(apiClient, panel), e.preventDefault(), !1 + + var panel = parentWithClass(this, 'dialog'); + + var collectionId = panel.querySelector('#selectCollectionToAddTo').value; + + var apiClient = connectionManager.getApiClient(currentServerId); + + if (collectionId) { + addToCollection(apiClient, panel, collectionId); + } else { + createCollection(apiClient, panel); + } + + e.preventDefault(); + return false; } function createCollection(apiClient, dlg) { + var url = apiClient.getUrl("Collections", { - Name: dlg.querySelector("#txtNewCollectionName").value, - IsLocked: !dlg.querySelector("#chkEnableInternetMetadata").checked, - Ids: dlg.querySelector(".fldSelectedItemIds").value || "" + + Name: dlg.querySelector('#txtNewCollectionName').value, + IsLocked: !dlg.querySelector('#chkEnableInternetMetadata').checked, + Ids: dlg.querySelector('.fldSelectedItemIds').value || '' }); + apiClient.ajax({ type: "POST", url: url, dataType: "json" - }).then(function(result) { + + }).then(function (result) { + loading.hide(); + var id = result.Id; - dlg.submitted = !0, dialogHelper.close(dlg), redirectToCollection(apiClient, id) - }) + + dlg.submitted = true; + dialogHelper.close(dlg); + redirectToCollection(apiClient, id); + + }); } function redirectToCollection(apiClient, id) { - appRouter.showItem(id, apiClient.serverId()) + + appRouter.showItem(id, apiClient.serverId()); } function addToCollection(apiClient, dlg, id) { + var url = apiClient.getUrl("Collections/" + id + "/Items", { - Ids: dlg.querySelector(".fldSelectedItemIds").value || "" + + Ids: dlg.querySelector('.fldSelectedItemIds').value || '' }); + apiClient.ajax({ type: "POST", url: url - }).then(function() { - loading.hide(), dlg.submitted = !0, dialogHelper.close(dlg), require(["toast"], function(toast) { - toast(globalize.translate("sharedcomponents#MessageItemsAdded")) - }) - }) + + }).then(function () { + + loading.hide(); + + dlg.submitted = true; + dialogHelper.close(dlg); + + require(['toast'], function (toast) { + toast(globalize.translate('sharedcomponents#MessageItemsAdded')); + }); + }); } function triggerChange(select) { - select.dispatchEvent(new CustomEvent("change", {})) + select.dispatchEvent(new CustomEvent('change', {})); } function populateCollections(panel) { + loading.show(); - var select = panel.querySelector("#selectCollectionToAddTo"); - panel.querySelector(".newCollectionInfo").classList.add("hide"); + + var select = panel.querySelector('#selectCollectionToAddTo'); + + panel.querySelector('.newCollectionInfo').classList.add('hide'); + var options = { - Recursive: !0, - IncludeItemTypes: "BoxSet", - SortBy: "SortName", - EnableTotalRecordCount: !1 - }, - apiClient = connectionManager.getApiClient(currentServerId); - apiClient.getItems(apiClient.getCurrentUserId(), options).then(function(result) { - var html = ""; - html += '", html += result.Items.map(function(i) { - return '" - }), select.innerHTML = html, select.value = "", triggerChange(select), loading.hide() - }) + + Recursive: true, + IncludeItemTypes: "BoxSet", + SortBy: "SortName", + EnableTotalRecordCount: false + }; + + var apiClient = connectionManager.getApiClient(currentServerId); + apiClient.getItems(apiClient.getCurrentUserId(), options).then(function (result) { + + var html = ''; + + html += ''; + + html += result.Items.map(function (i) { + + return ''; + }); + + select.innerHTML = html; + select.value = ''; + triggerChange(select); + + loading.hide(); + }); } function getEditorHtml() { - var html = ""; - return html += '
', html += '
', html += '
', html += "
", html += globalize.translate("sharedcomponents#NewCollectionHelp"), html += "
", html += '
', html += "
", html += "
", html += '
', html += '', html += "
", html += "
", html += '
', html += '
', html += '', html += '
' + globalize.translate("sharedcomponents#NewCollectionNameExample") + "
", html += "
", html += '", html += "
", html += '
', html += '", html += "
", html += '', html += "
", html += "
", html += "
" + + var html = ''; + + html += '
'; + html += '
'; + html += '
'; + + html += '
'; + html += globalize.translate('sharedcomponents#NewCollectionHelp'); + html += '
'; + + html += '
'; + html += '
'; + html += '
'; + html += '
'; + html += ''; + html += '
'; + html += '
'; + + html += '
'; + + html += '
'; + html += ''; + html += '
' + globalize.translate('sharedcomponents#NewCollectionNameExample') + '
'; + html += '
'; + + html += ''; + + // newCollectionInfo + html += '
'; + + html += '
'; + html += ''; + html += '
'; + + html += ''; + + html += '
'; + html += '
'; + html += '
'; + + return html; } function initEditor(content, items) { - if (content.querySelector("#selectCollectionToAddTo").addEventListener("change", function() { - this.value ? (content.querySelector(".newCollectionInfo").classList.add("hide"), content.querySelector("#txtNewCollectionName").removeAttribute("required")) : (content.querySelector(".newCollectionInfo").classList.remove("hide"), content.querySelector("#txtNewCollectionName").setAttribute("required", "required")) - }), content.querySelector("form").addEventListener("submit", onSubmit), content.querySelector(".fldSelectedItemIds", content).value = items.join(","), items.length) content.querySelector(".fldSelectCollection").classList.remove("hide"), populateCollections(content); - else { - content.querySelector(".fldSelectCollection").classList.add("hide"); - var selectCollectionToAddTo = content.querySelector("#selectCollectionToAddTo"); - selectCollectionToAddTo.innerHTML = "", selectCollectionToAddTo.value = "", triggerChange(selectCollectionToAddTo) + + content.querySelector('#selectCollectionToAddTo').addEventListener('change', function () { + if (this.value) { + content.querySelector('.newCollectionInfo').classList.add('hide'); + content.querySelector('#txtNewCollectionName').removeAttribute('required'); + } else { + content.querySelector('.newCollectionInfo').classList.remove('hide'); + content.querySelector('#txtNewCollectionName').setAttribute('required', 'required'); + } + }); + + content.querySelector('form').addEventListener('submit', onSubmit); + + content.querySelector('.fldSelectedItemIds', content).value = items.join(','); + + if (items.length) { + content.querySelector('.fldSelectCollection').classList.remove('hide'); + populateCollections(content); + } else { + content.querySelector('.fldSelectCollection').classList.add('hide'); + + var selectCollectionToAddTo = content.querySelector('#selectCollectionToAddTo'); + selectCollectionToAddTo.innerHTML = ''; + selectCollectionToAddTo.value = ''; + triggerChange(selectCollectionToAddTo); } } function centerFocus(elem, horiz, on) { - require(["scrollHelper"], function(scrollHelper) { - var fn = on ? "on" : "off"; - scrollHelper.centerFocus[fn](elem, horiz) - }) + require(['scrollHelper'], function (scrollHelper) { + var fn = on ? 'on' : 'off'; + scrollHelper.centerFocus[fn](elem, horiz); + }); } - function CollectionEditor() {} - var currentServerId; - return CollectionEditor.prototype.show = function(options) { + function CollectionEditor() { + + } + + CollectionEditor.prototype.show = function (options) { + var items = options.items || {}; currentServerId = options.serverId; + var dialogOptions = { - removeOnClose: !0, - scrollY: !1 + removeOnClose: true, + scrollY: false }; - layoutManager.tv ? dialogOptions.size = "fullscreen" : dialogOptions.size = "small"; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'small'; + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"); - var html = "", - title = items.length ? globalize.translate("sharedcomponents#HeaderAddToCollection") : globalize.translate("sharedcomponents#NewCollection"); - return html += '
', html += '', html += '

', html += title, html += "

", appHost.supports("externallinks") && (html += '' + globalize.translate("sharedcomponents#Help") + ""), html += "
", html += getEditorHtml(), dlg.innerHTML = html, initEditor(dlg, items), dlg.querySelector(".btnCancel").addEventListener("click", function() { - dialogHelper.close(dlg) - }), layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !0), dialogHelper.open(dlg).then(function() { - return layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !1), dlg.submitted ? Promise.resolve() : Promise.reject() - }) - }, CollectionEditor + + dlg.classList.add('formDialog'); + + var html = ''; + var title = items.length ? globalize.translate('sharedcomponents#HeaderAddToCollection') : globalize.translate('sharedcomponents#NewCollection'); + + html += '
'; + html += ''; + html += '

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

'; + + if (appHost.supports('externallinks')) { + html += '' + globalize.translate('sharedcomponents#Help') + ''; + } + + html += '
'; + + html += getEditorHtml(); + + dlg.innerHTML = html; + + initEditor(dlg, items); + + dlg.querySelector('.btnCancel').addEventListener('click', function () { + + dialogHelper.close(dlg); + }); + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, true); + } + + return dialogHelper.open(dlg).then(function () { + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, false); + } + + if (dlg.submitted) { + return Promise.resolve(); + } + + return Promise.reject(); + }); + }; + + return CollectionEditor; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/confirm/confirm.js b/src/bower_components/emby-webcomponents/confirm/confirm.js index 22890d52cb..877a0716e9 100644 --- a/src/bower_components/emby-webcomponents/confirm/confirm.js +++ b/src/bower_components/emby-webcomponents/confirm/confirm.js @@ -1,22 +1,40 @@ -define(["dialog", "globalize"], function(dialog, globalize) { - "use strict"; - return function(text, title) { +define(['dialog', 'globalize'], function (dialog, globalize) { + 'use strict'; + + return function (text, title) { + var options; - options = "string" == typeof text ? { - title: title, - text: text - } : text; + if (typeof text === 'string') { + options = { + title: title, + text: text + }; + } else { + options = text; + } + var items = []; - return items.push({ - name: options.cancelText || globalize.translate("sharedcomponents#ButtonCancel"), - id: "cancel", - type: "cancel" === options.primary ? "submit" : "cancel" - }), items.push({ - name: options.confirmText || globalize.translate("sharedcomponents#ButtonOk"), - id: "ok", - type: "cancel" === options.primary ? "cancel" : "submit" - }), options.buttons = items, dialog(options).then(function(result) { - return "ok" === result ? Promise.resolve() : Promise.reject() - }) - } + + items.push({ + name: options.cancelText || globalize.translate('sharedcomponents#ButtonCancel'), + id: 'cancel', + type: options.primary === 'cancel' ? 'submit' : 'cancel' + }); + + items.push({ + name: options.confirmText || globalize.translate('sharedcomponents#ButtonOk'), + id: 'ok', + type: options.primary === 'cancel' ? 'cancel' : 'submit' + }); + + options.buttons = items; + + return dialog(options).then(function (result) { + if (result === 'ok') { + return Promise.resolve(); + } + + return Promise.reject(); + }); + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/confirm/nativeconfirm.js b/src/bower_components/emby-webcomponents/confirm/nativeconfirm.js index d909e687e8..fd586ad1d5 100644 --- a/src/bower_components/emby-webcomponents/confirm/nativeconfirm.js +++ b/src/bower_components/emby-webcomponents/confirm/nativeconfirm.js @@ -1,15 +1,27 @@ -define([], function() { - "use strict"; +define([], function () { + 'use strict'; function replaceAll(str, find, replace) { - return str.split(find).join(replace) - } - return function(options) { - "string" == typeof options && (options = { - title: "", - text: options - }); - var text = replaceAll(options.text || "", "
", "\n"); - return confirm(text) ? Promise.resolve() : Promise.reject() + + return str.split(find).join(replace); } + + return function (options) { + + if (typeof options === 'string') { + options = { + title: '', + text: options + }; + } + + var text = replaceAll(options.text || '', '
', '\n'); + var result = confirm(text); + + if (result) { + return Promise.resolve(); + } else { + return Promise.reject(); + } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/datetime.js b/src/bower_components/emby-webcomponents/datetime.js index 9d3edd6786..15d0c64865 100644 --- a/src/bower_components/emby-webcomponents/datetime.js +++ b/src/bower_components/emby-webcomponents/datetime.js @@ -1,113 +1,272 @@ -define(["globalize"], function(globalize) { - "use strict"; +define(['globalize'], function (globalize) { + 'use strict'; function parseISO8601Date(s, toLocal) { - var re = /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(Z|([+-])(\d{2}):(\d{2}))?/, - d = s.match(re); - if (!d) throw "Couldn't parse ISO 8601 date string '" + s + "'"; + + // parenthese matches: + // year month day hours minutes seconds + // dotmilliseconds + // tzstring plusminus hours minutes + var re = /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(Z|([+-])(\d{2}):(\d{2}))?/; + + var d = s.match(re); + + // "2010-12-07T11:00:00.000-09:00" parses to: + // ["2010-12-07T11:00:00.000-09:00", "2010", "12", "07", "11", + // "00", "00", ".000", "-09:00", "-", "09", "00"] + // "2010-12-07T11:00:00.000Z" parses to: + // ["2010-12-07T11:00:00.000Z", "2010", "12", "07", "11", + // "00", "00", ".000", "Z", undefined, undefined, undefined] + + if (!d) { + + throw "Couldn't parse ISO 8601 date string '" + s + "'"; + } + + // parse strings, leading zeros into proper ints var a = [1, 2, 3, 4, 5, 6, 10, 11]; - for (var i in a) d[a[i]] = parseInt(d[a[i]], 10); + for (var i in a) { + d[a[i]] = parseInt(d[a[i]], 10); + } d[7] = parseFloat(d[7]); + + // Date.UTC(year, month[, date[, hrs[, min[, sec[, ms]]]]]) + // note that month is 0-11, not 1-12 + // see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/UTC var ms = Date.UTC(d[1], d[2] - 1, d[3], d[4], d[5], d[6]); - if (d[7] > 0 && (ms += Math.round(1e3 * d[7])), "Z" !== d[8] && d[10]) { - var offset = 60 * d[10] * 60 * 1e3; - d[11] && (offset += 60 * d[11] * 1e3), "-" === d[9] ? ms -= offset : ms += offset - } else !1 === toLocal && (ms += 6e4 * (new Date).getTimezoneOffset()); - return new Date(ms) + + // if there are milliseconds, add them + if (d[7] > 0) { + ms += Math.round(d[7] * 1000); + } + + // if there's a timezone, calculate it + if (d[8] !== "Z" && d[10]) { + var offset = d[10] * 60 * 60 * 1000; + if (d[11]) { + offset += d[11] * 60 * 1000; + } + if (d[9] === "-") { + ms -= offset; + } else { + ms += offset; + } + } else if (toLocal === false) { + ms += new Date().getTimezoneOffset() * 60000; + } + + return new Date(ms); } function getDisplayRunningTime(ticks) { - var parts = [], - hours = ticks / 36e9; - hours = Math.floor(hours), hours && parts.push(hours), ticks -= 36e9 * hours; - var minutes = ticks / 6e8; - minutes = Math.floor(minutes), ticks -= 6e8 * minutes, minutes < 10 && hours && (minutes = "0" + minutes), parts.push(minutes); - var seconds = ticks / 1e7; - return seconds = Math.floor(seconds), seconds < 10 && (seconds = "0" + seconds), parts.push(seconds), parts.join(":") + var ticksPerHour = 36000000000; + var ticksPerMinute = 600000000; + var ticksPerSecond = 10000000; + + var parts = []; + + var hours = ticks / ticksPerHour; + hours = Math.floor(hours); + + if (hours) { + parts.push(hours); + } + + ticks -= (hours * ticksPerHour); + + var minutes = ticks / ticksPerMinute; + minutes = Math.floor(minutes); + + ticks -= (minutes * ticksPerMinute); + + if (minutes < 10 && hours) { + minutes = '0' + minutes; + } + parts.push(minutes); + + var seconds = ticks / ticksPerSecond; + seconds = Math.floor(seconds); + + if (seconds < 10) { + seconds = '0' + seconds; + } + parts.push(seconds); + + return parts.join(':'); } + var toLocaleTimeStringSupportsLocales = function () { + try { + new Date().toLocaleTimeString('i'); + } catch (e) { + return e.name === 'RangeError'; + } + return false; + }(); + function getOptionList(options) { + var list = []; - for (var i in options) list.push({ - name: i, - value: options[i] - }); - return list + + for (var i in options) { + list.push({ + name: i, + value: options[i] + }); + } + + return list; } function toLocaleString(date, options) { - if (!date) throw new Error("date cannot be null"); - if (options = options || {}, toLocaleTimeStringSupportsLocales) { - var currentLocale = globalize.getCurrentDateTimeLocale(); - if (currentLocale) return date.toLocaleString(currentLocale, options) + + if (!date) { + throw new Error('date cannot be null'); } - return date.toLocaleString() + + options = options || {}; + + if (toLocaleTimeStringSupportsLocales) { + + var currentLocale = globalize.getCurrentDateTimeLocale(); + + if (currentLocale) { + return date.toLocaleString(currentLocale, options); + } + } + + return date.toLocaleString(); } function toLocaleDateString(date, options) { - if (!date) throw new Error("date cannot be null"); - if (options = options || {}, toLocaleTimeStringSupportsLocales) { + + if (!date) { + throw new Error('date cannot be null'); + } + + options = options || {}; + + if (toLocaleTimeStringSupportsLocales) { + var currentLocale = globalize.getCurrentDateTimeLocale(); - if (currentLocale) return date.toLocaleDateString(currentLocale, options) + + if (currentLocale) { + return date.toLocaleDateString(currentLocale, options); + } } + + // This is essentially a hard-coded polyfill var optionList = getOptionList(options); - if (1 === optionList.length && "weekday" === optionList[0].name) { + if (optionList.length === 1 && optionList[0].name === 'weekday') { var weekday = []; - return weekday[0] = "Sun", weekday[1] = "Mon", weekday[2] = "Tue", weekday[3] = "Wed", weekday[4] = "Thu", weekday[5] = "Fri", weekday[6] = "Sat", weekday[date.getDay()] + weekday[0] = "Sun"; + weekday[1] = "Mon"; + weekday[2] = "Tue"; + weekday[3] = "Wed"; + weekday[4] = "Thu"; + weekday[5] = "Fri"; + weekday[6] = "Sat"; + return weekday[date.getDay()]; } - return date.toLocaleDateString() + + return date.toLocaleDateString(); } function toLocaleTimeString(date, options) { - if (!date) throw new Error("date cannot be null"); - if (options = options || {}, toLocaleTimeStringSupportsLocales) { - var currentLocale = globalize.getCurrentDateTimeLocale(); - if (currentLocale) return date.toLocaleTimeString(currentLocale, options) + + if (!date) { + throw new Error('date cannot be null'); } - return date.toLocaleTimeString() + + options = options || {}; + + if (toLocaleTimeStringSupportsLocales) { + + var currentLocale = globalize.getCurrentDateTimeLocale(); + + if (currentLocale) { + return date.toLocaleTimeString(currentLocale, options); + } + } + + return date.toLocaleTimeString(); } function getDisplayTime(date) { - if (!date) throw new Error("date cannot be null"); - if ("string" === (typeof date).toString().toLowerCase()) try { - date = parseISO8601Date(date, !0) - } catch (err) { - return date + + if (!date) { + throw new Error('date cannot be null'); } - if (toLocaleTimeStringSupportsLocales) return toLocaleTimeString(date, { - hour: "numeric", - minute: "2-digit" - }); - var time = toLocaleTimeString(date), - timeLower = time.toLowerCase(); - if (-1 !== timeLower.indexOf("am") || -1 !== timeLower.indexOf("pm")) { + + if ((typeof date).toString().toLowerCase() === 'string') { + try { + + date = parseISO8601Date(date, true); + + } catch (err) { + return date; + } + } + + if (toLocaleTimeStringSupportsLocales) { + return toLocaleTimeString(date, { + + hour: 'numeric', + minute: '2-digit' + + }); + } + + var time = toLocaleTimeString(date); + + var timeLower = time.toLowerCase(); + + if (timeLower.indexOf('am') !== -1 || timeLower.indexOf('pm') !== -1) { + time = timeLower; - var hour = date.getHours() % 12, - suffix = date.getHours() > 11 ? "pm" : "am"; - hour || (hour = 12); + var hour = date.getHours() % 12; + var suffix = date.getHours() > 11 ? 'pm' : 'am'; + if (!hour) { + hour = 12; + } var minutes = date.getMinutes(); - minutes < 10 && (minutes = "0" + minutes), minutes = ":" + minutes, time = hour + minutes + suffix + + if (minutes < 10) { + minutes = '0' + minutes; + } + + minutes = ':' + minutes; + time = hour + minutes + suffix; } else { - var timeParts = time.split(":"); - timeParts.length > 2 && (timeParts.length = 2, time = timeParts.join(":")) + + var timeParts = time.split(':'); + + // Trim off seconds + if (timeParts.length > 2) { + + // setting to 2 also handles '21:00:28 GMT+9:30' + timeParts.length = 2; + time = timeParts.join(':'); + } } - return time + + return time; } function isRelativeDay(date, offsetInDays) { - if (!date) throw new Error("date cannot be null"); - var yesterday = new Date, - day = yesterday.getDate() + offsetInDays; - return yesterday.setDate(day), date.getFullYear() === yesterday.getFullYear() && date.getMonth() === yesterday.getMonth() && date.getDate() === day - } - var toLocaleTimeStringSupportsLocales = function() { - try { - (new Date).toLocaleTimeString("i") - } catch (e) { - return "RangeError" === e.name + + if (!date) { + throw new Error('date cannot be null'); } - return !1 - }(); + + var yesterday = new Date(); + var day = yesterday.getDate() + offsetInDays; + + yesterday.setDate(day); // automatically adjusts month/year appropriately + + return date.getFullYear() === yesterday.getFullYear() && date.getMonth() === yesterday.getMonth() && date.getDate() === day; + } + return { parseISO8601Date: parseISO8601Date, getDisplayRunningTime: getDisplayRunningTime, @@ -116,8 +275,8 @@ define(["globalize"], function(globalize) { getDisplayTime: getDisplayTime, isRelativeDay: isRelativeDay, toLocaleTimeString: toLocaleTimeString, - supportsLocalization: function() { - return toLocaleTimeStringSupportsLocales + supportsLocalization: function () { + return toLocaleTimeStringSupportsLocales; } - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/deletehelper.js b/src/bower_components/emby-webcomponents/deletehelper.js index e31b9104e3..e8f3ad3d18 100644 --- a/src/bower_components/emby-webcomponents/deletehelper.js +++ b/src/bower_components/emby-webcomponents/deletehelper.js @@ -1,39 +1,57 @@ -define(["connectionManager", "confirm", "appRouter", "globalize"], function(connectionManager, confirm, appRouter, globalize) { - "use strict"; +define(['connectionManager', 'confirm', 'appRouter', 'globalize'], function (connectionManager, confirm, appRouter, globalize) { + 'use strict'; function alertText(options) { - return new Promise(function(resolve, reject) { - require(["alert"], function(alert) { - alert(options).then(resolve, resolve) - }) - }) + + return new Promise(function (resolve, reject) { + + require(['alert'], function (alert) { + alert(options).then(resolve, resolve); + }); + }); } function deleteItem(options) { - var item = options.item, - itemId = item.Id, - parentId = item.SeasonId || item.SeriesId || item.ParentId, - serverId = item.ServerId, - msg = globalize.translate("sharedcomponents#ConfirmDeleteItem"), - title = globalize.translate("sharedcomponents#HeaderDeleteItem"), - apiClient = connectionManager.getApiClient(item.ServerId); + + var item = options.item; + var itemId = item.Id; + var parentId = item.SeasonId || item.SeriesId || item.ParentId; + var serverId = item.ServerId; + + var msg = globalize.translate('sharedcomponents#ConfirmDeleteItem'); + var title = globalize.translate('sharedcomponents#HeaderDeleteItem'); + var apiClient = connectionManager.getApiClient(item.ServerId); + return confirm({ + title: title, text: msg, - confirmText: globalize.translate("sharedcomponents#Delete"), - primary: "cancel" - }).then(function() { - return apiClient.deleteItem(itemId).then(function() { - options.navigate && (parentId ? appRouter.showItem(parentId, serverId) : appRouter.goHome()) - }, function(err) { - var result = function() { - return Promise.reject(err) + confirmText: globalize.translate('sharedcomponents#Delete'), + primary: 'cancel' + + }).then(function () { + + return apiClient.deleteItem(itemId).then(function () { + + if (options.navigate) { + if (parentId) { + appRouter.showItem(parentId, serverId); + } else { + appRouter.goHome(); + } + } + }, function (err) { + + var result = function () { + return Promise.reject(err); }; - return alertText(globalize.translate("sharedcomponents#ErrorDeletingItem")).then(result, result) - }) - }) + + return alertText(globalize.translate('sharedcomponents#ErrorDeletingItem')).then(result, result); + }); + }); } + return { deleteItem: deleteItem - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/dialog/dialog.js b/src/bower_components/emby-webcomponents/dialog/dialog.js index 1208719886..62faebd31e 100644 --- a/src/bower_components/emby-webcomponents/dialog/dialog.js +++ b/src/bower_components/emby-webcomponents/dialog/dialog.js @@ -1,46 +1,133 @@ -define(["dialogHelper", "dom", "layoutManager", "scrollHelper", "globalize", "require", "material-icons", "emby-button", "paper-icon-button-light", "emby-input", "formDialogStyle", "flexStyles"], function(dialogHelper, dom, layoutManager, scrollHelper, globalize, require) { - "use strict"; +define(['dialogHelper', 'dom', 'layoutManager', 'scrollHelper', 'globalize', 'require', 'material-icons', 'emby-button', 'paper-icon-button-light', 'emby-input', 'formDialogStyle', 'flexStyles'], function (dialogHelper, dom, layoutManager, scrollHelper, globalize, require) { + 'use strict'; function showDialog(options, template) { - function onButtonClick() { - dialogResult = this.getAttribute("data-id"), dialogHelper.close(dlg) - } + var dialogOptions = { - removeOnClose: !0, - scrollY: !1 - }, - enableTvLayout = layoutManager.tv; - enableTvLayout && (dialogOptions.size = "fullscreen"); - var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"), dlg.innerHTML = globalize.translateHtml(template, "sharedcomponents"), dlg.classList.add("align-items-center"), dlg.classList.add("justify-content-center"); - var formDialogContent = dlg.querySelector(".formDialogContent"); - formDialogContent.classList.add("no-grow"), enableTvLayout ? (formDialogContent.style["max-width"] = "50%", formDialogContent.style["max-height"] = "60%", scrollHelper.centerFocus.on(formDialogContent, !1)) : (formDialogContent.style.maxWidth = Math.min(150 * options.buttons.length + 200, dom.getWindowSize().innerWidth - 50) + "px", dlg.classList.add("dialog-fullscreen-lowres")), options.title ? dlg.querySelector(".formDialogHeaderTitle").innerHTML = options.title || "" : dlg.querySelector(".formDialogHeaderTitle").classList.add("hide"); - var displayText = options.html || options.text || ""; - dlg.querySelector(".text").innerHTML = displayText, displayText || dlg.querySelector(".dialogContentInner").classList.add("hide"); - var i, length, html = "", - hasDescriptions = !1; - for (i = 0, length = options.buttons.length; i < length; i++) { - var item = options.buttons[i], - autoFocus = 0 === i ? " autofocus" : "", - buttonClass = "btnOption raised formDialogFooterItem formDialogFooterItem-autosize"; - item.type && (buttonClass += " button-" + item.type), item.description && (hasDescriptions = !0), hasDescriptions && (buttonClass += " formDialogFooterItem-vertical formDialogFooterItem-nomarginbottom"), html += '", item.description && (html += '
' + item.description + "
") + removeOnClose: true, + scrollY: false + }; + + var enableTvLayout = layoutManager.tv; + + if (enableTvLayout) { + dialogOptions.size = 'fullscreen'; } - dlg.querySelector(".formDialogFooter").innerHTML = html, hasDescriptions && dlg.querySelector(".formDialogFooter").classList.add("formDialogFooter-vertical"); - var dialogResult, buttons = dlg.querySelectorAll(".btnOption"); - for (i = 0, length = buttons.length; i < length; i++) buttons[i].addEventListener("click", onButtonClick); - return dialogHelper.open(dlg).then(function() { - return enableTvLayout && scrollHelper.centerFocus.off(dlg.querySelector(".formDialogContent"), !1), dialogResult || Promise.reject() - }) + + var dlg = dialogHelper.createDialog(dialogOptions); + + dlg.classList.add('formDialog'); + + dlg.innerHTML = globalize.translateHtml(template, 'sharedcomponents'); + + dlg.classList.add('align-items-center'); + dlg.classList.add('justify-content-center'); + var formDialogContent = dlg.querySelector('.formDialogContent'); + formDialogContent.classList.add('no-grow'); + + if (enableTvLayout) { + formDialogContent.style['max-width'] = '50%'; + formDialogContent.style['max-height'] = '60%'; + scrollHelper.centerFocus.on(formDialogContent, false); + } else { + formDialogContent.style.maxWidth = (Math.min((options.buttons.length * 150) + 200, dom.getWindowSize().innerWidth - 50)) + 'px'; + dlg.classList.add('dialog-fullscreen-lowres'); + } + + //dlg.querySelector('.btnCancel').addEventListener('click', function (e) { + // dialogHelper.close(dlg); + //}); + + if (options.title) { + dlg.querySelector('.formDialogHeaderTitle').innerHTML = options.title || ''; + } else { + dlg.querySelector('.formDialogHeaderTitle').classList.add('hide'); + } + + var displayText = options.html || options.text || ''; + dlg.querySelector('.text').innerHTML = displayText; + + if (!displayText) { + dlg.querySelector('.dialogContentInner').classList.add('hide'); + } + + var i, length; + var html = ''; + var hasDescriptions = false; + + for (i = 0, length = options.buttons.length; i < length; i++) { + + var item = options.buttons[i]; + var autoFocus = i === 0 ? ' autofocus' : ''; + + var buttonClass = 'btnOption raised formDialogFooterItem formDialogFooterItem-autosize'; + + if (item.type) { + buttonClass += ' button-' + item.type; + } + + if (item.description) { + hasDescriptions = true; + } + + if (hasDescriptions) { + buttonClass += ' formDialogFooterItem-vertical formDialogFooterItem-nomarginbottom'; + } + + html += ''; + + if (item.description) { + html += '
' + item.description + '
'; + } + } + + dlg.querySelector('.formDialogFooter').innerHTML = html; + + if (hasDescriptions) { + dlg.querySelector('.formDialogFooter').classList.add('formDialogFooter-vertical'); + } + + var dialogResult; + function onButtonClick() { + dialogResult = this.getAttribute('data-id'); + dialogHelper.close(dlg); + } + + var buttons = dlg.querySelectorAll('.btnOption'); + for (i = 0, length = buttons.length; i < length; i++) { + buttons[i].addEventListener('click', onButtonClick); + } + + return dialogHelper.open(dlg).then(function () { + + if (enableTvLayout) { + scrollHelper.centerFocus.off(dlg.querySelector('.formDialogContent'), false); + } + + if (dialogResult) { + return dialogResult; + } else { + return Promise.reject(); + } + }); } - return function(text, title) { + + return function (text, title) { + var options; - return options = "string" == typeof text ? { - title: title, - text: text - } : text, new Promise(function(resolve, reject) { - require(["text!./dialog.template.html"], function(template) { - showDialog(options, template).then(resolve, reject) - }) - }) - } + if (typeof text === 'string') { + options = { + title: title, + text: text + }; + } else { + options = text; + } + + return new Promise(function (resolve, reject) { + require(['text!./dialog.template.html'], function (template) { + showDialog(options, template).then(resolve, reject); + }); + }); + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/dialoghelper/dialoghelper.css b/src/bower_components/emby-webcomponents/dialoghelper/dialoghelper.css index c396dec604..2cc20b5ff2 100644 --- a/src/bower_components/emby-webcomponents/dialoghelper/dialoghelper.css +++ b/src/bower_components/emby-webcomponents/dialoghelper/dialoghelper.css @@ -1,12 +1,6 @@ .dialogContainer { - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; position: fixed; top: 0; @@ -16,234 +10,148 @@ z-index: 999999 !important; contain: strict; overflow: hidden; - overscroll-behavior: contain + overscroll-behavior: contain; } .dialog { margin: 0; - -webkit-border-radius: .2em; border-radius: .2em; -webkit-font-smoothing: antialiased; border: 0; padding: 0; will-change: transform, opacity; + /* Strict does not work well with actionsheet */ contain: style paint; - -webkit-box-shadow: 0 16px 24px 2px rgba(0, 0, 0, .14), 0 6px 30px 5px rgba(0, 0, 0, .12), 0 8px 10px -5px rgba(0, 0, 0, .4); - box-shadow: 0 16px 24px 2px rgba(0, 0, 0, .14), 0 6px 30px 5px rgba(0, 0, 0, .12), 0 8px 10px -5px rgba(0, 0, 0, .4) + box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14), 0 6px 30px 5px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 0, 0, 0.4); } .dialog-fixedSize { - -webkit-border-radius: 0; border-radius: 0; max-height: none; max-width: none; - contain: layout style paint + contain: layout style paint; } .dialog-fullscreen { + /* Needed due to formDialog style */ position: fixed !important; top: 0; bottom: 0; left: 0; right: 0; margin: 0; - -webkit-box-shadow: none; - box-shadow: none -} - -@-webkit-keyframes scaledown { - from { - opacity: 1; - -webkit-transform: none; - transform: none - } - - to { - opacity: 0; - -webkit-transform: scale(.5); - transform: scale(.5) - } + box-shadow: none; } @keyframes scaledown { from { opacity: 1; - -webkit-transform: none; - transform: none + transform: none; } to { opacity: 0; - -webkit-transform: scale(.5); - transform: scale(.5) - } -} - -@-webkit-keyframes scaleup { - from { - -webkit-transform: scale(.5); transform: scale(.5); - opacity: 0 - } - - to { - -webkit-transform: none; - transform: none; - opacity: 1 } } @keyframes scaleup { from { - -webkit-transform: scale(.5); transform: scale(.5); - opacity: 0 + opacity: 0; } to { - -webkit-transform: none; transform: none; - opacity: 1 - } -} - -@-webkit-keyframes fadein { - from { - opacity: 0 - } - - to { - opacity: 1 + opacity: 1; } } @keyframes fadein { - from { - opacity: 0 - } - - to { - opacity: 1 - } -} - -@-webkit-keyframes fadeout { - from { - opacity: 1 - } - - to { - opacity: 0 - } -} - -@keyframes fadeout { - from { - opacity: 1 - } - - to { - opacity: 0 - } -} - -@-webkit-keyframes slideup { from { opacity: 0; - -webkit-transform: translate3d(0, 30%, 0); - transform: translate3d(0, 30%, 0) } to { opacity: 1; - -webkit-transform: none; - transform: none + } +} + +@keyframes fadeout { + + from { + opacity: 1; + } + + to { + opacity: 0; } } @keyframes slideup { from { opacity: 0; - -webkit-transform: translate3d(0, 30%, 0); - transform: translate3d(0, 30%, 0) + transform: translate3d(0, 30%, 0); } to { opacity: 1; - -webkit-transform: none; - transform: none - } -} - -@-webkit-keyframes slidedown { - from { - opacity: 1; - -webkit-transform: none; - transform: none - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, 20%, 0); - transform: translate3d(0, 20%, 0) + transform: none; } } @keyframes slidedown { + from { opacity: 1; - -webkit-transform: none; - transform: none + transform: none; } to { opacity: 0; - -webkit-transform: translate3d(0, 20%, 0); - transform: translate3d(0, 20%, 0) + transform: translate3d(0, 20%, 0); } } -@media all and (max-width:80em), -all and (max-height:45em) { +@media all and (max-width: 80em), all and (max-height: 45em) { - .dialog-fixedSize, - .dialog-fullscreen-lowres { + .dialog-fixedSize, .dialog-fullscreen-lowres { position: fixed !important; top: 0 !important; bottom: 0 !important; left: 0 !important; right: 0 !important; margin: 0 !important; - -webkit-box-shadow: none; - box-shadow: none + box-shadow: none; } } -@media all and (min-width:80em) and (min-height:45em) { +@media all and (min-width: 80em) and (min-height: 45em) { + .dialog-medium { width: 80%; - height: 80% + height: 80%; } .dialog-medium-tall { width: 80%; - height: 90% + height: 90%; } .dialog-small { width: 60%; - height: 80% + height: 80%; } .dialog-fullscreen-border { width: 90%; - height: 90% + height: 90%; } } .noScroll { overflow-x: hidden !important; - overflow-y: hidden !important + overflow-y: hidden !important; } .dialogBackdrop { @@ -256,12 +164,10 @@ all and (max-height:45em) { right: 0 !important; margin: 0 !important; z-index: 999999 !important; - -webkit-transition: opacity ease-out .2s; - -o-transition: opacity ease-out .2s; - transition: opacity ease-out .2s; - will-change: opacity + transition: opacity ease-out 0.2s; + will-change: opacity; } .dialogBackdropOpened { - opacity: .5 -} \ No newline at end of file + opacity: .5; +} diff --git a/src/bower_components/emby-webcomponents/dialoghelper/dialoghelper.js b/src/bower_components/emby-webcomponents/dialoghelper/dialoghelper.js index 9c5f6b0064..48aa006215 100644 --- a/src/bower_components/emby-webcomponents/dialoghelper/dialoghelper.js +++ b/src/bower_components/emby-webcomponents/dialoghelper/dialoghelper.js @@ -1,225 +1,486 @@ -define(["appRouter", "focusManager", "browser", "layoutManager", "inputManager", "dom", "css!./dialoghelper.css", "scrollStyles"], function(appRouter, focusManager, browser, layoutManager, inputManager, dom) { - "use strict"; +define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager', 'dom', 'css!./dialoghelper.css', 'scrollStyles'], function (appRouter, focusManager, browser, layoutManager, inputManager, dom) { + 'use strict'; + + var globalOnOpenCallback; function enableAnimation() { - return !browser.tv && browser.supportsCssAnimation() + + // too slow + if (browser.tv) { + return false; + } + + return browser.supportsCssAnimation(); } function removeCenterFocus(dlg) { - layoutManager.tv && (dlg.classList.contains("scrollX") ? centerFocus(dlg, !0, !1) : dlg.classList.contains("smoothScrollY") && centerFocus(dlg, !1, !1)) + + if (layoutManager.tv) { + if (dlg.classList.contains('scrollX')) { + centerFocus(dlg, true, false); + } + else if (dlg.classList.contains('smoothScrollY')) { + centerFocus(dlg, false, false); + } + } } function tryRemoveElement(elem) { var parentNode = elem.parentNode; - if (parentNode) try { - parentNode.removeChild(elem) - } catch (err) { - console.log("Error removing dialog element: " + err) + if (parentNode) { + + // Seeing crashes in edge webview + try { + parentNode.removeChild(elem); + } catch (err) { + console.log('Error removing dialog element: ' + err); + } } } function DialogHashHandler(dlg, hash, resolve) { + + var self = this; + self.originalUrl = window.location.href; + var activeElement = document.activeElement; + var removeScrollLockOnClose = false; + function onHashChange(e) { + var isBack = self.originalUrl === window.location.href; - !isBack && isOpened(dlg) || window.removeEventListener("popstate", onHashChange), isBack && (self.closedByBack = !0, closeDialog(dlg)) + + if (isBack || !isOpened(dlg)) { + window.removeEventListener('popstate', onHashChange); + } + + if (isBack) { + self.closedByBack = true; + closeDialog(dlg); + } } function onBackCommand(e) { - "back" === e.detail.command && (self.closedByBack = !0, e.preventDefault(), e.stopPropagation(), closeDialog(dlg)) + + if (e.detail.command === 'back') { + self.closedByBack = true; + e.preventDefault(); + e.stopPropagation(); + closeDialog(dlg); + } } function onDialogClosed() { - if (isHistoryEnabled(dlg) || inputManager.off(dlg, onBackCommand), window.removeEventListener("popstate", onHashChange), removeBackdrop(dlg), dlg.classList.remove("opened"), removeScrollLockOnClose && document.body.classList.remove("noScroll"), !self.closedByBack && isHistoryEnabled(dlg)) { - (history.state || {}).dialogId === hash && history.back() + + if (!isHistoryEnabled(dlg)) { + inputManager.off(dlg, onBackCommand); } - if (layoutManager.tv && focusManager.focus(activeElement), "false" !== dlg.getAttribute("data-removeonclose")) { + + window.removeEventListener('popstate', onHashChange); + + removeBackdrop(dlg); + dlg.classList.remove('opened'); + + if (removeScrollLockOnClose) { + document.body.classList.remove('noScroll'); + } + + if (!self.closedByBack && isHistoryEnabled(dlg)) { + var state = history.state || {}; + if (state.dialogId === hash) { + history.back(); + } + } + + if (layoutManager.tv) { + focusManager.focus(activeElement); + } + + if (dlg.getAttribute('data-removeonclose') !== 'false') { removeCenterFocus(dlg); + var dialogContainer = dlg.dialogContainer; - dialogContainer ? (tryRemoveElement(dialogContainer), dlg.dialogContainer = null) : tryRemoveElement(dlg) + if (dialogContainer) { + tryRemoveElement(dialogContainer); + dlg.dialogContainer = null; + } else { + tryRemoveElement(dlg); + } } - setTimeout(function() { + + //resolve(); + // if we just called history.back(), then use a timeout to allow the history events to fire first + setTimeout(function () { resolve({ element: dlg, closedByBack: self.closedByBack - }) - }, 1) + }); + }, 1); + } + + dlg.addEventListener('close', onDialogClosed); + + var center = !dlg.classList.contains('dialog-fixedSize'); + if (center) { + dlg.classList.add('centeredDialog'); + } + + dlg.classList.remove('hide'); + + addBackdropOverlay(dlg); + + dlg.classList.add('opened'); + dlg.dispatchEvent(new CustomEvent('open', { + bubbles: false, + cancelable: false + })); + + if (dlg.getAttribute('data-lockscroll') === 'true' && !document.body.classList.contains('noScroll')) { + document.body.classList.add('noScroll'); + removeScrollLockOnClose = true; + } + + animateDialogOpen(dlg); + + if (isHistoryEnabled(dlg)) { + appRouter.pushState({ dialogId: hash }, "Dialog", '#' + hash); + + window.addEventListener('popstate', onHashChange); + } else { + inputManager.on(dlg, onBackCommand); } - var self = this; - self.originalUrl = window.location.href; - var activeElement = document.activeElement, - removeScrollLockOnClose = !1; - dlg.addEventListener("close", onDialogClosed), !dlg.classList.contains("dialog-fixedSize") && dlg.classList.add("centeredDialog"), dlg.classList.remove("hide"), addBackdropOverlay(dlg), dlg.classList.add("opened"), dlg.dispatchEvent(new CustomEvent("open", { - bubbles: !1, - cancelable: !1 - })), "true" !== dlg.getAttribute("data-lockscroll") || document.body.classList.contains("noScroll") || (document.body.classList.add("noScroll"), removeScrollLockOnClose = !0), animateDialogOpen(dlg), isHistoryEnabled(dlg) ? (appRouter.pushState({ - dialogId: hash - }, "Dialog", "#" + hash), window.addEventListener("popstate", onHashChange)) : inputManager.on(dlg, onBackCommand) } function addBackdropOverlay(dlg) { - var backdrop = document.createElement("div"); - backdrop.classList.add("dialogBackdrop"); + + var backdrop = document.createElement('div'); + backdrop.classList.add('dialogBackdrop'); + var backdropParent = dlg.dialogContainer || dlg; - backdropParent.parentNode.insertBefore(backdrop, backdropParent), dlg.backdrop = backdrop, backdrop.offsetWidth, backdrop.classList.add("dialogBackdropOpened"), dom.addEventListener(dlg.dialogContainer || backdrop, "click", function(e) { - e.target === dlg.dialogContainer && close(dlg) + backdropParent.parentNode.insertBefore(backdrop, backdropParent); + dlg.backdrop = backdrop; + + // trigger reflow or the backdrop will not animate + void backdrop.offsetWidth; + backdrop.classList.add('dialogBackdropOpened'); + + dom.addEventListener((dlg.dialogContainer || backdrop), 'click', function (e) { + if (e.target === dlg.dialogContainer) { + close(dlg); + } }, { - passive: !0 - }) + passive: true + }); } function isHistoryEnabled(dlg) { - return "true" === dlg.getAttribute("data-history") + return dlg.getAttribute('data-history') === 'true'; } function open(dlg) { - globalOnOpenCallback && globalOnOpenCallback(dlg); + + if (globalOnOpenCallback) { + globalOnOpenCallback(dlg); + } + var parent = dlg.parentNode; - parent && parent.removeChild(dlg); - var dialogContainer = document.createElement("div"); - return dialogContainer.classList.add("dialogContainer"), dialogContainer.appendChild(dlg), dlg.dialogContainer = dialogContainer, document.body.appendChild(dialogContainer), new Promise(function(resolve, reject) { - new DialogHashHandler(dlg, "dlg" + (new Date).getTime(), resolve) - }) + if (parent) { + parent.removeChild(dlg); + } + + var dialogContainer = document.createElement('div'); + dialogContainer.classList.add('dialogContainer'); + dialogContainer.appendChild(dlg); + dlg.dialogContainer = dialogContainer; + document.body.appendChild(dialogContainer); + + return new Promise(function (resolve, reject) { + + new DialogHashHandler(dlg, 'dlg' + new Date().getTime(), resolve); + }); } function isOpened(dlg) { - return !dlg.classList.contains("hide") + + //return dlg.opened; + return !dlg.classList.contains('hide'); } function close(dlg) { - isOpened(dlg) && (isHistoryEnabled(dlg) ? history.back() : closeDialog(dlg)) + + if (isOpened(dlg)) { + if (isHistoryEnabled(dlg)) { + history.back(); + } else { + closeDialog(dlg); + } + } } function closeDialog(dlg) { - if (!dlg.classList.contains("hide")) { - dlg.dispatchEvent(new CustomEvent("closing", { - bubbles: !1, - cancelable: !1 + + if (!dlg.classList.contains('hide')) { + + dlg.dispatchEvent(new CustomEvent('closing', { + bubbles: false, + cancelable: false })); - animateDialogClose(dlg, function() { - focusManager.popScope(dlg), dlg.classList.add("hide"), dlg.dispatchEvent(new CustomEvent("close", { - bubbles: !1, - cancelable: !1 - })) - }) + + var onAnimationFinish = function () { + focusManager.popScope(dlg); + + dlg.classList.add('hide'); + dlg.dispatchEvent(new CustomEvent('close', { + bubbles: false, + cancelable: false + })); + }; + + animateDialogClose(dlg, onAnimationFinish); } } function animateDialogOpen(dlg) { - var onAnimationFinish = function() { - focusManager.pushScope(dlg), "true" === dlg.getAttribute("data-autofocus") && focusManager.autoFocus(dlg) + + var onAnimationFinish = function () { + focusManager.pushScope(dlg); + if (dlg.getAttribute('data-autofocus') === 'true') { + focusManager.autoFocus(dlg); + } }; + if (enableAnimation()) { - var onFinish = function() { + + var onFinish = function () { dom.removeEventListener(dlg, dom.whichAnimationEvent(), onFinish, { - once: !0 - }), onAnimationFinish() + once: true + }); + onAnimationFinish(); }; - return void dom.addEventListener(dlg, dom.whichAnimationEvent(), onFinish, { - once: !0 - }) + dom.addEventListener(dlg, dom.whichAnimationEvent(), onFinish, { + once: true + }); + return; } - onAnimationFinish() + + onAnimationFinish(); } function animateDialogClose(dlg, onAnimationFinish) { + if (enableAnimation()) { - var animated = !0; + + var animated = true; + switch (dlg.animationConfig.exit.name) { - case "fadeout": - dlg.style.animation = "fadeout " + dlg.animationConfig.exit.timing.duration + "ms ease-out normal both"; + + case 'fadeout': + dlg.style.animation = 'fadeout ' + dlg.animationConfig.exit.timing.duration + 'ms ease-out normal both'; break; - case "scaledown": - dlg.style.animation = "scaledown " + dlg.animationConfig.exit.timing.duration + "ms ease-out normal both"; + case 'scaledown': + dlg.style.animation = 'scaledown ' + dlg.animationConfig.exit.timing.duration + 'ms ease-out normal both'; break; - case "slidedown": - dlg.style.animation = "slidedown " + dlg.animationConfig.exit.timing.duration + "ms ease-out normal both"; + case 'slidedown': + dlg.style.animation = 'slidedown ' + dlg.animationConfig.exit.timing.duration + 'ms ease-out normal both'; break; default: - animated = !1 + animated = false; + break; } - var onFinish = function() { + var onFinish = function () { dom.removeEventListener(dlg, dom.whichAnimationEvent(), onFinish, { - once: !0 - }), onAnimationFinish() + once: true + }); + onAnimationFinish(); }; - if (dom.addEventListener(dlg, dom.whichAnimationEvent(), onFinish, { - once: !0 - }), animated) return + dom.addEventListener(dlg, dom.whichAnimationEvent(), onFinish, { + once: true + }); + + if (animated) { + return; + } } - onAnimationFinish() + + onAnimationFinish(); } + var supportsOverscrollBehavior = 'overscroll-behavior-y' in document.body.style; + function shouldLockDocumentScroll(options) { - return !(supportsOverscrollBehavior && (options.size || !browser.touch)) && (null != options.lockScroll ? options.lockScroll : "fullscreen" === options.size || (!!options.size || browser.touch)) + + if (supportsOverscrollBehavior && (options.size || !browser.touch)) { + return false; + } + + if (options.lockScroll != null) { + return options.lockScroll; + } + + if (options.size === 'fullscreen') { + return true; + } + + if (options.size) { + return true; + } + + return browser.touch; } function removeBackdrop(dlg) { + var backdrop = dlg.backdrop; - if (backdrop) { - dlg.backdrop = null; - var onAnimationFinish = function() { - tryRemoveElement(backdrop) - }; - if (enableAnimation()) return backdrop.classList.remove("dialogBackdropOpened"), void setTimeout(onAnimationFinish, 300); - onAnimationFinish() + + if (!backdrop) { + return; } + + dlg.backdrop = null; + + var onAnimationFinish = function () { + tryRemoveElement(backdrop); + }; + + if (enableAnimation()) { + + backdrop.classList.remove('dialogBackdropOpened'); + + // this is not firing animatonend + setTimeout(onAnimationFinish, 300); + return; + } + + onAnimationFinish(); } function centerFocus(elem, horiz, on) { - require(["scrollHelper"], function(scrollHelper) { - var fn = on ? "on" : "off"; - scrollHelper.centerFocus[fn](elem, horiz) - }) + require(['scrollHelper'], function (scrollHelper) { + var fn = on ? 'on' : 'off'; + scrollHelper.centerFocus[fn](elem, horiz); + }); } function createDialog(options) { + options = options || {}; - var dlg = document.createElement("div"); - dlg.classList.add("focuscontainer"), dlg.classList.add("hide"), shouldLockDocumentScroll(options) && dlg.setAttribute("data-lockscroll", "true"), !1 !== options.enableHistory && appRouter.enableNativeHistory() && dlg.setAttribute("data-history", "true"), !1 !== options.modal && dlg.setAttribute("modal", "modal"), !1 !== options.autoFocus && dlg.setAttribute("data-autofocus", "true"); - var defaultEntryAnimation, defaultExitAnimation; - defaultEntryAnimation = "scaleup", defaultExitAnimation = "scaledown"; - var entryAnimation = options.entryAnimation || defaultEntryAnimation, - exitAnimation = options.exitAnimation || defaultExitAnimation, - entryAnimationDuration = options.entryAnimationDuration || ("fullscreen" !== options.size ? 180 : 280), - exitAnimationDuration = options.exitAnimationDuration || ("fullscreen" !== options.size ? 120 : 220); - if (dlg.animationConfig = { - entry: { - name: entryAnimation, - timing: { - duration: entryAnimationDuration, - easing: "ease-out" - } - }, - exit: { - name: exitAnimation, - timing: { - duration: exitAnimationDuration, - easing: "ease-out", - fill: "both" - } - } - }, dlg.classList.add("dialog"), options.scrollX ? (dlg.classList.add("scrollX"), dlg.classList.add("smoothScrollX"), layoutManager.tv && centerFocus(dlg, !0, !0)) : !1 !== options.scrollY && (dlg.classList.add("smoothScrollY"), layoutManager.tv && centerFocus(dlg, !1, !0)), options.removeOnClose && dlg.setAttribute("data-removeonclose", "true"), options.size && (dlg.classList.add("dialog-fixedSize"), dlg.classList.add("dialog-" + options.size)), enableAnimation()) switch (dlg.animationConfig.entry.name) { - case "fadein": - dlg.style.animation = "fadein " + entryAnimationDuration + "ms ease-out normal"; - break; - case "scaleup": - dlg.style.animation = "scaleup " + entryAnimationDuration + "ms ease-out normal both"; - break; - case "slideup": - dlg.style.animation = "slideup " + entryAnimationDuration + "ms ease-out normal"; - break; - case "slidedown": - dlg.style.animation = "slidedown " + entryAnimationDuration + "ms ease-out normal" + + // If there's no native dialog support, use a plain div + // Also not working well in samsung tizen browser, content inside not clickable + // Just go ahead and always use a plain div because we're seeing issues overlaying absoltutely positioned content over a modal dialog + var dlg = document.createElement('div'); + + dlg.classList.add('focuscontainer'); + dlg.classList.add('hide'); + + if (shouldLockDocumentScroll(options)) { + dlg.setAttribute('data-lockscroll', 'true'); } - return dlg + + if (options.enableHistory !== false && appRouter.enableNativeHistory()) { + dlg.setAttribute('data-history', 'true'); + } + + // without this safari will scroll the background instead of the dialog contents + // but not needed here since this is already on top of an existing dialog + // but skip it in IE because it's causing the entire browser to hang + // Also have to disable for firefox because it's causing select elements to not be clickable + if (options.modal !== false) { + dlg.setAttribute('modal', 'modal'); + } + + if (options.autoFocus !== false) { + dlg.setAttribute('data-autofocus', 'true'); + } + + var defaultEntryAnimation; + var defaultExitAnimation; + + defaultEntryAnimation = 'scaleup'; + defaultExitAnimation = 'scaledown'; + var entryAnimation = options.entryAnimation || defaultEntryAnimation; + var exitAnimation = options.exitAnimation || defaultExitAnimation; + + // If it's not fullscreen then lower the default animation speed to make it open really fast + var entryAnimationDuration = options.entryAnimationDuration || (options.size !== 'fullscreen' ? 180 : 280); + var exitAnimationDuration = options.exitAnimationDuration || (options.size !== 'fullscreen' ? 120 : 220); + + dlg.animationConfig = { + // scale up + 'entry': { + name: entryAnimation, + timing: { + duration: entryAnimationDuration, + easing: 'ease-out' + } + }, + // fade out + 'exit': { + name: exitAnimation, + timing: { + duration: exitAnimationDuration, + easing: 'ease-out', + fill: 'both' + } + } + }; + + dlg.classList.add('dialog'); + + if (options.scrollX) { + dlg.classList.add('scrollX'); + dlg.classList.add('smoothScrollX'); + + if (layoutManager.tv) { + centerFocus(dlg, true, true); + } + } + else if (options.scrollY !== false) { + dlg.classList.add('smoothScrollY'); + + if (layoutManager.tv) { + centerFocus(dlg, false, true); + } + } + + if (options.removeOnClose) { + dlg.setAttribute('data-removeonclose', 'true'); + } + + if (options.size) { + dlg.classList.add('dialog-fixedSize'); + dlg.classList.add('dialog-' + options.size); + } + + if (enableAnimation()) { + + switch (dlg.animationConfig.entry.name) { + + case 'fadein': + dlg.style.animation = 'fadein ' + entryAnimationDuration + 'ms ease-out normal'; + break; + case 'scaleup': + dlg.style.animation = 'scaleup ' + entryAnimationDuration + 'ms ease-out normal both'; + break; + case 'slideup': + dlg.style.animation = 'slideup ' + entryAnimationDuration + 'ms ease-out normal'; + break; + case 'slidedown': + dlg.style.animation = 'slidedown ' + entryAnimationDuration + 'ms ease-out normal'; + break; + default: + break; + } + } + + return dlg; } - var globalOnOpenCallback, supportsOverscrollBehavior = "overscroll-behavior-y" in document.body.style; + return { open: open, close: close, createDialog: createDialog, - setOnOpen: function(val) { - globalOnOpenCallback = val + setOnOpen: function (val) { + globalOnOpenCallback = val; } - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/displaysettings/displaysettings.js b/src/bower_components/emby-webcomponents/displaysettings/displaysettings.js index 36158eaecc..90eaeba09e 100644 --- a/src/bower_components/emby-webcomponents/displaysettings/displaysettings.js +++ b/src/bower_components/emby-webcomponents/displaysettings/displaysettings.js @@ -1,122 +1,343 @@ -define(["require", "browser", "layoutManager", "appSettings", "pluginManager", "apphost", "focusManager", "datetime", "globalize", "loading", "connectionManager", "skinManager", "dom", "events", "emby-select", "emby-checkbox", "emby-linkbutton"], function(require, browser, layoutManager, appSettings, pluginManager, appHost, focusManager, datetime, globalize, loading, connectionManager, skinManager, dom, events) { +define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', 'apphost', 'focusManager', 'datetime', 'globalize', 'loading', 'connectionManager', 'skinManager', 'dom', 'events', 'emby-select', 'emby-checkbox', 'emby-linkbutton'], function (require, browser, layoutManager, appSettings, pluginManager, appHost, focusManager, datetime, globalize, loading, connectionManager, skinManager, dom, events) { "use strict"; function fillThemes(select, isDashboard) { - select.innerHTML = skinManager.getThemes().map(function(t) { + + select.innerHTML = skinManager.getThemes().map(function (t) { + var value = t.id; - return t.isDefault && !isDashboard ? value = "" : t.isDefaultServerDashboard && isDashboard && (value = ""), '" - }).join("") + + if (t.isDefault && !isDashboard) { + value = ''; + } + else if (t.isDefaultServerDashboard && isDashboard) { + value = ''; + } + + return ''; + + }).join(''); } function loadScreensavers(context, userSettings) { - var selectScreensaver = context.querySelector(".selectScreensaver"), - options = pluginManager.ofType("screensaver").map(function(plugin) { - return { - name: plugin.name, - value: plugin.id - } - }); + + var selectScreensaver = context.querySelector('.selectScreensaver'); + var options = pluginManager.ofType('screensaver').map(function (plugin) { + return { + name: plugin.name, + value: plugin.id + }; + }); + options.unshift({ - name: globalize.translate("sharedcomponents#None"), - value: "none" - }), selectScreensaver.innerHTML = options.map(function(o) { - return '" - }).join(""), selectScreensaver.value = userSettings.screensaver(), selectScreensaver.value || (selectScreensaver.value = "none") + name: globalize.translate('sharedcomponents#None'), + value: 'none' + }); + + selectScreensaver.innerHTML = options.map(function (o) { + return ''; + }).join(''); + selectScreensaver.value = userSettings.screensaver(); + + if (!selectScreensaver.value) { + // TODO: set the default instead of none + selectScreensaver.value = 'none'; + } } function loadSoundEffects(context, userSettings) { - var selectSoundEffects = context.querySelector(".selectSoundEffects"), - options = pluginManager.ofType("soundeffects").map(function(plugin) { - return { - name: plugin.name, - value: plugin.id - } - }); + + var selectSoundEffects = context.querySelector('.selectSoundEffects'); + var options = pluginManager.ofType('soundeffects').map(function (plugin) { + return { + name: plugin.name, + value: plugin.id + }; + }); + options.unshift({ - name: globalize.translate("sharedcomponents#None"), - value: "none" - }), selectSoundEffects.innerHTML = options.map(function(o) { - return '" - }).join(""), selectSoundEffects.value = userSettings.soundEffects(), selectSoundEffects.value || (selectSoundEffects.value = "none") + name: globalize.translate('sharedcomponents#None'), + value: 'none' + }); + + selectSoundEffects.innerHTML = options.map(function (o) { + return ''; + }).join(''); + selectSoundEffects.value = userSettings.soundEffects(); + + if (!selectSoundEffects.value) { + // TODO: set the default instead of none + selectSoundEffects.value = 'none'; + } } function loadSkins(context, userSettings) { - var selectSkin = context.querySelector(".selectSkin"), - options = pluginManager.ofType("skin").map(function(plugin) { - return { - name: plugin.name, - value: plugin.id - } - }); - selectSkin.innerHTML = options.map(function(o) { - return '" - }).join(""), selectSkin.value = userSettings.skin(), !selectSkin.value && options.length && (selectSkin.value = options[0].value), options.length > 1 && appHost.supports("skins") ? context.querySelector(".selectSkinContainer").classList.remove("hide") : context.querySelector(".selectSkinContainer").classList.add("hide") + + var selectSkin = context.querySelector('.selectSkin'); + + var options = pluginManager.ofType('skin').map(function (plugin) { + return { + name: plugin.name, + value: plugin.id + }; + }); + + selectSkin.innerHTML = options.map(function (o) { + return ''; + }).join(''); + selectSkin.value = userSettings.skin(); + + if (!selectSkin.value && options.length) { + selectSkin.value = options[0].value; + } + + if (options.length > 1 && appHost.supports('skins')) { + context.querySelector('.selectSkinContainer').classList.remove('hide'); + } else { + context.querySelector('.selectSkinContainer').classList.add('hide'); + } } function showOrHideMissingEpisodesField(context, user, apiClient) { - if (browser.tizen || browser.web0s) return void context.querySelector(".fldDisplayMissingEpisodes").classList.add("hide"); - context.querySelector(".fldDisplayMissingEpisodes").classList.remove("hide") + + if (browser.tizen || browser.web0s) { + context.querySelector('.fldDisplayMissingEpisodes').classList.add('hide'); + return; + } + + context.querySelector('.fldDisplayMissingEpisodes').classList.remove('hide'); } function loadForm(context, user, userSettings, apiClient) { - apiClient.getCurrentUserId(), user.Id; - user.Policy.IsAdministrator ? context.querySelector(".selectDashboardThemeContainer").classList.remove("hide") : context.querySelector(".selectDashboardThemeContainer").classList.add("hide"), appHost.supports("displaylanguage") ? context.querySelector(".languageSection").classList.remove("hide") : context.querySelector(".languageSection").classList.add("hide"), appHost.supports("displaymode") ? context.querySelector(".fldDisplayMode").classList.remove("hide") : context.querySelector(".fldDisplayMode").classList.add("hide"), appHost.supports("externallinks") ? context.querySelector(".learnHowToContributeContainer").classList.remove("hide") : context.querySelector(".learnHowToContributeContainer").classList.add("hide"), appHost.supports("runatstartup") ? context.querySelector(".fldAutorun").classList.remove("hide") : context.querySelector(".fldAutorun").classList.add("hide"), appHost.supports("soundeffects") ? context.querySelector(".fldSoundEffects").classList.remove("hide") : context.querySelector(".fldSoundEffects").classList.add("hide"), appHost.supports("screensaver") ? context.querySelector(".selectScreensaverContainer").classList.remove("hide") : context.querySelector(".selectScreensaverContainer").classList.add("hide"), datetime.supportsLocalization() ? context.querySelector(".fldDateTimeLocale").classList.remove("hide") : context.querySelector(".fldDateTimeLocale").classList.add("hide"), browser.tizen || browser.web0s ? (context.querySelector(".fldSeasonalThemes").classList.add("hide"), context.querySelector(".fldBackdrops").classList.add("hide"), context.querySelector(".fldThemeSong").classList.add("hide"), context.querySelector(".fldThemeVideo").classList.add("hide")) : (context.querySelector(".fldSeasonalThemes").classList.remove("hide"), context.querySelector(".fldBackdrops").classList.remove("hide"), context.querySelector(".fldThemeSong").classList.remove("hide"), context.querySelector(".fldThemeVideo").classList.remove("hide")), context.querySelector(".chkRunAtStartup").checked = appSettings.runAtStartup(); - var selectTheme = context.querySelector("#selectTheme"), - selectDashboardTheme = context.querySelector("#selectDashboardTheme"); - fillThemes(selectTheme), fillThemes(selectDashboardTheme, !0), loadScreensavers(context, userSettings), loadSoundEffects(context, userSettings), loadSkins(context, userSettings), context.querySelector(".chkDisplayMissingEpisodes").checked = user.Configuration.DisplayMissingEpisodes || !1, context.querySelector("#chkThemeSong").checked = userSettings.enableThemeSongs(), context.querySelector("#chkThemeVideo").checked = userSettings.enableThemeVideos(), context.querySelector("#chkBackdrops").checked = userSettings.enableBackdrops(), context.querySelector("#chkSeasonalThemes").checked = userSettings.enableSeasonalThemes(), context.querySelector("#selectLanguage").value = userSettings.language() || "", context.querySelector(".selectDateTimeLocale").value = userSettings.dateTimeLocale() || "", selectDashboardTheme.value = userSettings.dashboardTheme() || "", selectTheme.value = userSettings.theme() || "", context.querySelector(".selectLayout").value = layoutManager.getSavedLayout() || "", showOrHideMissingEpisodesField(context, user, apiClient), loading.hide() + + var loggedInUserId = apiClient.getCurrentUserId(); + var userId = user.Id; + + if (user.Policy.IsAdministrator) { + context.querySelector('.selectDashboardThemeContainer').classList.remove('hide'); + } else { + context.querySelector('.selectDashboardThemeContainer').classList.add('hide'); + } + + if (appHost.supports('displaylanguage')) { + context.querySelector('.languageSection').classList.remove('hide'); + } else { + context.querySelector('.languageSection').classList.add('hide'); + } + + if (appHost.supports('displaymode')) { + context.querySelector('.fldDisplayMode').classList.remove('hide'); + } else { + context.querySelector('.fldDisplayMode').classList.add('hide'); + } + + if (appHost.supports('externallinks')) { + context.querySelector('.learnHowToContributeContainer').classList.remove('hide'); + } else { + context.querySelector('.learnHowToContributeContainer').classList.add('hide'); + } + + if (appHost.supports('runatstartup')) { + context.querySelector('.fldAutorun').classList.remove('hide'); + } else { + context.querySelector('.fldAutorun').classList.add('hide'); + } + + if (appHost.supports('soundeffects')) { + context.querySelector('.fldSoundEffects').classList.remove('hide'); + } else { + context.querySelector('.fldSoundEffects').classList.add('hide'); + } + + if (appHost.supports('screensaver')) { + context.querySelector('.selectScreensaverContainer').classList.remove('hide'); + } else { + context.querySelector('.selectScreensaverContainer').classList.add('hide'); + } + + if (datetime.supportsLocalization()) { + context.querySelector('.fldDateTimeLocale').classList.remove('hide'); + } else { + context.querySelector('.fldDateTimeLocale').classList.add('hide'); + } + + if (!browser.tizen && !browser.web0s) { + context.querySelector('.fldSeasonalThemes').classList.remove('hide'); + context.querySelector('.fldBackdrops').classList.remove('hide'); + context.querySelector('.fldThemeSong').classList.remove('hide'); + context.querySelector('.fldThemeVideo').classList.remove('hide'); + } else { + context.querySelector('.fldSeasonalThemes').classList.add('hide'); + context.querySelector('.fldBackdrops').classList.add('hide'); + context.querySelector('.fldThemeSong').classList.add('hide'); + context.querySelector('.fldThemeVideo').classList.add('hide'); + } + + context.querySelector('.chkRunAtStartup').checked = appSettings.runAtStartup(); + + var selectTheme = context.querySelector('#selectTheme'); + var selectDashboardTheme = context.querySelector('#selectDashboardTheme'); + + fillThemes(selectTheme); + fillThemes(selectDashboardTheme, true); + loadScreensavers(context, userSettings); + loadSoundEffects(context, userSettings); + loadSkins(context, userSettings); + + context.querySelector('.chkDisplayMissingEpisodes').checked = user.Configuration.DisplayMissingEpisodes || false; + + context.querySelector('#chkThemeSong').checked = userSettings.enableThemeSongs(); + context.querySelector('#chkThemeVideo').checked = userSettings.enableThemeVideos(); + context.querySelector('#chkBackdrops').checked = userSettings.enableBackdrops(); + context.querySelector('#chkSeasonalThemes').checked = userSettings.enableSeasonalThemes(); + + context.querySelector('#selectLanguage').value = userSettings.language() || ''; + context.querySelector('.selectDateTimeLocale').value = userSettings.dateTimeLocale() || ''; + + selectDashboardTheme.value = userSettings.dashboardTheme() || ''; + selectTheme.value = userSettings.theme() || ''; + + context.querySelector('.selectLayout').value = layoutManager.getSavedLayout() || ''; + + showOrHideMissingEpisodesField(context, user, apiClient); + + loading.hide(); } function saveUser(context, user, userSettingsInstance, apiClient) { - return appSettings.runAtStartup(context.querySelector(".chkRunAtStartup").checked), user.Configuration.DisplayMissingEpisodes = context.querySelector(".chkDisplayMissingEpisodes").checked, appHost.supports("displaylanguage") && userSettingsInstance.language(context.querySelector("#selectLanguage").value), userSettingsInstance.dateTimeLocale(context.querySelector(".selectDateTimeLocale").value), userSettingsInstance.enableThemeSongs(context.querySelector("#chkThemeSong").checked), userSettingsInstance.enableThemeVideos(context.querySelector("#chkThemeVideo").checked), userSettingsInstance.dashboardTheme(context.querySelector("#selectDashboardTheme").value), userSettingsInstance.theme(context.querySelector("#selectTheme").value), userSettingsInstance.soundEffects(context.querySelector(".selectSoundEffects").value), userSettingsInstance.screensaver(context.querySelector(".selectScreensaver").value), userSettingsInstance.skin(context.querySelector(".selectSkin").value), userSettingsInstance.enableBackdrops(context.querySelector("#chkBackdrops").checked), userSettingsInstance.enableSeasonalThemes(context.querySelector("#chkSeasonalThemes").checked), user.Id === apiClient.getCurrentUserId() && skinManager.setTheme(userSettingsInstance.theme()), layoutManager.setLayout(context.querySelector(".selectLayout").value), apiClient.updateUserConfiguration(user.Id, user.Configuration) + + appSettings.runAtStartup(context.querySelector('.chkRunAtStartup').checked); + + user.Configuration.DisplayMissingEpisodes = context.querySelector('.chkDisplayMissingEpisodes').checked; + + if (appHost.supports('displaylanguage')) { + userSettingsInstance.language(context.querySelector('#selectLanguage').value); + } + + userSettingsInstance.dateTimeLocale(context.querySelector('.selectDateTimeLocale').value); + + userSettingsInstance.enableThemeSongs(context.querySelector('#chkThemeSong').checked); + userSettingsInstance.enableThemeVideos(context.querySelector('#chkThemeVideo').checked); + userSettingsInstance.dashboardTheme(context.querySelector('#selectDashboardTheme').value); + userSettingsInstance.theme(context.querySelector('#selectTheme').value); + userSettingsInstance.soundEffects(context.querySelector('.selectSoundEffects').value); + userSettingsInstance.screensaver(context.querySelector('.selectScreensaver').value); + + userSettingsInstance.skin(context.querySelector('.selectSkin').value); + + userSettingsInstance.enableBackdrops(context.querySelector('#chkBackdrops').checked); + userSettingsInstance.enableSeasonalThemes(context.querySelector('#chkSeasonalThemes').checked); + + if (user.Id === apiClient.getCurrentUserId()) { + + skinManager.setTheme(userSettingsInstance.theme()); + } + + layoutManager.setLayout(context.querySelector('.selectLayout').value); + + return apiClient.updateUserConfiguration(user.Id, user.Configuration); } function save(instance, context, userId, userSettings, apiClient, enableSaveConfirmation) { - loading.show(), apiClient.getUser(userId).then(function(user) { - saveUser(context, user, userSettings, apiClient).then(function() { - loading.hide(), enableSaveConfirmation && require(["toast"], function(toast) { - toast(globalize.translate("sharedcomponents#SettingsSaved")) - }), events.trigger(instance, "saved") - }, function() { - loading.hide() - }) - }) + + loading.show(); + + apiClient.getUser(userId).then(function (user) { + + saveUser(context, user, userSettings, apiClient).then(function () { + + loading.hide(); + if (enableSaveConfirmation) { + require(['toast'], function (toast) { + toast(globalize.translate('sharedcomponents#SettingsSaved')); + }); + } + + events.trigger(instance, 'saved'); + + }, function () { + loading.hide(); + }); + }); } function onSubmit(e) { - var self = this, - apiClient = connectionManager.getApiClient(self.options.serverId), - userId = self.options.userId, - userSettings = self.options.userSettings; - return userSettings.setUserInfo(userId, apiClient).then(function() { + + var self = this; + var apiClient = connectionManager.getApiClient(self.options.serverId); + var userId = self.options.userId; + var userSettings = self.options.userSettings; + + userSettings.setUserInfo(userId, apiClient).then(function () { + var enableSaveConfirmation = self.options.enableSaveConfirmation; - save(self, self.options.element, userId, userSettings, apiClient, enableSaveConfirmation) - }), e && e.preventDefault(), !1 + save(self, self.options.element, userId, userSettings, apiClient, enableSaveConfirmation); + }); + + // Disable default form submission + if (e) { + e.preventDefault(); + } + return false; } function embed(options, self) { - require(["text!./displaysettings.template.html"], function(template) { - options.element.innerHTML = globalize.translateDocument(template, "sharedcomponents"), options.element.querySelector("form").addEventListener("submit", onSubmit.bind(self)), options.enableSaveButton && options.element.querySelector(".btnSave").classList.remove("hide"), self.loadData(options.autoFocus) - }) + + require(['text!./displaysettings.template.html'], function (template) { + + options.element.innerHTML = globalize.translateDocument(template, 'sharedcomponents'); + + options.element.querySelector('form').addEventListener('submit', onSubmit.bind(self)); + + if (options.enableSaveButton) { + options.element.querySelector('.btnSave').classList.remove('hide'); + } + + self.loadData(options.autoFocus); + }); } function DisplaySettings(options) { - this.options = options, embed(options, this) + + this.options = options; + + embed(options, this); } - return DisplaySettings.prototype.loadData = function(autoFocus) { - var self = this, - context = self.options.element; + + DisplaySettings.prototype.loadData = function (autoFocus) { + + var self = this; + var context = self.options.element; + loading.show(); - var userId = self.options.userId, - apiClient = connectionManager.getApiClient(self.options.serverId), - userSettings = self.options.userSettings; - return apiClient.getUser(userId).then(function(user) { - return userSettings.setUserInfo(userId, apiClient).then(function() { - self.dataLoaded = !0, loadForm(context, user, userSettings, apiClient), autoFocus && focusManager.autoFocus(context) - }) - }) - }, DisplaySettings.prototype.submit = function() { - onSubmit.call(this) - }, DisplaySettings.prototype.destroy = function() { - this.options = null - }, DisplaySettings + + var userId = self.options.userId; + var apiClient = connectionManager.getApiClient(self.options.serverId); + var userSettings = self.options.userSettings; + + return apiClient.getUser(userId).then(function (user) { + + return userSettings.setUserInfo(userId, apiClient).then(function () { + + self.dataLoaded = true; + + loadForm(context, user, userSettings, apiClient); + + if (autoFocus) { + focusManager.autoFocus(context); + } + }); + }); + }; + + DisplaySettings.prototype.submit = function () { + onSubmit.call(this); + }; + + DisplaySettings.prototype.destroy = function () { + + this.options = null; + }; + + return DisplaySettings; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/dom.js b/src/bower_components/emby-webcomponents/dom.js index cbc85e28a4..ea8902b98e 100644 --- a/src/bower_components/emby-webcomponents/dom.js +++ b/src/bower_components/emby-webcomponents/dom.js @@ -1,96 +1,170 @@ -define([], function() { - "use strict"; +define([], function () { + 'use strict'; function parentWithAttribute(elem, name, value) { - for (; value ? elem.getAttribute(name) !== value : !elem.getAttribute(name);) - if (!(elem = elem.parentNode) || !elem.getAttribute) return null; - return elem + + while ((value ? elem.getAttribute(name) !== value : !elem.getAttribute(name))) { + elem = elem.parentNode; + + if (!elem || !elem.getAttribute) { + return null; + } + } + + return elem; } function parentWithTag(elem, tagNames) { - for (Array.isArray(tagNames) || (tagNames = [tagNames]); - 1 === tagNames.indexOf(elem.tagName || "");) - if (!(elem = elem.parentNode)) return null; - return elem + + // accept both string and array passed in + if (!Array.isArray(tagNames)) { + tagNames = [tagNames]; + } + + while (tagNames.indexOf(elem.tagName || '') === -1) { + elem = elem.parentNode; + + if (!elem) { + return null; + } + } + + return elem; } function containsAnyClass(classList, classNames) { - for (var i = 0, length = classNames.length; i < length; i++) - if (classList.contains(classNames[i])) return !0; - return !1 + + for (var i = 0, length = classNames.length; i < length; i++) { + if (classList.contains(classNames[i])) { + return true; + } + } + return false; } function parentWithClass(elem, classNames) { - for (Array.isArray(classNames) || (classNames = [classNames]); !elem.classList || !containsAnyClass(elem.classList, classNames);) - if (!(elem = elem.parentNode)) return null; - return elem + + // accept both string and array passed in + if (!Array.isArray(classNames)) { + classNames = [classNames]; + } + + while (!elem.classList || !containsAnyClass(elem.classList, classNames)) { + elem = elem.parentNode; + + if (!elem) { + return null; + } + } + + return elem; } + var supportsCaptureOption = false; + try { + var opts = Object.defineProperty({}, 'capture', { + get: function () { + supportsCaptureOption = true; + } + }); + window.addEventListener("test", null, opts); + } catch (e) { } + function addEventListenerWithOptions(target, type, handler, options) { var optionsOrCapture = options; - supportsCaptureOption || (optionsOrCapture = options.capture), target.addEventListener(type, handler, optionsOrCapture) + if (!supportsCaptureOption) { + optionsOrCapture = options.capture; + } + target.addEventListener(type, handler, optionsOrCapture); } function removeEventListenerWithOptions(target, type, handler, options) { var optionsOrCapture = options; - supportsCaptureOption || (optionsOrCapture = options.capture), target.removeEventListener(type, handler, optionsOrCapture) + if (!supportsCaptureOption) { + optionsOrCapture = options.capture; + } + target.removeEventListener(type, handler, optionsOrCapture); } + var windowSize; + var windowSizeEventsBound; function clearWindowSize() { - windowSize = null + windowSize = null; } function getWindowSize() { - return windowSize || (windowSize = { - innerHeight: window.innerHeight, - innerWidth: window.innerWidth - }, windowSizeEventsBound || (windowSizeEventsBound = !0, addEventListenerWithOptions(window, "orientationchange", clearWindowSize, { - passive: !0 - }), addEventListenerWithOptions(window, "resize", clearWindowSize, { - passive: !0 - }))), windowSize + if (!windowSize) { + windowSize = { + innerHeight: window.innerHeight, + innerWidth: window.innerWidth + }; + + if (!windowSizeEventsBound) { + windowSizeEventsBound = true; + addEventListenerWithOptions(window, "orientationchange", clearWindowSize, { passive: true }); + addEventListenerWithOptions(window, 'resize', clearWindowSize, { passive: true }); + } + } + + return windowSize; } + var _animationEvent; function whichAnimationEvent() { - if (_animationEvent) return _animationEvent; - var t, el = document.createElement("div"), - animations = { - animation: "animationend", - OAnimation: "oAnimationEnd", - MozAnimation: "animationend", - WebkitAnimation: "webkitAnimationEnd" - }; - for (t in animations) - if (void 0 !== el.style[t]) return _animationEvent = animations[t], animations[t]; - return _animationEvent = "animationend" + + if (_animationEvent) { + return _animationEvent; + } + + var t, + el = document.createElement("div"); + var animations = { + "animation": "animationend", + "OAnimation": "oAnimationEnd", + "MozAnimation": "animationend", + "WebkitAnimation": "webkitAnimationEnd" + }; + for (t in animations) { + if (el.style[t] !== undefined) { + _animationEvent = animations[t]; + return animations[t]; + } + } + + _animationEvent = 'animationend'; + return _animationEvent; } function whichAnimationCancelEvent() { - return whichAnimationEvent().replace("animationend", "animationcancel").replace("AnimationEnd", "AnimationCancel") + + return whichAnimationEvent().replace('animationend', 'animationcancel').replace('AnimationEnd', 'AnimationCancel'); } + var _transitionEvent; function whichTransitionEvent() { - if (_transitionEvent) return _transitionEvent; - var t, el = document.createElement("div"), - transitions = { - transition: "transitionend", - OTransition: "oTransitionEnd", - MozTransition: "transitionend", - WebkitTransition: "webkitTransitionEnd" - }; - for (t in transitions) - if (void 0 !== el.style[t]) return _transitionEvent = transitions[t], transitions[t]; - return _transitionEvent = "transitionend" - } - var supportsCaptureOption = !1; - try { - var opts = Object.defineProperty({}, "capture", { - get: function() { - supportsCaptureOption = !0 + if (_transitionEvent) { + return _transitionEvent; + } + + var t, + el = document.createElement("div"); + var transitions = { + "transition": "transitionend", + "OTransition": "oTransitionEnd", + "MozTransition": "transitionend", + "WebkitTransition": "webkitTransitionEnd" + }; + for (t in transitions) { + if (el.style[t] !== undefined) { + _transitionEvent = transitions[t]; + return transitions[t]; } - }); - window.addEventListener("test", null, opts) - } catch (e) {} - var windowSize, windowSizeEventsBound, _animationEvent, _transitionEvent; + } + + _transitionEvent = 'transitionend'; + return _transitionEvent; + } + return { parentWithAttribute: parentWithAttribute, parentWithClass: parentWithClass, @@ -101,5 +175,5 @@ define([], function() { whichTransitionEvent: whichTransitionEvent, whichAnimationEvent: whichAnimationEvent, whichAnimationCancelEvent: whichAnimationCancelEvent - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/emby-button/emby-button.css b/src/bower_components/emby-webcomponents/emby-button/emby-button.css index 2147405eff..90807d2a8b 100644 --- a/src/bower_components/emby-webcomponents/emby-button/emby-button.css +++ b/src/bower_components/emby-webcomponents/emby-button/emby-button.css @@ -1,33 +1,14 @@ -.emby-button, -.fab { - -webkit-box-sizing: border-box; - -webkit-box-align: center -} - -.button-flat, -.button-link { - background: 0 0 -} - -.emby-button, -.paper-icon-button-light { - text-align: center; - font-family: inherit; - color: inherit; - outline: 0 !important; - -webkit-tap-highlight-color: transparent; - position: relative -} - .emby-button { - display: -webkit-inline-box; - display: -webkit-inline-flex; + position: relative; display: inline-flex; - -webkit-align-items: center; align-items: center; box-sizing: border-box; margin: 0 .29em; + text-align: center; font-size: inherit; + font-family: inherit; + color: inherit; + outline-width: 0; -moz-user-select: none; -ms-user-select: none; -webkit-user-select: none; @@ -35,99 +16,98 @@ cursor: pointer; z-index: 0; padding: .86em 1em; + vertical-align: middle; border: 0; vertical-align: middle; - -webkit-border-radius: .2em; border-radius: .2em; + /* These are getting an outline in opera tv browsers, which run chrome 30 */ + outline: none !important; + position: relative; font-weight: 600; + /* Disable webkit tap highlighting */ + -webkit-tap-highlight-color: rgba(0,0,0,0); text-decoration: none; - line-height: 1.35 + + /* Not crazy about this but it normalizes heights between anchors and buttons */ + line-height: 1.35; } .emby-button::-moz-focus-inner { - border: 0 + border: 0; } -.button-flat:hover { - opacity: .5 +.button-flat { + background: transparent; } + .button-flat:hover { + opacity: .5; + } + .button-link { + background: transparent; margin: 0; padding: 0; - vertical-align: initial + vertical-align: initial; } .button-link-inline { - display: inline + display: inline; } .button-link:hover { - text-decoration: underline + text-decoration: underline; } .emby-button-focusscale { - -webkit-transition: -webkit-transform 180ms ease-out !important; - -o-transition: transform 180ms ease-out !important; transition: transform 180ms ease-out !important; -webkit-transform-origin: center center; - transform-origin: center center + transform-origin: center center; } -.emby-button-focusscale:focus { - -webkit-transform: scale(1.16); - transform: scale(1.16); - z-index: 1 + .emby-button-focusscale:focus { + transform: scale(1.16); + z-index: 1; + } + +.emby-button > i { + /* For non-fab buttons that have icons */ + font-size: 1.36em; } -.emby-button>i { - font-size: 1.36em -} - -.button-link>i { - font-size: 1em +.button-link > i { + font-size: 1em; } .fab { - display: -webkit-inline-box; - display: -webkit-inline-flex; display: inline-flex; - -webkit-border-radius: 50%; border-radius: 50%; padding: .6em; box-sizing: border-box; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; - text-align: center + text-align: center; } .emby-button.block { display: block; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; margin: .25em 0; - width: 100% + width: 100%; } .paper-icon-button-light { - display: -webkit-inline-box; - display: -webkit-inline-flex; + position: relative; display: inline-flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - -webkit-box-sizing: border-box; box-sizing: border-box; margin: 0 .29em; - background: 0 0; + background: transparent; + text-align: center; font-size: inherit; + font-family: inherit; + color: inherit; -moz-user-select: none; -ms-user-select: none; -webkit-user-select: none; @@ -139,84 +119,82 @@ width: auto; height: auto; padding: .556em; + vertical-align: middle; border: 0; vertical-align: middle; + /* These are getting an outline in opera tv browsers, which run chrome 30 */ + outline: none !important; + position: relative; overflow: hidden; - -webkit-border-radius: 50%; border-radius: 50%; - -webkit-box-pack: center; - -webkit-justify-content: center; - justify-content: center + /* Disable webkit tap highlighting */ + -webkit-tap-highlight-color: rgba(0,0,0,0); + justify-content: center; } -.paper-icon-button-light::-moz-focus-inner { - border: 0 -} + .paper-icon-button-light::-moz-focus-inner { + border: 0; + } -.paper-icon-button-light[disabled] { - opacity: .3 -} + .paper-icon-button-light[disabled] { + opacity: .3; + } -.paper-icon-button-light>i { - font-size: 1.66956521739130434em; - position: relative; - z-index: 1; - vertical-align: middle -} + .paper-icon-button-light > i { + font-size: 1.66956521739130434em; + /* Make sure its on top of the ripple */ + position: relative; + z-index: 1; + vertical-align: middle; + } -.paper-icon-button-light>img { - width: 1.72em; - max-height: 100%; - position: relative; - z-index: 1; - vertical-align: middle -} + .paper-icon-button-light > img { + width: 1.72em; + /* Can't use 100% height or it will stretch past the boundaries in safari */ + /*height: 100%;*/ + max-height: 100%; + /* Make sure its on top of the ripple */ + position: relative; + z-index: 1; + vertical-align: middle; + } .emby-button-foreground { position: relative; - z-index: 1 + z-index: 1; } .icon-button-focusscale { - -webkit-transition: -webkit-transform 180ms ease-out !important; - -o-transition: transform 180ms ease-out !important; transition: transform 180ms ease-out !important; -webkit-transform-origin: center center; - transform-origin: center center + transform-origin: center center; } -.icon-button-focusscale:focus { - -webkit-transform: scale(1.3); - transform: scale(1.3); - z-index: 1 -} + .icon-button-focusscale:focus { + transform: scale(1.3); + z-index: 1; + } .btnFilterWithBubble { - position: relative + position: relative; } .filterButtonBubble { color: #fff; position: absolute; + background: #444; top: 0; right: 0; + /* padding: .5em; */ width: 1.6em; height: 1.6em; z-index: 100000000; - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; font-size: 82%; - -webkit-border-radius: 100em; border-radius: 100em; - -webkit-box-shadow: 0 4px 5px 0 rgba(0, 0, 0, .14), 0 1px 10px 0 rgba(0, 0, 0, .12), 0 2px 4px -1px rgba(0, 0, 0, .2); - box-shadow: 0 4px 5px 0 rgba(0, 0, 0, .14), 0 1px 10px 0 rgba(0, 0, 0, .12), 0 2px 4px -1px rgba(0, 0, 0, .2); + box-shadow: 0px 4px 5px 0px rgba(0, 0, 0, 0.14), 0px 1px 10px 0px rgba(0, 0, 0, 0.12), 0px 2px 4px -1px rgba(0, 0, 0, 0.2); background: #03A9F4; - font-weight: 700 -} \ No newline at end of file + font-weight: bold; +} diff --git a/src/bower_components/emby-webcomponents/emby-button/emby-button.js b/src/bower_components/emby-webcomponents/emby-button/emby-button.js index bd27515990..173d21b9bd 100644 --- a/src/bower_components/emby-webcomponents/emby-button/emby-button.js +++ b/src/bower_components/emby-webcomponents/emby-button/emby-button.js @@ -1,29 +1,99 @@ -define(["browser", "dom", "layoutManager", "shell", "appRouter", "apphost", "css!./emby-button", "registerElement"], function(browser, dom, layoutManager, shell, appRouter, appHost) { - "use strict"; +define(['browser', 'dom', 'layoutManager', 'shell', 'appRouter', 'apphost', 'css!./emby-button', 'registerElement'], function (browser, dom, layoutManager, shell, appRouter, appHost) { + 'use strict'; + + var EmbyButtonPrototype = Object.create(HTMLButtonElement.prototype); + var EmbyLinkButtonPrototype = Object.create(HTMLAnchorElement.prototype); function openPremiumInfo() { - require(["registrationServices"], function(registrationServices) { - registrationServices.showPremiereInfo() - }) + + require(['registrationServices'], function (registrationServices) { + registrationServices.showPremiereInfo(); + }); } function onAnchorClick(e) { - var href = this.getAttribute("href") || ""; - "#" !== href ? this.getAttribute("target") ? -1 === href.indexOf("emby.media/premiere") || appHost.supports("externalpremium") ? appHost.supports("targetblank") || (e.preventDefault(), shell.openUrl(href)) : (e.preventDefault(), openPremiumInfo()) : appRouter.handleAnchorClick(e) : e.preventDefault() + + var href = this.getAttribute('href') || ''; + + if (href !== '#') { + + if (this.getAttribute('target')) { + if (href.indexOf('emby.media/premiere') !== -1 && !appHost.supports('externalpremium')) { + e.preventDefault(); + openPremiumInfo(); + } + else if (!appHost.supports('targetblank')) { + e.preventDefault(); + shell.openUrl(href); + } + } else { + appRouter.handleAnchorClick(e); + } + } else { + e.preventDefault(); + } } - var EmbyButtonPrototype = Object.create(HTMLButtonElement.prototype), - EmbyLinkButtonPrototype = Object.create(HTMLAnchorElement.prototype); - return EmbyButtonPrototype.createdCallback = function() { - this.classList.contains("emby-button") || (this.classList.add("emby-button"), browser.firefox && this.classList.add("button-link-inline"), layoutManager.tv && ("false" !== this.getAttribute("data-focusscale") && this.classList.add("emby-button-focusscale"), this.classList.add("emby-button-tv"))) - }, EmbyButtonPrototype.attachedCallback = function() { - "A" === this.tagName && (dom.removeEventListener(this, "click", onAnchorClick, {}), dom.addEventListener(this, "click", onAnchorClick, {}), "true" === this.getAttribute("data-autohide") && (appHost.supports("externallinks") ? this.classList.remove("hide") : this.classList.add("hide"))) - }, EmbyButtonPrototype.detachedCallback = function() { - dom.removeEventListener(this, "click", onAnchorClick, {}) - }, EmbyLinkButtonPrototype.createdCallback = EmbyButtonPrototype.createdCallback, EmbyLinkButtonPrototype.attachedCallback = EmbyButtonPrototype.attachedCallback, document.registerElement("emby-button", { + + EmbyButtonPrototype.createdCallback = function () { + + if (this.classList.contains('emby-button')) { + return; + } + + this.classList.add('emby-button'); + + if (browser.firefox) { + // a ff hack is needed for vertical alignment + this.classList.add('button-link-inline'); + } + + if (layoutManager.tv) { + if (this.getAttribute('data-focusscale') !== 'false') { + this.classList.add('emby-button-focusscale'); + } + this.classList.add('emby-button-tv'); + } + }; + + EmbyButtonPrototype.attachedCallback = function () { + + if (this.tagName === 'A') { + + dom.removeEventListener(this, 'click', onAnchorClick, { + }); + + dom.addEventListener(this, 'click', onAnchorClick, { + }); + + if (this.getAttribute('data-autohide') === 'true') { + if (appHost.supports('externallinks')) { + this.classList.remove('hide'); + } else { + this.classList.add('hide'); + } + } + } + }; + + EmbyButtonPrototype.detachedCallback = function () { + + dom.removeEventListener(this, 'click', onAnchorClick, { + }); + }; + + EmbyLinkButtonPrototype.createdCallback = EmbyButtonPrototype.createdCallback; + EmbyLinkButtonPrototype.attachedCallback = EmbyButtonPrototype.attachedCallback; + + document.registerElement('emby-button', { prototype: EmbyButtonPrototype, - extends: "button" - }), document.registerElement("emby-linkbutton", { + extends: 'button' + }); + + document.registerElement('emby-linkbutton', { prototype: EmbyLinkButtonPrototype, - extends: "a" - }), EmbyButtonPrototype + extends: 'a' + }); + + // For extension purposes + return EmbyButtonPrototype; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/emby-button/paper-icon-button-light.js b/src/bower_components/emby-webcomponents/emby-button/paper-icon-button-light.js index 85b377c06f..b3b5c5aee3 100644 --- a/src/bower_components/emby-webcomponents/emby-button/paper-icon-button-light.js +++ b/src/bower_components/emby-webcomponents/emby-button/paper-icon-button-light.js @@ -1,10 +1,19 @@ -define(["layoutManager", "css!./emby-button", "registerElement"], function(layoutManager) { - "use strict"; +define(['layoutManager', 'css!./emby-button', 'registerElement'], function (layoutManager) { + 'use strict'; + var EmbyButtonPrototype = Object.create(HTMLButtonElement.prototype); - EmbyButtonPrototype.createdCallback = function() { - this.classList.add("paper-icon-button-light"), layoutManager.tv && this.classList.add("icon-button-focusscale") - }, document.registerElement("paper-icon-button-light", { + + EmbyButtonPrototype.createdCallback = function () { + + this.classList.add('paper-icon-button-light'); + + if (layoutManager.tv) { + this.classList.add('icon-button-focusscale'); + } + }; + + document.registerElement('paper-icon-button-light', { prototype: EmbyButtonPrototype, - extends: "button" - }) + extends: 'button' + }); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/emby-checkbox/emby-checkbox.css b/src/bower_components/emby-webcomponents/emby-checkbox/emby-checkbox.css index 7ae2f8c973..dc12b2de82 100644 --- a/src/bower_components/emby-webcomponents/emby-checkbox/emby-checkbox.css +++ b/src/bower_components/emby-webcomponents/emby-checkbox/emby-checkbox.css @@ -2,45 +2,37 @@ position: relative; z-index: 1; vertical-align: middle; - display: -webkit-inline-box; - display: -webkit-inline-flex; display: inline-flex; - -webkit-box-sizing: border-box; box-sizing: border-box; width: 100%; margin: 0; - padding: 0 0 0 2.4em; - -webkit-box-align: center; - -webkit-align-items: center; + padding: 0; + padding-left: 2.4em; align-items: center; height: 2.35em; - cursor: pointer -} - -.checkboxContainer, -.checkboxListContainer { - margin-bottom: 1.8em + cursor: pointer; } .checkboxFieldDescription { - padding-left: 2.4em + padding-left: 2.4em; } .checkboxContainer { - display: -webkit-box; - display: -webkit-flex; - display: flex + margin-bottom: 1.8em; + display: flex; +} + +.checkboxListContainer { + margin-bottom: 1.8em; } .checkboxContainer-withDescription { - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -webkit-flex-direction: column; - flex-direction: column + flex-direction: column; } .emby-checkbox { position: absolute; + /* This is for focusing purposes, so the focusManager doesn't skip over it */ width: 1px; height: 1px; margin: 0; @@ -50,112 +42,109 @@ -moz-appearance: none; -webkit-appearance: none; appearance: none; - border: none + border: none; } .checkboxOutline { position: absolute; top: 3px; left: 0; - -webkit-box-sizing: border-box; + display: inline-block; box-sizing: border-box; width: 1.83em; height: 1.83em; margin: 0; overflow: hidden; border: 2px solid currentcolor; - -webkit-border-radius: .14em; border-radius: .14em; z-index: 2; - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; - justify-content: center + justify-content: center; } +/* Commenting this out - set by theme */ +/*.emby-checkbox:checked + span + span + .checkboxOutline { + border-color: #52B54B; +}*/ + .emby-checkbox-focushelper { position: absolute; - top: -.915em; - left: -.915em; + top: -0.915em; + left: -0.915em; width: 3.66em; height: 3.66em; display: inline-block; - -webkit-box-sizing: border-box; box-sizing: border-box; - margin: 3px 0 0; - -webkit-border-radius: 50%; + margin: 3px 0 0 0; border-radius: 50%; - background-color: transparent + background-color: transparent; } +/* Commenting this out - set by theme */ +/*.emby-checkbox:focus + span + .emby-checkbox-focushelper { + background-color: rgba(82, 181, 75, 0.26); +}*/ + .checkboxIcon { font-size: 1.6em; - color: #fff + color: #fff; } .checkboxIcon-checked { - display: none + display: none; } -.emby-checkbox:checked+span+span+.checkboxOutline>.checkboxIcon-checked { - display: -webkit-box !important; - display: -webkit-flex !important; - display: flex !important +.emby-checkbox:checked + span + span + .checkboxOutline > .checkboxIcon-checked { + /* background-color set by theme */ + /*background-color: #52B54B;*/ + display: flex !important; } -.emby-checkbox:checked+span+span+.checkboxOutline>.checkboxIcon-unchecked { - display: none !important +.emby-checkbox:checked + span + span + .checkboxOutline > .checkboxIcon-unchecked { + /* background-color set by theme */ + display: none !important; } -.emby-checkbox:checked[disabled]+span+span+.checkboxOutline>.checkboxIcon { - background-color: rgba(0, 0, 0, .26) +.emby-checkbox:checked[disabled] + span + span + .checkboxOutline > .checkboxIcon { + background-color: rgba(0, 0, 0, 0.26); } .checkboxLabel { position: relative; - margin: 0 + margin: 0; } -.checkboxList>.emby-checkbox-label { - display: -webkit-box; - display: -webkit-flex; +.checkboxList > .emby-checkbox-label { display: flex; - margin: .5em 0 + margin: .5em 0; } .checkboxList-verticalwrap { - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-flex-wrap: wrap; - flex-wrap: wrap + flex-wrap: wrap; } -.checkboxList-verticalwrap>.emby-checkbox-label { - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: inline-flex; - margin: .3em 0; - width: 12em -} + .checkboxList-verticalwrap > .emby-checkbox-label { + display: inline-flex; + margin: .3em 0 .3em 0; + width: 12em; + } .checkboxList-paperList { - padding: 1em !important + padding: 1em !important; } .checkboxListLabel { - margin-bottom: .25em + margin-bottom: .25em; } @-webkit-keyframes repaintChrome { - - from, - to { - padding: 0 + from { + padding: 0; } -} \ No newline at end of file + + to { + padding: 0; + } +} diff --git a/src/bower_components/emby-webcomponents/emby-checkbox/emby-checkbox.js b/src/bower_components/emby-webcomponents/emby-checkbox/emby-checkbox.js index 38649f0934..5d350612a4 100644 --- a/src/bower_components/emby-webcomponents/emby-checkbox/emby-checkbox.js +++ b/src/bower_components/emby-webcomponents/emby-checkbox/emby-checkbox.js @@ -1,55 +1,110 @@ -define(["browser", "dom", "css!./emby-checkbox", "registerElement"], function(browser, dom) { - "use strict"; +define(['browser', 'dom', 'css!./emby-checkbox', 'registerElement'], function (browser, dom) { + 'use strict'; + + var EmbyCheckboxPrototype = Object.create(HTMLInputElement.prototype); function onKeyDown(e) { - if (13 === e.keyCode) return e.preventDefault(), this.checked = !this.checked, this.dispatchEvent(new CustomEvent("change", { - bubbles: !0 - })), !1 + + // Don't submit form on enter + if (e.keyCode === 13) { + e.preventDefault(); + + this.checked = !this.checked; + + this.dispatchEvent(new CustomEvent('change', { + bubbles: true + })); + + return false; + } } + var enableRefreshHack = browser.tizen || browser.orsay || browser.operaTv || browser.web0s ? true : false; + function forceRefresh(loading) { + var elem = this.parentNode; - elem.style.webkitAnimationName = "repaintChrome", elem.style.webkitAnimationDelay = !0 === loading ? "500ms" : "", elem.style.webkitAnimationDuration = "10ms", elem.style.webkitAnimationIterationCount = "1", setTimeout(function() { - elem.style.webkitAnimationName = "" - }, !0 === loading ? 520 : 20) + + elem.style.webkitAnimationName = 'repaintChrome'; + elem.style.webkitAnimationDelay = (loading === true ? '500ms' : ''); + elem.style.webkitAnimationDuration = '10ms'; + elem.style.webkitAnimationIterationCount = '1'; + + setTimeout(function () { + elem.style.webkitAnimationName = ''; + }, (loading === true ? 520 : 20)); } - var EmbyCheckboxPrototype = Object.create(HTMLInputElement.prototype), - enableRefreshHack = !!(browser.tizen || browser.orsay || browser.operaTv || browser.web0s); - EmbyCheckboxPrototype.attachedCallback = function() { - if ("true" !== this.getAttribute("data-embycheckbox")) { - this.setAttribute("data-embycheckbox", "true"), this.classList.add("emby-checkbox"); - var labelElement = this.parentNode; - labelElement.classList.add("emby-checkbox-label"); - var labelTextElement = labelElement.querySelector("span"), - outlineClass = "checkboxOutline", - customClass = this.getAttribute("data-outlineclass"); - customClass && (outlineClass += " " + customClass); - var checkedIcon = this.getAttribute("data-checkedicon") || "", - uncheckedIcon = this.getAttribute("data-uncheckedicon") || "", - checkHtml = '' + checkedIcon + "", - uncheckedHtml = '' + uncheckedIcon + ""; - labelElement.insertAdjacentHTML("beforeend", '' + checkHtml + uncheckedHtml + ""), labelTextElement.classList.add("checkboxLabel"), this.addEventListener("keydown", onKeyDown), enableRefreshHack && (forceRefresh.call(this, !0), dom.addEventListener(this, "click", forceRefresh, { - passive: !0 - }), dom.addEventListener(this, "blur", forceRefresh, { - passive: !0 - }), dom.addEventListener(this, "focus", forceRefresh, { - passive: !0 - }), dom.addEventListener(this, "change", forceRefresh, { - passive: !0 - })) + + EmbyCheckboxPrototype.attachedCallback = function () { + + if (this.getAttribute('data-embycheckbox') === 'true') { + return; } - }, EmbyCheckboxPrototype.detachedCallback = function() { - this.removeEventListener("keydown", onKeyDown), dom.removeEventListener(this, "click", forceRefresh, { - passive: !0 - }), dom.removeEventListener(this, "blur", forceRefresh, { - passive: !0 - }), dom.removeEventListener(this, "focus", forceRefresh, { - passive: !0 - }), dom.removeEventListener(this, "change", forceRefresh, { - passive: !0 - }) - }, document.registerElement("emby-checkbox", { + + this.setAttribute('data-embycheckbox', 'true'); + + this.classList.add('emby-checkbox'); + + var labelElement = this.parentNode; + labelElement.classList.add('emby-checkbox-label'); + + var labelTextElement = labelElement.querySelector('span'); + + var outlineClass = 'checkboxOutline'; + + var customClass = this.getAttribute('data-outlineclass'); + if (customClass) { + outlineClass += ' ' + customClass; + } + + var checkedIcon = this.getAttribute('data-checkedicon') || ''; + var uncheckedIcon = this.getAttribute('data-uncheckedicon') || ''; + var checkHtml = '' + checkedIcon + ''; + var uncheckedHtml = '' + uncheckedIcon + ''; + labelElement.insertAdjacentHTML('beforeend', '' + checkHtml + uncheckedHtml + ''); + + labelTextElement.classList.add('checkboxLabel'); + + this.addEventListener('keydown', onKeyDown); + + if (enableRefreshHack) { + + forceRefresh.call(this, true); + dom.addEventListener(this, 'click', forceRefresh, { + passive: true + }); + dom.addEventListener(this, 'blur', forceRefresh, { + passive: true + }); + dom.addEventListener(this, 'focus', forceRefresh, { + passive: true + }); + dom.addEventListener(this, 'change', forceRefresh, { + passive: true + }); + } + }; + + EmbyCheckboxPrototype.detachedCallback = function () { + + this.removeEventListener('keydown', onKeyDown); + + dom.removeEventListener(this, 'click', forceRefresh, { + passive: true + }); + dom.removeEventListener(this, 'blur', forceRefresh, { + passive: true + }); + dom.removeEventListener(this, 'focus', forceRefresh, { + passive: true + }); + dom.removeEventListener(this, 'change', forceRefresh, { + passive: true + }); + }; + + document.registerElement('emby-checkbox', { prototype: EmbyCheckboxPrototype, - extends: "input" - }) -}); \ No newline at end of file + extends: 'input' + }); +}); diff --git a/src/bower_components/emby-webcomponents/emby-collapse/emby-collapse.css b/src/bower_components/emby-webcomponents/emby-collapse/emby-collapse.css index a03a0aee72..0a982e975d 100644 --- a/src/bower_components/emby-webcomponents/emby-collapse/emby-collapse.css +++ b/src/bower_components/emby-webcomponents/emby-collapse/emby-collapse.css @@ -1,56 +1,44 @@ .emby-collapse { - margin: .5em 0 + margin: .5em 0; } .collapseContent { border-width: 0; - padding: 1.25em; + padding: 1.25em 1.25em; height: 0; - -webkit-transition-property: height; - -o-transition-property: height; transition-property: height; - -webkit-transition-duration: .3s; - -o-transition-duration: .3s; - transition-duration: .3s; - overflow: hidden + transition-duration: 300ms; + overflow: hidden; } .emby-collapsible-button { margin: 0; - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; + text-transform: none; width: 100%; text-align: left; text-transform: none; - border-width: 0 0 .1em; + border-width: 0 0 .1em 0; border-style: solid; padding-left: .1em; - background: 0 0; - -webkit-box-shadow: none; - box-shadow: none + background: transparent; + box-shadow: none; } .emby-collapse-expandIcon { - -webkit-transform-origin: 50% 50%; transform-origin: 50% 50%; - -webkit-transition: -webkit-transform 180ms ease-out; - -o-transition: transform 180ms ease-out; transition: transform 180ms ease-out; position: absolute; right: .5em; - font-size: 1.5em + font-size: 1.5em; } .emby-collapse-expandIconExpanded { - -webkit-transform: rotate(180deg); - transform: rotate(180deg) + transform: rotate(180deg); } .emby-collapsible-title { margin: 0; - padding: 0 -} \ No newline at end of file + padding: 0; +} diff --git a/src/bower_components/emby-webcomponents/emby-collapse/emby-collapse.js b/src/bower_components/emby-webcomponents/emby-collapse/emby-collapse.js index 4a237a9174..198278e1d7 100644 --- a/src/bower_components/emby-webcomponents/emby-collapse/emby-collapse.js +++ b/src/bower_components/emby-webcomponents/emby-collapse/emby-collapse.js @@ -1,43 +1,100 @@ -define(["browser", "css!./emby-collapse", "registerElement", "emby-button"], function(browser) { - "use strict"; +define(['browser', 'css!./emby-collapse', 'registerElement', 'emby-button'], function (browser) { + 'use strict'; + + var EmbyButtonPrototype = Object.create(HTMLDivElement.prototype); function slideDownToShow(button, elem) { - elem.classList.remove("hide"), elem.classList.add("expanded"), elem.style.height = "auto"; - var height = elem.offsetHeight + "px"; - elem.style.height = "0"; - elem.offsetHeight; - elem.style.height = height, setTimeout(function() { - elem.classList.contains("expanded") ? elem.classList.remove("hide") : elem.classList.add("hide"), elem.style.height = "auto" - }, 300), button.querySelector("i").classList.add("emby-collapse-expandIconExpanded") + + elem.classList.remove('hide'); + elem.classList.add('expanded'); + elem.style.height = 'auto'; + var height = elem.offsetHeight + 'px'; + elem.style.height = '0'; + + // trigger reflow + var newHeight = elem.offsetHeight; + elem.style.height = height; + + setTimeout(function () { + if (elem.classList.contains('expanded')) { + elem.classList.remove('hide'); + } else { + elem.classList.add('hide'); + } + elem.style.height = 'auto'; + }, 300); + + var icon = button.querySelector('i'); + //icon.innerHTML = 'expand_less'; + icon.classList.add('emby-collapse-expandIconExpanded'); } function slideUpToHide(button, elem) { - elem.style.height = elem.offsetHeight + "px"; - elem.offsetHeight; - elem.classList.remove("expanded"), elem.style.height = "0", setTimeout(function() { - elem.classList.contains("expanded") ? elem.classList.remove("hide") : elem.classList.add("hide") - }, 300), button.querySelector("i").classList.remove("emby-collapse-expandIconExpanded") + + elem.style.height = elem.offsetHeight + 'px'; + // trigger reflow + var newHeight = elem.offsetHeight; + + elem.classList.remove('expanded'); + elem.style.height = '0'; + + setTimeout(function () { + if (elem.classList.contains('expanded')) { + elem.classList.remove('hide'); + } else { + elem.classList.add('hide'); + } + }, 300); + + var icon = button.querySelector('i'); + //icon.innerHTML = 'expand_more'; + icon.classList.remove('emby-collapse-expandIconExpanded'); } function onButtonClick(e) { - var button = this, - collapseContent = button.parentNode.querySelector(".collapseContent"); - collapseContent.expanded ? (collapseContent.expanded = !1, slideUpToHide(button, collapseContent)) : (collapseContent.expanded = !0, slideDownToShow(button, collapseContent)) - } - var EmbyButtonPrototype = Object.create(HTMLDivElement.prototype); - EmbyButtonPrototype.attachedCallback = function() { - if (!this.classList.contains("emby-collapse")) { - this.classList.add("emby-collapse"); - var collapseContent = this.querySelector(".collapseContent"); - collapseContent && collapseContent.classList.add("hide"); - var title = this.getAttribute("title"), - html = ''; - this.insertAdjacentHTML("afterbegin", html); - var button = this.querySelector(".emby-collapsible-button"); - button.addEventListener("click", onButtonClick), "true" === this.getAttribute("data-expanded") && onButtonClick.call(button) + + var button = this; + var collapseContent = button.parentNode.querySelector('.collapseContent'); + + if (collapseContent.expanded) { + collapseContent.expanded = false; + slideUpToHide(button, collapseContent); + } else { + collapseContent.expanded = true; + slideDownToShow(button, collapseContent); } - }, document.registerElement("emby-collapse", { + } + + EmbyButtonPrototype.attachedCallback = function () { + + if (this.classList.contains('emby-collapse')) { + return; + } + + this.classList.add('emby-collapse'); + + var collapseContent = this.querySelector('.collapseContent'); + if (collapseContent) { + collapseContent.classList.add('hide'); + } + + var title = this.getAttribute('title'); + + var html = ''; + + this.insertAdjacentHTML('afterbegin', html); + + var button = this.querySelector('.emby-collapsible-button'); + + button.addEventListener('click', onButtonClick); + + if (this.getAttribute('data-expanded') === 'true') { + onButtonClick.call(button); + } + }; + + document.registerElement('emby-collapse', { prototype: EmbyButtonPrototype, - extends: "div" - }) + extends: 'div' + }); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/emby-connect/connecthelper.js b/src/bower_components/emby-webcomponents/emby-connect/connecthelper.js index 50292c9f14..054070a447 100644 --- a/src/bower_components/emby-webcomponents/emby-connect/connecthelper.js +++ b/src/bower_components/emby-webcomponents/emby-connect/connecthelper.js @@ -1,106 +1,219 @@ -define(["globalize", "apphost", "loading", "alert", "emby-linkbutton"], function(globalize, appHost, loading, alert) { - "use strict"; +define(['globalize', 'apphost', 'loading', 'alert', 'emby-linkbutton'], function (globalize, appHost, loading, alert) { + 'use strict'; function resolvePromise() { - return Promise.resolve() + return Promise.resolve(); } function rejectPromise() { - return Promise.reject() + return Promise.reject(); } function showNewUserInviteMessage(result) { - if (!result.IsNewUserInvitation && !result.IsPending) return Promise.resolve(); - var message = result.IsNewUserInvitation ? globalize.translate("sharedcomponents#MessageInvitationSentToNewUser", result.GuestDisplayName) : globalize.translate("sharedcomponents#MessageInvitationSentToUser", result.GuestDisplayName); + + if (!result.IsNewUserInvitation && !result.IsPending) { + + // It was immediately approved + return Promise.resolve(); + } + + var message = result.IsNewUserInvitation ? + globalize.translate('sharedcomponents#MessageInvitationSentToNewUser', result.GuestDisplayName) : + globalize.translate('sharedcomponents#MessageInvitationSentToUser', result.GuestDisplayName); + return alert({ + text: message, - title: globalize.translate("sharedcomponents#HeaderInvitationSent") - }).then(resolvePromise, resolvePromise) + title: globalize.translate('sharedcomponents#HeaderInvitationSent') + + }).then(resolvePromise, resolvePromise); } function inviteGuest(options) { + var apiClient = options.apiClient; - return loading.show(), apiClient.ajax({ + + loading.show(); + + // Add/Update connect info + return apiClient.ajax({ + type: "POST", - url: apiClient.getUrl("Connect/Invite"), - dataType: "json", + url: apiClient.getUrl('Connect/Invite'), + dataType: 'json', data: options.guestOptions || {} - }).then(function(result) { - return loading.hide(), showNewUserInviteMessage(result) - }, function(response) { + + }).then(function (result) { + loading.hide(); + return showNewUserInviteMessage(result); + + }, function (response) { + + loading.hide(); + var statusCode = response ? response.status : 0; - return 502 === statusCode ? showConnectServerUnreachableErrorMessage().then(rejectPromise, rejectPromise) : 404 === statusCode ? alert({ - text: globalize.translate("sharedcomponents#GuestUserNotFound") - }).then(rejectPromise, rejectPromise) : (statusCode || 0) >= 500 ? alert({ - text: globalize.translate("sharedcomponents#ErrorReachingEmbyConnect") - }).then(rejectPromise, rejectPromise) : showGuestGeneralErrorMessage().then(rejectPromise, rejectPromise) - }) + + if (statusCode === 502) { + return showConnectServerUnreachableErrorMessage().then(rejectPromise, rejectPromise); + } + else if (statusCode === 404) { + // User doesn't exist + return alert({ + text: globalize.translate('sharedcomponents#GuestUserNotFound') + }).then(rejectPromise, rejectPromise); + + } else if ((statusCode || 0) >= 500) { + + // Unable to reach connect server ? + return alert({ + text: globalize.translate('sharedcomponents#ErrorReachingEmbyConnect') + }).then(rejectPromise, rejectPromise); + + } else { + + // status 400 = account not activated + + // General error + return showGuestGeneralErrorMessage().then(rejectPromise, rejectPromise); + } + }); } function showGuestGeneralErrorMessage() { + var html; - appHost.supports("externallinks") && (html = globalize.translate("sharedcomponents#ErrorAddingGuestAccount1", 'https://github.com/jellyfin/jellyfin'), html += "

" + globalize.translate("sharedcomponents#ErrorAddingGuestAccount2", "apps@emby.media")); - var text = globalize.translate("sharedcomponents#ErrorAddingGuestAccount1", "https://github.com/jellyfin/jellyfin"); - return text += "\n\n" + globalize.translate("sharedcomponents#ErrorAddingGuestAccount2", "apps@emby.media"), alert({ + + if (appHost.supports('externallinks')) { + html = globalize.translate('sharedcomponents#ErrorAddingGuestAccount1', 'https://emby.media/connect'); + html += '

' + globalize.translate('sharedcomponents#ErrorAddingGuestAccount2', 'apps@emby.media'); + } + + var text = globalize.translate('sharedcomponents#ErrorAddingGuestAccount1', 'https://emby.media/connect'); + text += '\n\n' + globalize.translate('sharedcomponents#ErrorAddingGuestAccount2', 'apps@emby.media'); + + return alert({ text: text, html: html - }) + }); } function showConnectServerUnreachableErrorMessage() { - var text = globalize.translate("sharedcomponents#ErrorConnectServerUnreachable", "https://connect.emby.media"); + + var text = globalize.translate('sharedcomponents#ErrorConnectServerUnreachable', 'https://connect.emby.media'); + return alert({ text: text - }) + }); } function showLinkUserErrorMessage(username, statusCode) { - var html, text; - return 502 === statusCode ? showConnectServerUnreachableErrorMessage() : (username ? (appHost.supports("externallinks") && (html = globalize.translate("sharedcomponents#ErrorAddingEmbyConnectAccount1", 'https://github.com/jellyfin/jellyfin'), html += "

" + globalize.translate("sharedcomponents#ErrorAddingEmbyConnectAccount2", "apps@emby.media")), text = globalize.translate("sharedcomponents#ErrorAddingEmbyConnectAccount1", "https://github.com/jellyfin/jellyfin"), text += "\n\n" + globalize.translate("sharedcomponents#ErrorAddingEmbyConnectAccount2", "apps@emby.media")) : html = text = globalize.translate("sharedcomponents#DefaultErrorMessage"), alert({ + + var html; + var text; + + if (statusCode === 502) { + return showConnectServerUnreachableErrorMessage(); + } + else if (username) { + + if (appHost.supports('externallinks')) { + html = globalize.translate('sharedcomponents#ErrorAddingEmbyConnectAccount1', 'https://emby.media/connect'); + html += '

' + globalize.translate('sharedcomponents#ErrorAddingEmbyConnectAccount2', 'apps@emby.media'); + } + + text = globalize.translate('sharedcomponents#ErrorAddingEmbyConnectAccount1', 'https://emby.media/connect'); + text += '\n\n' + globalize.translate('sharedcomponents#ErrorAddingEmbyConnectAccount2', 'apps@emby.media'); + + } else { + html = text = globalize.translate('sharedcomponents#DefaultErrorMessage'); + } + + return alert({ text: text, html: html - })) + }); } function updateUserLink(apiClient, user, newConnectUsername) { - var currentConnectUsername = user.ConnectUserName || "", - enteredConnectUsername = newConnectUsername, - linkUrl = apiClient.getUrl("Users/" + user.Id + "/Connect/Link"); - return currentConnectUsername && !enteredConnectUsername ? apiClient.ajax({ - type: "DELETE", - url: linkUrl - }).then(function() { - return alert({ - text: globalize.translate("sharedcomponents#MessageEmbyAccontRemoved"), - title: globalize.translate("sharedcomponents#HeaderEmbyAccountRemoved") - }).catch(resolvePromise) - }, function(response) { - return 502 === (response ? response.status : 0) ? showConnectServerUnreachableErrorMessage().then(rejectPromise) : alert({ - text: globalize.translate("sharedcomponents#ErrorRemovingEmbyConnectAccount") - }).then(rejectPromise) - }) : currentConnectUsername !== enteredConnectUsername ? apiClient.ajax({ - type: "POST", - url: linkUrl, - data: { - ConnectUsername: enteredConnectUsername - }, - dataType: "json" - }).then(function(result) { - var msgKey = result.IsPending ? "sharedcomponents#MessagePendingEmbyAccountAdded" : "sharedcomponents#MessageEmbyAccountAdded"; - return alert({ - text: globalize.translate(msgKey), - title: globalize.translate("sharedcomponents#HeaderEmbyAccountAdded") - }).catch(resolvePromise) - }, function(response) { - var statusCode = response ? response.status : 0; - return 502 === statusCode ? showConnectServerUnreachableErrorMessage().then(rejectPromise) : showLinkUserErrorMessage(".", statusCode).then(rejectPromise) - }) : Promise.reject() + var currentConnectUsername = user.ConnectUserName || ''; + var enteredConnectUsername = newConnectUsername; + + var linkUrl = apiClient.getUrl('Users/' + user.Id + '/Connect/Link'); + + if (currentConnectUsername && !enteredConnectUsername) { + + // Remove connect info + // Add/Update connect info + return apiClient.ajax({ + + type: "DELETE", + url: linkUrl + + }).then(function () { + + return alert({ + text: globalize.translate('sharedcomponents#MessageEmbyAccontRemoved'), + title: globalize.translate('sharedcomponents#HeaderEmbyAccountRemoved'), + + }).catch(resolvePromise); + + }, function (response) { + + var statusCode = response ? response.status : 0; + + if (statusCode === 502) { + return showConnectServerUnreachableErrorMessage().then(rejectPromise); + } + + return alert({ + text: globalize.translate('sharedcomponents#ErrorRemovingEmbyConnectAccount') + + }).then(rejectPromise); + }); + + } + else if (currentConnectUsername !== enteredConnectUsername) { + + // Add/Update connect info + return apiClient.ajax({ + type: "POST", + url: linkUrl, + data: { + ConnectUsername: enteredConnectUsername + }, + dataType: 'json' + + }).then(function (result) { + + var msgKey = result.IsPending ? 'sharedcomponents#MessagePendingEmbyAccountAdded' : 'sharedcomponents#MessageEmbyAccountAdded'; + + return alert({ + text: globalize.translate(msgKey), + title: globalize.translate('sharedcomponents#HeaderEmbyAccountAdded'), + + }).catch(resolvePromise); + + }, function (response) { + + var statusCode = response ? response.status : 0; + + if (statusCode === 502) { + return showConnectServerUnreachableErrorMessage().then(rejectPromise); + } + + return showLinkUserErrorMessage('.', statusCode).then(rejectPromise); + }); + + } else { + return Promise.reject(); + } } + return { inviteGuest: inviteGuest, updateUserLink: updateUserLink, showLinkUserErrorMessage: showLinkUserErrorMessage, showConnectServerUnreachableErrorMessage: showConnectServerUnreachableErrorMessage - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/emby-input/emby-input.css b/src/bower_components/emby-webcomponents/emby-input/emby-input.css index 31041f0a63..d888eb570d 100644 --- a/src/bower_components/emby-webcomponents/emby-input/emby-input.css +++ b/src/bower_components/emby-webcomponents/emby-input/emby-input.css @@ -2,35 +2,39 @@ display: block; margin: 0; margin-bottom: 0 !important; + /* Remove select styling */ + /* Font size must the 16px or larger to prevent iOS page zoom on focus */ font-size: 110%; + /* General select styles: change as needed */ font-family: inherit; font-weight: inherit; padding: .4em .25em; + /* Prevent padding from causing width overflow */ -webkit-box-sizing: border-box; box-sizing: border-box; - outline: 0 !important; - -webkit-tap-highlight-color: transparent; - width: 100% + outline: none !important; + -webkit-tap-highlight-color: rgba(0,0,0,0); + width: 100%; } -.emby-input::-moz-focus-inner { - border: 0 -} + .emby-input::-moz-focus-inner { + border: 0; + } .inputContainer { - margin-bottom: 1.8em + margin-bottom: 1.8em; } .inputLabel { display: inline-block; - margin-bottom: .25em + margin-bottom: .25em; } -.emby-input+.fieldDescription { - margin-top: .25em +.emby-input + .fieldDescription { + margin-top: .25em; } .emby-input-iconbutton { -webkit-align-self: flex-end; - align-self: flex-end -} \ No newline at end of file + align-self: flex-end; +} diff --git a/src/bower_components/emby-webcomponents/emby-input/emby-input.js b/src/bower_components/emby-webcomponents/emby-input/emby-input.js index e6d6c0aeb7..9481b89225 100644 --- a/src/bower_components/emby-webcomponents/emby-input/emby-input.js +++ b/src/bower_components/emby-webcomponents/emby-input/emby-input.js @@ -1,56 +1,125 @@ -define(["layoutManager", "browser", "dom", "css!./emby-input", "registerElement"], function(layoutManager, browser, dom) { - "use strict"; +define(['layoutManager', 'browser', 'dom', 'css!./emby-input', 'registerElement'], function (layoutManager, browser, dom) { + 'use strict'; + + var EmbyInputPrototype = Object.create(HTMLInputElement.prototype); + + var inputId = 0; + var supportsFloatingLabel = false; - function onChange() { - var label = this.labelElement; - if (this.value) label.classList.remove("inputLabel-float"); - else { - supportsFloatingLabel && "date" !== this.type && "time" !== this.type && label.classList.add("inputLabel-float") - } - } - var EmbyInputPrototype = Object.create(HTMLInputElement.prototype), - inputId = 0, - supportsFloatingLabel = !1; if (Object.getOwnPropertyDescriptor && Object.defineProperty) { - var descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value"); + + var descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value'); + + // descriptor returning null in webos if (descriptor && descriptor.configurable) { var baseSetMethod = descriptor.set; - descriptor.set = function(value) { - baseSetMethod.call(this, value), this.dispatchEvent(new CustomEvent("valueset", { - bubbles: !1, - cancelable: !1 - })) - }, Object.defineProperty(HTMLInputElement.prototype, "value", descriptor), supportsFloatingLabel = !0 + descriptor.set = function (value) { + baseSetMethod.call(this, value); + + this.dispatchEvent(new CustomEvent('valueset', { + bubbles: false, + cancelable: false + })); + }; + + Object.defineProperty(HTMLInputElement.prototype, 'value', descriptor); + supportsFloatingLabel = true; } } - EmbyInputPrototype.createdCallback = function() { - if (this.id || (this.id = "embyinput" + inputId, inputId++), !this.classList.contains("emby-input")) { - this.classList.add("emby-input"); - var parentNode = this.parentNode, - document = this.ownerDocument, - label = document.createElement("label"); - label.innerHTML = this.getAttribute("label") || "", label.classList.add("inputLabel"), label.classList.add("inputLabelUnfocused"), label.htmlFor = this.id, parentNode.insertBefore(label, this), this.labelElement = label, dom.addEventListener(this, "focus", function() { - onChange.call(this), document.attachIME && document.attachIME(this), label.classList.add("inputLabelFocused"), label.classList.remove("inputLabelUnfocused") - }, { - passive: !0 - }), dom.addEventListener(this, "blur", function() { - onChange.call(this), label.classList.remove("inputLabelFocused"), label.classList.add("inputLabelUnfocused") - }, { - passive: !0 - }), dom.addEventListener(this, "change", onChange, { - passive: !0 - }), dom.addEventListener(this, "input", onChange, { - passive: !0 - }), dom.addEventListener(this, "valueset", onChange, { - passive: !0 - }), browser.orsay && this === document.activeElement && document.attachIME && document.attachIME(this) + + EmbyInputPrototype.createdCallback = function () { + + if (!this.id) { + this.id = 'embyinput' + inputId; + inputId++; + } if (this.classList.contains('emby-input')) { + return; } - }, EmbyInputPrototype.attachedCallback = function() { - this.labelElement.htmlFor = this.id, onChange.call(this) - }, EmbyInputPrototype.label = function(text) { - this.labelElement.innerHTML = text - }, document.registerElement("emby-input", { + + this.classList.add('emby-input'); + + var parentNode = this.parentNode; + var document = this.ownerDocument; + var label = document.createElement('label'); + label.innerHTML = this.getAttribute('label') || ''; + label.classList.add('inputLabel'); + label.classList.add('inputLabelUnfocused'); + + label.htmlFor = this.id; + parentNode.insertBefore(label, this); + this.labelElement = label; + + dom.addEventListener(this, 'focus', function () { + onChange.call(this); + + // For Samsung orsay devices + if (document.attachIME) { + document.attachIME(this); + } + + label.classList.add('inputLabelFocused'); + label.classList.remove('inputLabelUnfocused'); + }, { + passive: true + }); + + dom.addEventListener(this, 'blur', function () { + onChange.call(this); + label.classList.remove('inputLabelFocused'); + label.classList.add('inputLabelUnfocused'); + }, { + passive: true + }); + + dom.addEventListener(this, 'change', onChange, { + passive: true + }); + dom.addEventListener(this, 'input', onChange, { + passive: true + }); + dom.addEventListener(this, 'valueset', onChange, { + passive: true + }); + + if (browser.orsay) { + if (this === document.activeElement) { + //Make sure the IME pops up if this is the first/default element on the page + if (document.attachIME) { + document.attachIME(this); + } + } + } + + }; + + function onChange() { + + var label = this.labelElement; + if (this.value) { + label.classList.remove('inputLabel-float'); + } else { + + var instanceSupportsFloat = supportsFloatingLabel && this.type !== 'date' && this.type !== 'time'; + + if (instanceSupportsFloat) { + label.classList.add('inputLabel-float'); + } + } + } + + EmbyInputPrototype.attachedCallback = function () { + + this.labelElement.htmlFor = this.id; + + onChange.call(this); + }; + + EmbyInputPrototype.label = function (text) { + this.labelElement.innerHTML = text; + }; + + document.registerElement('emby-input', { prototype: EmbyInputPrototype, - extends: "input" - }) + extends: 'input' + }); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/emby-itemrefreshindicator/emby-itemrefreshindicator.js b/src/bower_components/emby-webcomponents/emby-itemrefreshindicator/emby-itemrefreshindicator.js index a575c8ec27..502af23542 100644 --- a/src/bower_components/emby-webcomponents/emby-itemrefreshindicator/emby-itemrefreshindicator.js +++ b/src/bower_components/emby-webcomponents/emby-itemrefreshindicator/emby-itemrefreshindicator.js @@ -1,32 +1,77 @@ -define(["emby-progressring", "dom", "serverNotifications", "events", "registerElement"], function(EmbyProgressRing, dom, serverNotifications, events) { - "use strict"; +define(['emby-progressring', 'dom', 'serverNotifications', 'events', 'registerElement'], function (EmbyProgressRing, dom, serverNotifications, events) { + 'use strict'; function addNotificationEvent(instance, name, handler) { + var localHandler = handler.bind(instance); - events.on(serverNotifications, name, localHandler), instance[name] = localHandler + events.on(serverNotifications, name, localHandler); + instance[name] = localHandler; } function removeNotificationEvent(instance, name) { + var handler = instance[name]; - handler && (events.off(serverNotifications, name, handler), instance[name] = null) + if (handler) { + events.off(serverNotifications, name, handler); + instance[name] = null; + } } function onRefreshProgress(e, apiClient, info) { + var indicator = this; - if (indicator.itemId || (indicator.itemId = dom.parentWithAttribute(indicator, "data-id").getAttribute("data-id")), info.ItemId === indicator.itemId) { + + if (!indicator.itemId) { + indicator.itemId = dom.parentWithAttribute(indicator, 'data-id').getAttribute('data-id'); + } + + if (info.ItemId === indicator.itemId) { + var progress = parseFloat(info.Progress); - progress && progress < 100 ? this.classList.remove("hide") : this.classList.add("hide"), this.setProgress(progress) + + if (progress && progress < 100) { + this.classList.remove('hide'); + } else { + this.classList.add('hide'); + } + + this.setProgress(progress); } } + var EmbyItemRefreshIndicatorPrototype = Object.create(EmbyProgressRing); - EmbyItemRefreshIndicatorPrototype.createdCallback = function() { - EmbyProgressRing.createdCallback && EmbyProgressRing.createdCallback.call(this), addNotificationEvent(this, "RefreshProgress", onRefreshProgress) - }, EmbyItemRefreshIndicatorPrototype.attachedCallback = function() { - EmbyProgressRing.attachedCallback && EmbyProgressRing.attachedCallback.call(this) - }, EmbyItemRefreshIndicatorPrototype.detachedCallback = function() { - EmbyProgressRing.detachedCallback && EmbyProgressRing.detachedCallback.call(this), removeNotificationEvent(this, "RefreshProgress"), this.itemId = null - }, document.registerElement("emby-itemrefreshindicator", { + + EmbyItemRefreshIndicatorPrototype.createdCallback = function () { + + // base method + if (EmbyProgressRing.createdCallback) { + EmbyProgressRing.createdCallback.call(this); + } + + addNotificationEvent(this, 'RefreshProgress', onRefreshProgress); + }; + + EmbyItemRefreshIndicatorPrototype.attachedCallback = function () { + + // base method + if (EmbyProgressRing.attachedCallback) { + EmbyProgressRing.attachedCallback.call(this); + } + }; + + EmbyItemRefreshIndicatorPrototype.detachedCallback = function () { + + // base method + if (EmbyProgressRing.detachedCallback) { + EmbyProgressRing.detachedCallback.call(this); + } + + removeNotificationEvent(this, 'RefreshProgress'); + this.itemId = null; + }; + + document.registerElement('emby-itemrefreshindicator', { prototype: EmbyItemRefreshIndicatorPrototype, - extends: "div" - }) + extends: 'div' + }); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/emby-itemscontainer/emby-itemscontainer.js b/src/bower_components/emby-webcomponents/emby-itemscontainer/emby-itemscontainer.js index b33c3f4509..cd2cab827b 100644 --- a/src/bower_components/emby-webcomponents/emby-itemscontainer/emby-itemscontainer.js +++ b/src/bower_components/emby-webcomponents/emby-itemscontainer/emby-itemscontainer.js @@ -1,220 +1,548 @@ -define(["itemShortcuts", "inputManager", "connectionManager", "playbackManager", "imageLoader", "layoutManager", "browser", "dom", "loading", "focusManager", "serverNotifications", "events", "registerElement"], function(itemShortcuts, inputManager, connectionManager, playbackManager, imageLoader, layoutManager, browser, dom, loading, focusManager, serverNotifications, events) { - "use strict"; +define(['itemShortcuts', 'inputManager', 'connectionManager', 'playbackManager', 'imageLoader', 'layoutManager', 'browser', 'dom', 'loading', 'focusManager', 'serverNotifications', 'events', 'registerElement'], function (itemShortcuts, inputManager, connectionManager, playbackManager, imageLoader, layoutManager, browser, dom, loading, focusManager, serverNotifications, events) { + 'use strict'; + + var ItemsContainerProtoType = Object.create(HTMLDivElement.prototype); function onClick(e) { - var itemsContainer = this, - multiSelect = (e.target, itemsContainer.multiSelect); - multiSelect && !1 === multiSelect.onContainerClick.call(itemsContainer, e) || itemShortcuts.onClick.call(itemsContainer, e) + + var itemsContainer = this; + var target = e.target; + + var multiSelect = itemsContainer.multiSelect; + + if (multiSelect) { + if (multiSelect.onContainerClick.call(itemsContainer, e) === false) { + return; + } + } + + itemShortcuts.onClick.call(itemsContainer, e); } function disableEvent(e) { - return e.preventDefault(), e.stopPropagation(), !1 + + e.preventDefault(); + e.stopPropagation(); + return false; } function onContextMenu(e) { - var target = e.target, - card = dom.parentWithAttribute(target, "data-id"); - if (card && card.getAttribute("data-serverid")) return inputManager.trigger("menu", { - sourceElement: card - }), e.preventDefault(), e.stopPropagation(), !1 + + var itemsContainer = this; + + var target = e.target; + var card = dom.parentWithAttribute(target, 'data-id'); + + // check for serverId, it won't be present on selectserver + if (card && card.getAttribute('data-serverid')) { + + inputManager.trigger('menu', { + sourceElement: card + }); + + e.preventDefault(); + e.stopPropagation(); + return false; + } } function getShortcutOptions() { return { - click: !1 - } + click: false + }; } + ItemsContainerProtoType.enableMultiSelect = function (enabled) { + + var current = this.multiSelect; + + if (!enabled) { + if (current) { + current.destroy(); + this.multiSelect = null; + } + return; + } + + if (current) { + return; + } + + var self = this; + require(['multiSelect'], function (MultiSelect) { + self.multiSelect = new MultiSelect({ + container: self, + bindOnClick: false + }); + }); + }; + function onDrop(evt, itemsContainer) { - var el = evt.item, - newIndex = evt.newIndex, - itemId = el.getAttribute("data-playlistitemid"), - playlistId = el.getAttribute("data-playlistid"); + + var el = evt.item; + + var newIndex = evt.newIndex; + var itemId = el.getAttribute('data-playlistitemid'); + var playlistId = el.getAttribute('data-playlistid'); + if (!playlistId) { + var oldIndex = evt.oldIndex; - return void el.dispatchEvent(new CustomEvent("itemdrop", { + + el.dispatchEvent(new CustomEvent('itemdrop', { detail: { oldIndex: oldIndex, newIndex: newIndex, playlistItemId: itemId }, - bubbles: !0, - cancelable: !1 - })) + bubbles: true, + cancelable: false + })); + return; } - var serverId = el.getAttribute("data-serverid"), - apiClient = connectionManager.getApiClient(serverId); - loading.show(), apiClient.ajax({ - url: apiClient.getUrl("Playlists/" + playlistId + "/Items/" + itemId + "/Move/" + newIndex), - type: "POST" - }).then(function() { - loading.hide() - }, function() { - loading.hide(), itemsContainer.refreshItems() - }) + + var serverId = el.getAttribute('data-serverid'); + var apiClient = connectionManager.getApiClient(serverId); + + loading.show(); + + apiClient.ajax({ + + url: apiClient.getUrl('Playlists/' + playlistId + '/Items/' + itemId + '/Move/' + newIndex), + + type: 'POST' + + }).then(function () { + + loading.hide(); + + }, function () { + + loading.hide(); + + itemsContainer.refreshItems(); + }); } - function onUserDataChanged(e, apiClient, userData) { - var itemsContainer = this; - require(["cardBuilder"], function(cardBuilder) { - cardBuilder.onUserDataChanged(userData, itemsContainer) + ItemsContainerProtoType.enableDragReordering = function (enabled) { + + var current = this.sortable; + + if (!enabled) { + if (current) { + current.destroy(); + this.sortable = null; + } + return; + } + + if (current) { + return; + } + + var self = this; + require(['sortable'], function (Sortable) { + + self.sortable = new Sortable(self, { + + draggable: ".listItem", + handle: '.listViewDragHandle', + + // dragging ended + onEnd: function (/**Event*/evt) { + + return onDrop(evt, self); + } + }); }); - var eventsToMonitor = getEventsToMonitor(itemsContainer); - 1 !== eventsToMonitor.indexOf("markfavorite") ? itemsContainer.notifyRefreshNeeded() : -1 !== eventsToMonitor.indexOf("markplayed") && itemsContainer.notifyRefreshNeeded() + }; + + function onUserDataChanged(e, apiClient, userData) { + + var itemsContainer = this; + + require(['cardBuilder'], function (cardBuilder) { + cardBuilder.onUserDataChanged(userData, itemsContainer); + }); + + var eventsToMonitor = getEventsToMonitor(itemsContainer); + + // TODO: Check user data change reason? + if (eventsToMonitor.indexOf('markfavorite') !== -1) { + + itemsContainer.notifyRefreshNeeded(); + } + else if (eventsToMonitor.indexOf('markplayed') !== -1) { + + itemsContainer.notifyRefreshNeeded(); + } } function getEventsToMonitor(itemsContainer) { - var monitor = itemsContainer.getAttribute("data-monitor"); - return monitor ? monitor.split(",") : [] + + var monitor = itemsContainer.getAttribute('data-monitor'); + if (monitor) { + return monitor.split(','); + } + + return []; } function onTimerCreated(e, apiClient, data) { + var itemsContainer = this; - if (-1 !== getEventsToMonitor(itemsContainer).indexOf("timers")) return void itemsContainer.notifyRefreshNeeded(); - var programId = data.ProgramId, - newTimerId = data.Id; - require(["cardBuilder"], function(cardBuilder) { - cardBuilder.onTimerCreated(programId, newTimerId, itemsContainer) - }) + + if (getEventsToMonitor(itemsContainer).indexOf('timers') !== -1) { + + itemsContainer.notifyRefreshNeeded(); + return; + } + + var programId = data.ProgramId; + // This could be null, not supported by all tv providers + var newTimerId = data.Id; + + require(['cardBuilder'], function (cardBuilder) { + cardBuilder.onTimerCreated(programId, newTimerId, itemsContainer); + }); } function onSeriesTimerCreated(e, apiClient, data) { + var itemsContainer = this; - if (-1 !== getEventsToMonitor(itemsContainer).indexOf("seriestimers")) return void itemsContainer.notifyRefreshNeeded() + if (getEventsToMonitor(itemsContainer).indexOf('seriestimers') !== -1) { + + itemsContainer.notifyRefreshNeeded(); + return; + } } function onTimerCancelled(e, apiClient, data) { var itemsContainer = this; - if (-1 !== getEventsToMonitor(itemsContainer).indexOf("timers")) return void itemsContainer.notifyRefreshNeeded(); + + if (getEventsToMonitor(itemsContainer).indexOf('timers') !== -1) { + + itemsContainer.notifyRefreshNeeded(); + return; + } + var id = data.Id; - require(["cardBuilder"], function(cardBuilder) { - cardBuilder.onTimerCancelled(id, itemsContainer) - }) + + require(['cardBuilder'], function (cardBuilder) { + cardBuilder.onTimerCancelled(id, itemsContainer); + }); } function onSeriesTimerCancelled(e, apiClient, data) { + var itemsContainer = this; - if (-1 !== getEventsToMonitor(itemsContainer).indexOf("seriestimers")) return void itemsContainer.notifyRefreshNeeded(); + if (getEventsToMonitor(itemsContainer).indexOf('seriestimers') !== -1) { + + itemsContainer.notifyRefreshNeeded(); + return; + } + var id = data.Id; - require(["cardBuilder"], function(cardBuilder) { - cardBuilder.onSeriesTimerCancelled(id, itemsContainer) - }) + + require(['cardBuilder'], function (cardBuilder) { + cardBuilder.onSeriesTimerCancelled(id, itemsContainer); + }); } function onLibraryChanged(e, apiClient, data) { - var itemsContainer = this, - eventsToMonitor = getEventsToMonitor(itemsContainer); - if (-1 === eventsToMonitor.indexOf("seriestimers") && -1 === eventsToMonitor.indexOf("timers")) { - var itemsAdded = data.ItemsAdded || [], - itemsRemoved = data.ItemsRemoved || []; - if (itemsAdded.length || itemsRemoved.length) { - var parentId = itemsContainer.getAttribute("data-parentid"); - if (parentId) { - var foldersAddedTo = data.FoldersAddedTo || [], - foldersRemovedFrom = data.FoldersRemovedFrom || [], - collectionFolders = data.CollectionFolders || []; - if (-1 === foldersAddedTo.indexOf(parentId) && -1 === foldersRemovedFrom.indexOf(parentId) && -1 === collectionFolders.indexOf(parentId)) return - } - itemsContainer.notifyRefreshNeeded() + + var itemsContainer = this; + var eventsToMonitor = getEventsToMonitor(itemsContainer); + if (eventsToMonitor.indexOf('seriestimers') !== -1 || eventsToMonitor.indexOf('timers') !== -1) { + + // yes this is an assumption + return; + } + + var itemsAdded = data.ItemsAdded || []; + var itemsRemoved = data.ItemsRemoved || []; + if (!itemsAdded.length && !itemsRemoved.length) { + return; + } + + var parentId = itemsContainer.getAttribute('data-parentid'); + if (parentId) { + var foldersAddedTo = data.FoldersAddedTo || []; + var foldersRemovedFrom = data.FoldersRemovedFrom || []; + var collectionFolders = data.CollectionFolders || []; + + if (foldersAddedTo.indexOf(parentId) === -1 && foldersRemovedFrom.indexOf(parentId) === -1 && collectionFolders.indexOf(parentId) === -1) { + return; + } + } + + itemsContainer.notifyRefreshNeeded(); + } + + function onPlaybackStopped(e, stopInfo) { + + var itemsContainer = this; + + var state = stopInfo.state; + + var eventsToMonitor = getEventsToMonitor(itemsContainer); + if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Video') { + + if (eventsToMonitor.indexOf('videoplayback') !== -1) { + + itemsContainer.notifyRefreshNeeded(true); + return; + } + } + + else if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Audio') { + + if (eventsToMonitor.indexOf('audioplayback') !== -1) { + + itemsContainer.notifyRefreshNeeded(true); + return; } } } - function onPlaybackStopped(e, stopInfo) { - var itemsContainer = this, - state = stopInfo.state, - eventsToMonitor = getEventsToMonitor(itemsContainer); - if (state.NowPlayingItem && "Video" === state.NowPlayingItem.MediaType) { - if (-1 !== eventsToMonitor.indexOf("videoplayback")) return void itemsContainer.notifyRefreshNeeded(!0) - } else if (state.NowPlayingItem && "Audio" === state.NowPlayingItem.MediaType && -1 !== eventsToMonitor.indexOf("audioplayback")) return void itemsContainer.notifyRefreshNeeded(!0) - } - function addNotificationEvent(instance, name, handler, owner) { + var localHandler = handler.bind(instance); - owner = owner || serverNotifications, events.on(owner, name, localHandler), instance["event_" + name] = localHandler + owner = owner || serverNotifications; + events.on(owner, name, localHandler); + instance['event_' + name] = localHandler; } function removeNotificationEvent(instance, name, owner) { - var handler = instance["event_" + name]; - handler && (owner = owner || serverNotifications, events.off(owner, name, handler), instance["event_" + name] = null) + + var handler = instance['event_' + name]; + if (handler) { + owner = owner || serverNotifications; + events.off(owner, name, handler); + instance['event_' + name] = null; + } } + ItemsContainerProtoType.createdCallback = function () { + + this.classList.add('itemsContainer'); + }; + + ItemsContainerProtoType.attachedCallback = function () { + + this.addEventListener('click', onClick); + + if (browser.touch) { + this.addEventListener('contextmenu', disableEvent); + } else { + if (this.getAttribute('data-contextmenu') !== 'false') { + this.addEventListener('contextmenu', onContextMenu); + } + } + + if (layoutManager.desktop || layoutManager.mobile) { + if (this.getAttribute('data-multiselect') !== 'false') { + this.enableMultiSelect(true); + } + } + + if (layoutManager.tv) { + this.classList.add('itemsContainer-tv'); + } + + itemShortcuts.on(this, getShortcutOptions()); + + addNotificationEvent(this, 'UserDataChanged', onUserDataChanged); + addNotificationEvent(this, 'TimerCreated', onTimerCreated); + addNotificationEvent(this, 'SeriesTimerCreated', onSeriesTimerCreated); + addNotificationEvent(this, 'TimerCancelled', onTimerCancelled); + addNotificationEvent(this, 'SeriesTimerCancelled', onSeriesTimerCancelled); + addNotificationEvent(this, 'LibraryChanged', onLibraryChanged); + addNotificationEvent(this, 'playbackstop', onPlaybackStopped, playbackManager); + + if (this.getAttribute('data-dragreorder') === 'true') { + this.enableDragReordering(true); + } + }; + + ItemsContainerProtoType.detachedCallback = function () { + + clearRefreshInterval(this); + + this.enableMultiSelect(false); + this.enableDragReordering(false); + this.removeEventListener('click', onClick); + this.removeEventListener('contextmenu', onContextMenu); + this.removeEventListener('contextmenu', disableEvent); + itemShortcuts.off(this, getShortcutOptions()); + + removeNotificationEvent(this, 'UserDataChanged'); + removeNotificationEvent(this, 'TimerCreated'); + removeNotificationEvent(this, 'SeriesTimerCreated'); + removeNotificationEvent(this, 'TimerCancelled'); + removeNotificationEvent(this, 'SeriesTimerCancelled'); + removeNotificationEvent(this, 'LibraryChanged'); + removeNotificationEvent(this, 'playbackstop', playbackManager); + + this.fetchData = null; + this.getItemsHtml = null; + this.parentContainer = null; + }; + + ItemsContainerProtoType.pause = function () { + + clearRefreshInterval(this, true); + + this.paused = true; + }; + + ItemsContainerProtoType.resume = function (options) { + + this.paused = false; + + var refreshIntervalEndTime = this.refreshIntervalEndTime; + if (refreshIntervalEndTime) { + + var remainingMs = refreshIntervalEndTime - new Date().getTime(); + if (remainingMs > 0 && !this.needsRefresh) { + + resetRefreshInterval(this, remainingMs); + + } else { + this.needsRefresh = true; + this.refreshIntervalEndTime = null; + } + } + + if (this.needsRefresh || (options && options.refresh)) { + return this.refreshItems(); + } + + return Promise.resolve(); + }; + + ItemsContainerProtoType.refreshItems = function () { + + if (!this.fetchData) { + return Promise.resolve(); + } + + if (this.paused) { + this.needsRefresh = true; + return Promise.resolve(); + } + + this.needsRefresh = false; + + return this.fetchData().then(onDataFetched.bind(this)); + }; + + ItemsContainerProtoType.notifyRefreshNeeded = function (isInForeground) { + + if (this.paused) { + this.needsRefresh = true; + return; + } + + var timeout = this.refreshTimeout; + if (timeout) { + clearTimeout(timeout); + } + + if (isInForeground === true) { + this.refreshItems(); + } else { + this.refreshTimeout = setTimeout(this.refreshItems.bind(this), 10000); + } + }; + function clearRefreshInterval(itemsContainer, isPausing) { - itemsContainer.refreshInterval && (clearInterval(itemsContainer.refreshInterval), itemsContainer.refreshInterval = null, isPausing || (itemsContainer.refreshIntervalEndTime = null)) + + if (itemsContainer.refreshInterval) { + + clearInterval(itemsContainer.refreshInterval); + itemsContainer.refreshInterval = null; + + if (!isPausing) { + itemsContainer.refreshIntervalEndTime = null; + } + } } function resetRefreshInterval(itemsContainer, intervalMs) { - clearRefreshInterval(itemsContainer), intervalMs || (intervalMs = parseInt(itemsContainer.getAttribute("data-refreshinterval") || "0")), intervalMs && (itemsContainer.refreshInterval = setInterval(itemsContainer.notifyRefreshNeeded.bind(itemsContainer), intervalMs), itemsContainer.refreshIntervalEndTime = (new Date).getTime() + intervalMs) + + clearRefreshInterval(itemsContainer); + + if (!intervalMs) { + intervalMs = parseInt(itemsContainer.getAttribute('data-refreshinterval') || '0'); + } + + if (intervalMs) { + itemsContainer.refreshInterval = setInterval(itemsContainer.notifyRefreshNeeded.bind(itemsContainer), intervalMs); + itemsContainer.refreshIntervalEndTime = new Date().getTime() + intervalMs; + } } function onDataFetched(result) { - var items = result.Items || result, - parentContainer = this.parentContainer; - parentContainer && (items.length ? parentContainer.classList.remove("hide") : parentContainer.classList.add("hide")); - var focusId, hasActiveElement, activeElement = document.activeElement; - this.contains(activeElement) && (hasActiveElement = !0, focusId = activeElement.getAttribute("data-id")), this.innerHTML = this.getItemsHtml(items), imageLoader.lazyChildren(this), hasActiveElement && setFocus(this, focusId), resetRefreshInterval(this), this.afterRefresh && this.afterRefresh(result) + + var items = result.Items || result; + + var parentContainer = this.parentContainer; + if (parentContainer) { + if (items.length) { + parentContainer.classList.remove('hide'); + } else { + parentContainer.classList.add('hide'); + } + } + + // Scroll back up so they can see the results from the beginning + // TODO: Find scroller + //window.scrollTo(0, 0); + + var activeElement = document.activeElement; + var focusId; + var hasActiveElement; + + if (this.contains(activeElement)) { + hasActiveElement = true; + focusId = activeElement.getAttribute('data-id'); + } + + this.innerHTML = this.getItemsHtml(items); + + imageLoader.lazyChildren(this); + + if (hasActiveElement) { + setFocus(this, focusId); + } + + resetRefreshInterval(this); + + if (this.afterRefresh) { + this.afterRefresh(result); + } } function setFocus(itemsContainer, focusId) { if (focusId) { var newElement = itemsContainer.querySelector('[data-id="' + focusId + '"]'); - if (newElement) try { - return void focusManager.focus(newElement) - } catch (err) {} + if (newElement) { + + try { + focusManager.focus(newElement); + return; + } + catch (err) { + } + } } - focusManager.autoFocus(itemsContainer) + + focusManager.autoFocus(itemsContainer); } - var ItemsContainerProtoType = Object.create(HTMLDivElement.prototype); - ItemsContainerProtoType.enableMultiSelect = function(enabled) { - var current = this.multiSelect; - if (!enabled) return void(current && (current.destroy(), this.multiSelect = null)); - if (!current) { - var self = this; - require(["multiSelect"], function(MultiSelect) { - self.multiSelect = new MultiSelect({ - container: self, - bindOnClick: !1 - }) - }) - } - }, ItemsContainerProtoType.enableDragReordering = function(enabled) { - var current = this.sortable; - if (!enabled) return void(current && (current.destroy(), this.sortable = null)); - if (!current) { - var self = this; - require(["sortable"], function(Sortable) { - self.sortable = new Sortable(self, { - draggable: ".listItem", - handle: ".listViewDragHandle", - onEnd: function(evt) { - return onDrop(evt, self) - } - }) - }) - } - }, ItemsContainerProtoType.createdCallback = function() { - this.classList.add("itemsContainer") - }, ItemsContainerProtoType.attachedCallback = function() { - this.addEventListener("click", onClick), browser.touch ? this.addEventListener("contextmenu", disableEvent) : "false" !== this.getAttribute("data-contextmenu") && this.addEventListener("contextmenu", onContextMenu), (layoutManager.desktop || layoutManager.mobile) && "false" !== this.getAttribute("data-multiselect") && this.enableMultiSelect(!0), layoutManager.tv && this.classList.add("itemsContainer-tv"), itemShortcuts.on(this, getShortcutOptions()), addNotificationEvent(this, "UserDataChanged", onUserDataChanged), addNotificationEvent(this, "TimerCreated", onTimerCreated), addNotificationEvent(this, "SeriesTimerCreated", onSeriesTimerCreated), addNotificationEvent(this, "TimerCancelled", onTimerCancelled), addNotificationEvent(this, "SeriesTimerCancelled", onSeriesTimerCancelled), addNotificationEvent(this, "LibraryChanged", onLibraryChanged), addNotificationEvent(this, "playbackstop", onPlaybackStopped, playbackManager), "true" === this.getAttribute("data-dragreorder") && this.enableDragReordering(!0) - }, ItemsContainerProtoType.detachedCallback = function() { - clearRefreshInterval(this), this.enableMultiSelect(!1), this.enableDragReordering(!1), this.removeEventListener("click", onClick), this.removeEventListener("contextmenu", onContextMenu), this.removeEventListener("contextmenu", disableEvent), itemShortcuts.off(this, getShortcutOptions()), removeNotificationEvent(this, "UserDataChanged"), removeNotificationEvent(this, "TimerCreated"), removeNotificationEvent(this, "SeriesTimerCreated"), removeNotificationEvent(this, "TimerCancelled"), removeNotificationEvent(this, "SeriesTimerCancelled"), removeNotificationEvent(this, "LibraryChanged"), removeNotificationEvent(this, "playbackstop", playbackManager), this.fetchData = null, this.getItemsHtml = null, this.parentContainer = null - }, ItemsContainerProtoType.pause = function() { - clearRefreshInterval(this, !0), this.paused = !0 - }, ItemsContainerProtoType.resume = function(options) { - this.paused = !1; - var refreshIntervalEndTime = this.refreshIntervalEndTime; - if (refreshIntervalEndTime) { - var remainingMs = refreshIntervalEndTime - (new Date).getTime(); - remainingMs > 0 && !this.needsRefresh ? resetRefreshInterval(this, remainingMs) : (this.needsRefresh = !0, this.refreshIntervalEndTime = null) - } - return this.needsRefresh || options && options.refresh ? this.refreshItems() : Promise.resolve() - }, ItemsContainerProtoType.refreshItems = function() { - return this.fetchData ? this.paused ? (this.needsRefresh = !0, Promise.resolve()) : (this.needsRefresh = !1, this.fetchData().then(onDataFetched.bind(this))) : Promise.resolve() - }, ItemsContainerProtoType.notifyRefreshNeeded = function(isInForeground) { - if (this.paused) return void(this.needsRefresh = !0); - var timeout = this.refreshTimeout; - timeout && clearTimeout(timeout), !0 === isInForeground ? this.refreshItems() : this.refreshTimeout = setTimeout(this.refreshItems.bind(this), 1e4) - }, document.registerElement("emby-itemscontainer", { + + document.registerElement('emby-itemscontainer', { prototype: ItemsContainerProtoType, - extends: "div" - }) + extends: 'div' + }); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/emby-progressring/emby-progressring.css b/src/bower_components/emby-webcomponents/emby-progressring/emby-progressring.css index 5edd2cfa21..11c05ab518 100644 --- a/src/bower_components/emby-webcomponents/emby-progressring/emby-progressring.css +++ b/src/bower_components/emby-webcomponents/emby-progressring/emby-progressring.css @@ -1,157 +1,105 @@ -.progressring { +.progressring { position: relative; width: 2.6em; height: 2.6em; float: left; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; user-select: none; - -webkit-box-sizing: border-box; - box-sizing: border-box + box-sizing: border-box; } .progressring-bg { width: 100%; height: 100%; - -webkit-border-radius: 50%; border-radius: 50%; border: .25em solid rgba(0, 0, 0, 1); - -webkit-box-sizing: border-box; box-sizing: border-box; background: rgba(0, 0, 0, .9); - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; - justify-content: center -} - -.spiner-holder-one, -.spiner-holder-two { - position: absolute; - top: 0; - left: 0; - overflow: hidden; - background: 0 0; - -webkit-box-sizing: border-box + justify-content: center; } .progressring-text { text-align: center; color: #ddd; - font-size: 90% + font-size: 90%; } .spiner-holder-one { + position: absolute; + top: 0; + left: 0; + overflow: hidden; width: 51%; height: 51%; - box-sizing: border-box + background: transparent; + box-sizing: border-box; } .spiner-holder-two { + position: absolute; + top: 0; + left: 0; + overflow: hidden; width: 100%; height: 100%; - box-sizing: border-box + background: transparent; + box-sizing: border-box; } .progressring-spiner { width: 200%; height: 200%; - -webkit-border-radius: 50%; border-radius: 50%; border-width: .25em; border-style: solid; - -webkit-box-sizing: border-box; - box-sizing: border-box + box-sizing: border-box; } .animate-0-25-a { - -webkit-transform: rotate(90deg); transform: rotate(90deg); - -webkit-transform-origin: 100% 100%; transform-origin: 100% 100%; - -webkit-transition: -webkit-transform 180ms ease-out; - -o-transition: transform 180ms ease-out; - transition: transform 180ms ease-out -} - -.animate-0-25-b, -.animate-25-50-a { - -webkit-transition: -webkit-transform 180ms ease-out; - -o-transition: transform 180ms ease-out + transition: transform 180ms ease-out; } .animate-0-25-b { - -webkit-transform: rotate(-90deg); transform: rotate(-90deg); - -webkit-transform-origin: 100% 100%; transform-origin: 100% 100%; - transition: transform 180ms ease-out + transition: transform 180ms ease-out; } .animate-25-50-a { - -webkit-transform: rotate(180deg); transform: rotate(180deg); - -webkit-transform-origin: 100% 100%; transform-origin: 100% 100%; - transition: transform 180ms ease-out -} - -.animate-25-50-b, -.animate-50-75-a { - -webkit-transition: -webkit-transform 180ms ease-out; - -o-transition: transform 180ms ease-out + transition: transform 180ms ease-out; } .animate-25-50-b { - -webkit-transform: rotate(-90deg); transform: rotate(-90deg); - -webkit-transform-origin: 100% 100%; transform-origin: 100% 100%; - transition: transform 180ms ease-out + transition: transform 180ms ease-out; } .animate-50-75-a { - -webkit-transform: rotate(270deg); transform: rotate(270deg); - -webkit-transform-origin: 100% 100%; transform-origin: 100% 100%; - transition: transform 180ms ease-out -} - -.animate-50-75-b, -.animate-75-100-a { - -webkit-transition: -webkit-transform 180ms ease-out; - -o-transition: transform 180ms ease-out + transition: transform 180ms ease-out; } .animate-50-75-b { - -webkit-transform: rotate(-90deg); transform: rotate(-90deg); - -webkit-transform-origin: 100% 100%; transform-origin: 100% 100%; - transition: transform 180ms ease-out + transition: transform 180ms ease-out; } .animate-75-100-a { - -webkit-transform: rotate(0); - transform: rotate(0); - -webkit-transform-origin: 100% 100%; + transform: rotate(0deg); transform-origin: 100% 100%; - transition: transform 180ms ease-out + transition: transform 180ms ease-out; } .animate-75-100-b { - -webkit-transform: rotate(-90deg); transform: rotate(-90deg); - -webkit-transform-origin: 100% 100%; transform-origin: 100% 100%; - -webkit-transition: -webkit-transform 180ms ease-out; - -o-transition: transform 180ms ease-out; - transition: transform 180ms ease-out -} \ No newline at end of file + transition: transform 180ms ease-out; +} diff --git a/src/bower_components/emby-webcomponents/emby-progressring/emby-progressring.js b/src/bower_components/emby-webcomponents/emby-progressring/emby-progressring.js index c5358f32e2..feb4ddb20d 100644 --- a/src/bower_components/emby-webcomponents/emby-progressring/emby-progressring.js +++ b/src/bower_components/emby-webcomponents/emby-progressring/emby-progressring.js @@ -1,21 +1,105 @@ -define(["require", "css!./emby-progressring", "registerElement"], function(require) { - "use strict"; +define(['require', 'css!./emby-progressring', 'registerElement'], function (require) { + 'use strict'; + var EmbyProgressRing = Object.create(HTMLDivElement.prototype); - return EmbyProgressRing.createdCallback = function() { - this.classList.add("progressring"); + + EmbyProgressRing.createdCallback = function () { + + this.classList.add('progressring'); var instance = this; - require(["text!./emby-progressring.template.html"], function(template) { - instance.innerHTML = template, instance.setProgress(parseFloat(instance.getAttribute("data-progress") || "0")) - }) - }, EmbyProgressRing.setProgress = function(progress) { + + require(['text!./emby-progressring.template.html'], function (template) { + instance.innerHTML = template; + + //if (window.MutationObserver) { + // // create an observer instance + // var observer = new MutationObserver(function (mutations) { + // mutations.forEach(function (mutation) { + + // instance.setProgress(parseFloat(instance.getAttribute('data-progress') || '0')); + // }); + // }); + + // // configuration of the observer: + // var config = { attributes: true, childList: false, characterData: false }; + + // // pass in the target node, as well as the observer options + // observer.observe(instance, config); + + // instance.observer = observer; + //} + + instance.setProgress(parseFloat(instance.getAttribute('data-progress') || '0')); + }); + }; + + EmbyProgressRing.setProgress = function (progress) { + progress = Math.floor(progress); + var angle; - progress < 25 ? (angle = progress / 100 * 360 - 90, this.querySelector(".animate-0-25-b").style.transform = "rotate(" + angle + "deg)", this.querySelector(".animate-25-50-b").style.transform = "rotate(-90deg)", this.querySelector(".animate-50-75-b").style.transform = "rotate(-90deg)", this.querySelector(".animate-75-100-b").style.transform = "rotate(-90deg)") : progress >= 25 && progress < 50 ? (angle = (progress - 25) / 100 * 360 - 90, this.querySelector(".animate-0-25-b").style.transform = "none", this.querySelector(".animate-25-50-b").style.transform = "rotate(" + angle + "deg)", this.querySelector(".animate-50-75-b").style.transform = "rotate(-90deg)", this.querySelector(".animate-75-100-b").style.transform = "rotate(-90deg)") : progress >= 50 && progress < 75 ? (angle = (progress - 50) / 100 * 360 - 90, this.querySelector(".animate-0-25-b").style.transform = "none", this.querySelector(".animate-25-50-b").style.transform = "none", this.querySelector(".animate-50-75-b").style.transform = "rotate(" + angle + "deg)", this.querySelector(".animate-75-100-b").style.transform = "rotate(-90deg)") : progress >= 75 && progress <= 100 && (angle = (progress - 75) / 100 * 360 - 90, this.querySelector(".animate-0-25-b").style.transform = "none", this.querySelector(".animate-25-50-b").style.transform = "none", this.querySelector(".animate-50-75-b").style.transform = "none", this.querySelector(".animate-75-100-b").style.transform = "rotate(" + angle + "deg)"), this.querySelector(".progressring-text").innerHTML = progress + "%" - }, EmbyProgressRing.attachedCallback = function() {}, EmbyProgressRing.detachedCallback = function() { + + if (progress < 25) { + angle = -90 + (progress / 100) * 360; + + this.querySelector('.animate-0-25-b').style.transform = 'rotate(' + angle + 'deg)'; + + this.querySelector('.animate-25-50-b').style.transform = 'rotate(-90deg)'; + this.querySelector('.animate-50-75-b').style.transform = 'rotate(-90deg)'; + this.querySelector('.animate-75-100-b').style.transform = 'rotate(-90deg)'; + } + else if (progress >= 25 && progress < 50) { + + angle = -90 + ((progress - 25) / 100) * 360; + + this.querySelector('.animate-0-25-b').style.transform = 'none'; + this.querySelector('.animate-25-50-b').style.transform = 'rotate(' + angle + 'deg)'; + + this.querySelector('.animate-50-75-b').style.transform = 'rotate(-90deg)'; + this.querySelector('.animate-75-100-b').style.transform = 'rotate(-90deg)'; + } + else if (progress >= 50 && progress < 75) { + angle = -90 + ((progress - 50) / 100) * 360; + + this.querySelector('.animate-0-25-b').style.transform = 'none'; + this.querySelector('.animate-25-50-b').style.transform = 'none'; + this.querySelector('.animate-50-75-b').style.transform = 'rotate(' + angle + 'deg)'; + + this.querySelector('.animate-75-100-b').style.transform = 'rotate(-90deg)'; + } + else if (progress >= 75 && progress <= 100) { + angle = -90 + ((progress - 75) / 100) * 360; + + this.querySelector('.animate-0-25-b').style.transform = 'none'; + this.querySelector('.animate-25-50-b').style.transform = 'none'; + this.querySelector('.animate-50-75-b').style.transform = 'none'; + this.querySelector('.animate-75-100-b').style.transform = 'rotate(' + angle + 'deg)'; + } + + this.querySelector('.progressring-text').innerHTML = progress + '%'; + }; + + EmbyProgressRing.attachedCallback = function () { + + }; + + EmbyProgressRing.detachedCallback = function () { + + var observer = this.observer; - observer && (observer.disconnect(), this.observer = null) - }, document.registerElement("emby-progressring", { + + if (observer) { + // later, you can stop observing + observer.disconnect(); + + this.observer = null; + } + }; + + document.registerElement('emby-progressring', { prototype: EmbyProgressRing, - extends: "div" - }), EmbyProgressRing + extends: 'div' + }); + + return EmbyProgressRing; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/emby-radio/emby-radio.css b/src/bower_components/emby-webcomponents/emby-radio/emby-radio.css index 382919ac1f..25fdc5b933 100644 --- a/src/bower_components/emby-webcomponents/emby-radio/emby-radio.css +++ b/src/bower_components/emby-webcomponents/emby-radio/emby-radio.css @@ -2,26 +2,26 @@ position: relative; line-height: 24px; display: inline-block; - -webkit-box-sizing: border-box; box-sizing: border-box; margin: 0; - padding-left: 24px + padding-left: 0; } .radio-label-block { - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; margin-top: .5em; - margin-bottom: .5em + margin-bottom: .5em; +} + +.mdl-radio { + padding-left: 24px; } .mdl-radio__button { line-height: 24px; position: absolute; + /* 1px is for focusing purposes, so the focusManager doesn't skip over it */ width: 1px; height: 1px; margin: 0; @@ -31,7 +31,7 @@ -moz-appearance: none; -webkit-appearance: none; appearance: none; - border: none + border: none; } .mdl-radio__outer-circle { @@ -39,25 +39,23 @@ top: 4px; left: 0; display: inline-block; - -webkit-box-sizing: border-box; box-sizing: border-box; width: 16px; height: 16px; margin: 0; cursor: pointer; border: 2px solid currentcolor; - -webkit-border-radius: 50%; border-radius: 50%; - z-index: 2 + z-index: 2; } -.mdl-radio__button:checked+.mdl-radio__label+.mdl-radio__outer-circle { - border: 2px solid #00a4dc +.mdl-radio__button:checked + .mdl-radio__label + .mdl-radio__outer-circle { + border: 2px solid #00a4dc; } -.mdl-radio__button:disabled+.mdl-radio__label+.mdl-radio__outer-circle { - border: 2px solid rgba(0, 0, 0, .26); - cursor: auto +.mdl-radio__button:disabled + .mdl-radio__label + .mdl-radio__outer-circle { + border: 2px solid rgba(0,0,0, 0.26); + cursor: auto; } .mdl-radio__inner-circle { @@ -66,52 +64,44 @@ margin: 0; top: 8px; left: 4px; - -webkit-box-sizing: border-box; box-sizing: border-box; width: 8px; height: 8px; cursor: pointer; - -webkit-transition-duration: .28s; - -o-transition-duration: .28s; - transition-duration: .28s; - -webkit-transition-timing-function: cubic-bezier(.4, 0, .2, 1); - -o-transition-timing-function: cubic-bezier(.4, 0, .2, 1); - transition-timing-function: cubic-bezier(.4, 0, .2, 1); - -o-transition-property: transform; - -webkit-transition-property: -webkit-transform, -webkit-transform; + transition-duration: 0.28s; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-property: -webkit-transform; + transition-property: transform; transition-property: transform, -webkit-transform; -webkit-transform: scale3d(0, 0, 0); transform: scale3d(0, 0, 0); - -webkit-border-radius: 50%; border-radius: 50%; - background: #00a4dc + background: #00a4dc; } -.mdl-radio__button:checked+.mdl-radio__label+.mdl-radio__outer-circle+.mdl-radio__inner-circle { +.mdl-radio__button:checked + .mdl-radio__label + .mdl-radio__outer-circle + .mdl-radio__inner-circle { -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1) + transform: scale3d(1, 1, 1); } -.mdl-radio__button:disabled+.mdl-radio__label+.mdl-radio__outer-circle+.mdl-radio__inner-circle { - background: rgba(0, 0, 0, .26); - cursor: auto +.mdl-radio__button:disabled + .mdl-radio__label + .mdl-radio__outer-circle + .mdl-radio__inner-circle { + background: rgba(0,0,0, 0.26); + cursor: auto; } -.mdl-radio__button:focus+.mdl-radio__label+.mdl-radio__outer-circle+.mdl-radio__inner-circle { - -webkit-box-shadow: 0 0 0 10px rgba(255, 255, 255, .76); - box-shadow: 0 0 0 10px rgba(255, 255, 255, .76) +.mdl-radio__button:focus + .mdl-radio__label + .mdl-radio__outer-circle + .mdl-radio__inner-circle { + box-shadow: 0 0 0px 10px rgba(255, 255, 255, 0.76); } -.mdl-radio__button:checked:focus+.mdl-radio__label+.mdl-radio__outer-circle+.mdl-radio__inner-circle { - -webkit-box-shadow: 0 0 0 10px rgba(0,164,220, .26); - box-shadow: 0 0 0 10px rgba(0,164,220, .26) +.mdl-radio__button:checked:focus + .mdl-radio__label + .mdl-radio__outer-circle + .mdl-radio__inner-circle { + box-shadow: 0 0 0 10px rgba(0, 164, 220, 0.26) } .mdl-radio__label { - cursor: pointer + cursor: pointer; } -.mdl-radio__button:disabled+.mdl-radio__label { - color: rgba(0, 0, 0, .26); - cursor: auto -} \ No newline at end of file +.mdl-radio__button:disabled + .mdl-radio__label { + color: rgba(0,0,0, 0.26); + cursor: auto; +} diff --git a/src/bower_components/emby-webcomponents/emby-radio/emby-radio.js b/src/bower_components/emby-webcomponents/emby-radio/emby-radio.js index a41b3ed06d..6e3b6b9bf4 100644 --- a/src/bower_components/emby-webcomponents/emby-radio/emby-radio.js +++ b/src/bower_components/emby-webcomponents/emby-radio/emby-radio.js @@ -1,20 +1,48 @@ -define(["css!./emby-radio", "registerElement"], function() { - "use strict"; +define(['css!./emby-radio', 'registerElement'], function () { + 'use strict'; + + var EmbyRadioPrototype = Object.create(HTMLInputElement.prototype); function onKeyDown(e) { - if (13 === e.keyCode) return e.preventDefault(), this.checked = !0, !1 - } - var EmbyRadioPrototype = Object.create(HTMLInputElement.prototype); - EmbyRadioPrototype.attachedCallback = function() { - if ("true" !== this.getAttribute("data-radio")) { - this.setAttribute("data-radio", "true"), this.classList.add("mdl-radio__button"); - var labelElement = this.parentNode; - labelElement.classList.add("mdl-radio"), labelElement.classList.add("mdl-js-radio"), labelElement.classList.add("mdl-js-ripple-effect"); - var labelTextElement = labelElement.querySelector("span"); - labelTextElement.classList.add("radioButtonLabel"), labelTextElement.classList.add("mdl-radio__label"), labelElement.insertAdjacentHTML("beforeend", ''), this.addEventListener("keydown", onKeyDown) + + // Don't submit form on enter + if (e.keyCode === 13) { + e.preventDefault(); + + this.checked = true; + + return false; } - }, document.registerElement("emby-radio", { + } + + EmbyRadioPrototype.attachedCallback = function () { + + if (this.getAttribute('data-radio') === 'true') { + return; + } + + this.setAttribute('data-radio', 'true'); + + this.classList.add('mdl-radio__button'); + + var labelElement = this.parentNode; + //labelElement.classList.add('"mdl-radio mdl-js-radio mdl-js-ripple-effect'); + labelElement.classList.add('mdl-radio'); + labelElement.classList.add('mdl-js-radio'); + labelElement.classList.add('mdl-js-ripple-effect'); + + var labelTextElement = labelElement.querySelector('span'); + + labelTextElement.classList.add('radioButtonLabel'); + labelTextElement.classList.add('mdl-radio__label'); + + labelElement.insertAdjacentHTML('beforeend', ''); + + this.addEventListener('keydown', onKeyDown); + }; + + document.registerElement('emby-radio', { prototype: EmbyRadioPrototype, - extends: "input" - }) + extends: 'input' + }); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/emby-scrollbuttons/emby-scrollbuttons.css b/src/bower_components/emby-webcomponents/emby-scrollbuttons/emby-scrollbuttons.css index 39408352e0..5e404e780d 100644 --- a/src/bower_components/emby-webcomponents/emby-scrollbuttons/emby-scrollbuttons.css +++ b/src/bower_components/emby-webcomponents/emby-scrollbuttons/emby-scrollbuttons.css @@ -1,67 +1,60 @@ .emby-scrollbuttons-scroller { - position: relative + position: relative; } .scrollbuttoncontainer { position: absolute; top: 0; bottom: 0; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; z-index: 1; font-size: 3em; color: #fff; display: none; - overflow: hidden + overflow: hidden; } .scrollbuttoncontainer-left { background: rgba(20, 20, 20, .5); - background: -webkit-linear-gradient(left, #000 0, rgba(0, 0, 0, 0) 100%); - background: -webkit-gradient(linear, left top, right top, from(#000), to(rgba(0, 0, 0, 0))); - background: -webkit-linear-gradient(left, #000, rgba(0, 0, 0, 0)); - background: -o-linear-gradient(left, #000, rgba(0, 0, 0, 0)); - background: linear-gradient(to right, #000, rgba(0, 0, 0, 0)); - left: 0 + background: -moz-linear-gradient(left,#000 0,rgba(0,0,0,0) 100%); + background: -webkit-linear-gradient(left,#000 0,rgba(0,0,0,0) 100%); + background: linear-gradient(to right,#000,rgba(0,0,0,0)); } .scrollbuttoncontainer-right { background: rgba(20, 20, 20, .5); - background: -webkit-linear-gradient(right, #000 0, rgba(0, 0, 0, 0) 100%); - background: -webkit-gradient(linear, right top, left top, from(#000), to(rgba(0, 0, 0, 0))); - background: -webkit-linear-gradient(right, #000, rgba(0, 0, 0, 0)); - background: -o-linear-gradient(right, #000, rgba(0, 0, 0, 0)); - background: linear-gradient(to left, #000, rgba(0, 0, 0, 0)); - right: 0 + background: -moz-linear-gradient(right,#000 0,rgba(0,0,0,0) 100%); + background: -webkit-linear-gradient(right,#000 0,rgba(0,0,0,0) 100%); + background: linear-gradient(to left,#000,rgba(0,0,0,0)); } .emby-scrollbuttons-scroller:hover .scrollbuttoncontainer { - display: -webkit-box; - display: -webkit-flex; - display: flex + display: flex; +} + +.scrollbuttoncontainer-left { + left: 0; +} + +.scrollbuttoncontainer-right { + right: 0; } .emby-scrollbuttons-scrollbutton { margin: 0 -.2em; - -webkit-transition: -webkit-transform 160ms ease-out; - -o-transition: transform 160ms ease-out; - transition: transform 160ms ease-out + transition: transform 160ms ease-out; } -.scrollbuttoncontainer:hover>.emby-scrollbuttons-scrollbutton { - -webkit-transform: scale(1.3, 1.3); - transform: scale(1.3, 1.3) +.scrollbuttoncontainer:hover > .emby-scrollbuttons-scrollbutton { + transform: scale(1.3, 1.3); } .emby-scrollbuttons-scrollbutton:after { content: ''; - display: none !important + display: none !important; } .emby-scrollbuttons-scrollbutton:focus { - color: inherit !important -} \ No newline at end of file + color: inherit !important; +} diff --git a/src/bower_components/emby-webcomponents/emby-scrollbuttons/emby-scrollbuttons.js b/src/bower_components/emby-webcomponents/emby-scrollbuttons/emby-scrollbuttons.js index fc9c7be825..cd285a8769 100644 --- a/src/bower_components/emby-webcomponents/emby-scrollbuttons/emby-scrollbuttons.js +++ b/src/bower_components/emby-webcomponents/emby-scrollbuttons/emby-scrollbuttons.js @@ -1,75 +1,201 @@ -define(["layoutManager", "dom", "css!./emby-scrollbuttons", "registerElement", "paper-icon-button-light"], function(layoutManager, dom) { - "use strict"; +define(['layoutManager', 'dom', 'css!./emby-scrollbuttons', 'registerElement', 'paper-icon-button-light'], function (layoutManager, dom) { + 'use strict'; + + var EmbyScrollButtonsPrototype = Object.create(HTMLDivElement.prototype); + + EmbyScrollButtonsPrototype.createdCallback = function () { + + }; function getScrollButtonContainerHtml(direction) { - var html = ""; - html += '
'; - var icon = "left" === direction ? "" : ""; - return html += '", html += "
" + + var html = ''; + + var hide = direction === 'left' ? ' hide' : ''; + html += '
'; + + var icon = direction === 'left' ? '' : ''; + + html += ''; + + html += '
'; + + return html; } function getScrollPosition(parent) { - return parent.getScrollPosition ? parent.getScrollPosition() : 0 + + if (parent.getScrollPosition) { + return parent.getScrollPosition(); + } + + return 0; } function getScrollWidth(parent) { - return parent.getScrollSize ? parent.getScrollSize() : 0 + + if (parent.getScrollSize) { + return parent.getScrollSize(); + } + + return 0; } function onScrolledToPosition(scrollButtons, pos, scrollWidth) { - pos > 0 ? scrollButtons.scrollButtonsLeft.classList.remove("hide") : scrollButtons.scrollButtonsLeft.classList.add("hide"), scrollWidth > 0 && (pos += scrollButtons.offsetWidth, pos >= scrollWidth ? scrollButtons.scrollButtonsRight.classList.add("hide") : scrollButtons.scrollButtonsRight.classList.remove("hide")) + + if (pos > 0) { + scrollButtons.scrollButtonsLeft.classList.remove('hide'); + } else { + scrollButtons.scrollButtonsLeft.classList.add('hide'); + } + + if (scrollWidth > 0) { + + pos += scrollButtons.offsetWidth; + + if (pos >= scrollWidth) { + scrollButtons.scrollButtonsRight.classList.add('hide'); + } else { + scrollButtons.scrollButtonsRight.classList.remove('hide'); + } + } } function onScroll(e) { - var scrollButtons = this, - scroller = this.scroller; - onScrolledToPosition(scrollButtons, getScrollPosition(scroller), getScrollWidth(scroller)) + + var scrollButtons = this; + var scroller = this.scroller; + var pos = getScrollPosition(scroller); + var scrollWidth = getScrollWidth(scroller); + + onScrolledToPosition(scrollButtons, pos, scrollWidth); } function getStyleValue(style, name) { + var value = style.getPropertyValue(name); - return value && (value = value.replace("px", "")) ? (value = parseInt(value), isNaN(value) ? 0 : value) : 0 + + if (!value) { + return 0; + } + + value = value.replace('px', ''); + + if (!value) { + return 0; + } + + value = parseInt(value); + if (isNaN(value)) { + return 0; + } + + return value; } function getScrollSize(elem) { - var scrollSize = elem.offsetWidth, - style = window.getComputedStyle(elem, null), - paddingLeft = getStyleValue(style, "padding-left"); - paddingLeft && (scrollSize -= paddingLeft); - var paddingRight = getStyleValue(style, "padding-right"); - paddingRight && (scrollSize -= paddingRight); + + var scrollSize = elem.offsetWidth; + + var style = window.getComputedStyle(elem, null); + + var paddingLeft = getStyleValue(style, 'padding-left'); + + if (paddingLeft) { + scrollSize -= paddingLeft; + } + var paddingRight = getStyleValue(style, 'padding-right'); + + if (paddingRight) { + scrollSize -= paddingRight; + } + var slider = elem.getScrollSlider(); - return style = window.getComputedStyle(slider, null), paddingLeft = getStyleValue(style, "padding-left"), paddingLeft && (scrollSize -= paddingLeft), paddingRight = getStyleValue(style, "padding-right"), paddingRight && (scrollSize -= paddingRight), scrollSize + style = window.getComputedStyle(slider, null); + + paddingLeft = getStyleValue(style, 'padding-left'); + + if (paddingLeft) { + scrollSize -= paddingLeft; + } + paddingRight = getStyleValue(style, 'padding-right'); + + if (paddingRight) { + scrollSize -= paddingRight; + } + + return scrollSize; } function onScrollButtonClick(e) { - var newPos, parent = dom.parentWithAttribute(this, "is", "emby-scroller"), - direction = this.getAttribute("data-direction"), - scrollSize = getScrollSize(parent), - pos = getScrollPosition(parent); - newPos = "left" === direction ? Math.max(0, pos - scrollSize) : pos + scrollSize, parent.scrollToPosition(newPos, !1) + + var parent = dom.parentWithAttribute(this, 'is', 'emby-scroller'); + + var direction = this.getAttribute('data-direction'); + + var scrollSize = getScrollSize(parent); + + var pos = getScrollPosition(parent); + var newPos; + + if (direction === 'left') { + newPos = Math.max(0, pos - scrollSize); + } else { + newPos = pos + scrollSize; + } + + parent.scrollToPosition(newPos, false); } - var EmbyScrollButtonsPrototype = Object.create(HTMLDivElement.prototype); - EmbyScrollButtonsPrototype.createdCallback = function() {}, EmbyScrollButtonsPrototype.attachedCallback = function() { - var parent = dom.parentWithAttribute(this, "is", "emby-scroller"); - this.scroller = parent, parent.classList.add("emby-scrollbuttons-scroller"), this.innerHTML = getScrollButtonContainerHtml("left") + getScrollButtonContainerHtml("right"); + + EmbyScrollButtonsPrototype.attachedCallback = function () { + + var parent = dom.parentWithAttribute(this, 'is', 'emby-scroller'); + this.scroller = parent; + + parent.classList.add('emby-scrollbuttons-scroller'); + + this.innerHTML = getScrollButtonContainerHtml('left') + getScrollButtonContainerHtml('right'); + var scrollHandler = onScroll.bind(this); this.scrollHandler = scrollHandler; - var buttons = this.querySelectorAll(".emby-scrollbuttons-scrollbutton"); - buttons[0].addEventListener("click", onScrollButtonClick), buttons[1].addEventListener("click", onScrollButtonClick), buttons = this.querySelectorAll(".scrollbuttoncontainer"), this.scrollButtonsLeft = buttons[0], this.scrollButtonsRight = buttons[1], parent.addScrollEventListener(scrollHandler, { - capture: !1, - passive: !0 - }) - }, EmbyScrollButtonsPrototype.detachedCallback = function() { + + var buttons = this.querySelectorAll('.emby-scrollbuttons-scrollbutton'); + buttons[0].addEventListener('click', onScrollButtonClick); + buttons[1].addEventListener('click', onScrollButtonClick); + + buttons = this.querySelectorAll('.scrollbuttoncontainer'); + this.scrollButtonsLeft = buttons[0]; + this.scrollButtonsRight = buttons[1]; + + parent.addScrollEventListener(scrollHandler, { + capture: false, + passive: true + }); + }; + + EmbyScrollButtonsPrototype.detachedCallback = function () { + var parent = this.scroller; this.scroller = null; + var scrollHandler = this.scrollHandler; - parent && scrollHandler && parent.removeScrollEventListener(scrollHandler, { - capture: !1, - passive: !0 - }), this.scrollHandler = null, this.scrollButtonsLeft = null, this.scrollButtonsRight = null - }, document.registerElement("emby-scrollbuttons", { + + if (parent && scrollHandler) { + parent.removeScrollEventListener(scrollHandler, { + capture: false, + passive: true + }); + } + + this.scrollHandler = null; + this.scrollButtonsLeft = null; + this.scrollButtonsRight = null; + }; + + document.registerElement('emby-scrollbuttons', { prototype: EmbyScrollButtonsPrototype, - extends: "div" - }) + extends: 'div' + }); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/emby-scroller/emby-scroller.js b/src/bower_components/emby-webcomponents/emby-scroller/emby-scroller.js index d358666f97..6237fd99f6 100644 --- a/src/bower_components/emby-webcomponents/emby-scroller/emby-scroller.js +++ b/src/bower_components/emby-webcomponents/emby-scroller/emby-scroller.js @@ -1,101 +1,224 @@ -define(["scroller", "dom", "layoutManager", "inputManager", "focusManager", "browser", "registerElement"], function(scroller, dom, layoutManager, inputManager, focusManager, browser) { - "use strict"; +define(['scroller', 'dom', 'layoutManager', 'inputManager', 'focusManager', 'browser', 'registerElement'], function (scroller, dom, layoutManager, inputManager, focusManager, browser) { + 'use strict'; + + var ScrollerProtoType = Object.create(HTMLDivElement.prototype); + + ScrollerProtoType.createdCallback = function () { + this.classList.add('emby-scroller'); + }; function initCenterFocus(elem, scrollerInstance) { - dom.addEventListener(elem, "focus", function(e) { + + dom.addEventListener(elem, 'focus', function (e) { + var focused = focusManager.focusableParent(e.target); - focused && scrollerInstance.toCenter(focused) + + if (focused) { + scrollerInstance.toCenter(focused); + } + }, { - capture: !0, - passive: !0 - }) + capture: true, + passive: true + }); } + ScrollerProtoType.scrollToBeginning = function () { + if (this.scroller) { + this.scroller.slideTo(0, true); + } + }; + ScrollerProtoType.toStart = function (elem, immediate) { + if (this.scroller) { + this.scroller.toStart(elem, immediate); + } + }; + ScrollerProtoType.toCenter = function (elem, immediate) { + if (this.scroller) { + this.scroller.toCenter(elem, immediate); + } + }; + + ScrollerProtoType.scrollToPosition = function (pos, immediate) { + if (this.scroller) { + this.scroller.slideTo(pos, immediate); + } + }; + + ScrollerProtoType.getScrollPosition = function () { + if (this.scroller) { + return this.scroller.getScrollPosition(); + } + }; + + ScrollerProtoType.getScrollSize = function () { + if (this.scroller) { + return this.scroller.getScrollSize(); + } + }; + + ScrollerProtoType.getScrollEventName = function () { + if (this.scroller) { + return this.scroller.getScrollEventName(); + } + }; + + ScrollerProtoType.getScrollSlider = function () { + if (this.scroller) { + return this.scroller.getScrollSlider(); + } + }; + + ScrollerProtoType.addScrollEventListener = function (fn, options) { + if (this.scroller) { + dom.addEventListener(this.scroller.getScrollFrame(), this.scroller.getScrollEventName(), fn, options); + } + }; + + ScrollerProtoType.removeScrollEventListener = function (fn, options) { + if (this.scroller) { + dom.removeEventListener(this.scroller.getScrollFrame(), this.scroller.getScrollEventName(), fn, options); + } + }; + function onInputCommand(e) { + var cmd = e.detail.command; - "end" === cmd ? (focusManager.focusLast(this, "." + this.getAttribute("data-navcommands")), e.preventDefault(), e.stopPropagation()) : "pageup" === cmd ? (focusManager.moveFocus(e.target, this, "." + this.getAttribute("data-navcommands"), -12), e.preventDefault(), e.stopPropagation()) : "pagedown" === cmd && (focusManager.moveFocus(e.target, this, "." + this.getAttribute("data-navcommands"), 12), e.preventDefault(), e.stopPropagation()) + + if (cmd === 'end') { + focusManager.focusLast(this, '.' + this.getAttribute('data-navcommands')); + e.preventDefault(); + e.stopPropagation(); + } + else if (cmd === 'pageup') { + focusManager.moveFocus(e.target, this, '.' + this.getAttribute('data-navcommands'), -12); + e.preventDefault(); + e.stopPropagation(); + } + else if (cmd === 'pagedown') { + focusManager.moveFocus(e.target, this, '.' + this.getAttribute('data-navcommands'), 12); + e.preventDefault(); + e.stopPropagation(); + } } function initHeadroom(elem) { - require(["headroom"], function(Headroom) { + require(['headroom'], function (Headroom) { + var headroom = new Headroom([], { scroller: elem }); - headroom.init(), headroom.add(document.querySelector(".skinHeader")), elem.headroom = headroom - }) + // initialise + headroom.init(); + headroom.add(document.querySelector('.skinHeader')); + elem.headroom = headroom; + }); } + ScrollerProtoType.attachedCallback = function () { + + if (this.getAttribute('data-navcommands')) { + inputManager.on(this, onInputCommand); + } + + var horizontal = this.getAttribute('data-horizontal') !== 'false'; + + var slider = this.querySelector('.scrollSlider'); + + if (horizontal) { + slider.style['white-space'] = 'nowrap'; + } + + var bindHeader = this.getAttribute('data-bindheader') === 'true'; + + var scrollFrame = this; + var enableScrollButtons = layoutManager.desktop && horizontal && this.getAttribute('data-scrollbuttons') !== 'false'; + + var options = { + horizontal: horizontal, + mouseDragging: 1, + mouseWheel: this.getAttribute('data-mousewheel') !== 'false', + touchDragging: 1, + slidee: slider, + scrollBy: 200, + speed: horizontal ? 270 : 240, + //immediateSpeed: pageOptions.immediateSpeed, + elasticBounds: 1, + dragHandle: 1, + scrollWidth: this.getAttribute('data-scrollsize') === 'auto' ? null : 5000000, + autoImmediate: true, + skipSlideToWhenVisible: this.getAttribute('data-skipfocuswhenvisible') === 'true', + dispatchScrollEvent: enableScrollButtons || bindHeader || this.getAttribute('data-scrollevent') === 'true', + hideScrollbar: enableScrollButtons || this.getAttribute('data-hidescrollbar') === 'true', + allowNativeSmoothScroll: this.getAttribute('data-allownativesmoothscroll') === 'true' && !enableScrollButtons, + allowNativeScroll: !enableScrollButtons, + forceHideScrollbars: enableScrollButtons, + + // In edge, with the native scroll, the content jumps around when hovering over the buttons + requireAnimation: enableScrollButtons && browser.edge + }; + + // If just inserted it might not have any height yet - yes this is a hack + this.scroller = new scroller(scrollFrame, options); + this.scroller.init(); + + if (layoutManager.tv && this.getAttribute('data-centerfocus')) { + initCenterFocus(this, this.scroller); + } + + if (bindHeader) { + initHeadroom(this); + } + + if (enableScrollButtons) { + loadScrollButtons(this); + } + }; + function loadScrollButtons(scroller) { - require(["emby-scrollbuttons"], function() { - scroller.insertAdjacentHTML("beforeend", '
') - }) + + require(['emby-scrollbuttons'], function () { + scroller.insertAdjacentHTML('beforeend', '
'); + }); } - var ScrollerProtoType = Object.create(HTMLDivElement.prototype); - ScrollerProtoType.createdCallback = function() { - this.classList.add("emby-scroller") - }, ScrollerProtoType.scrollToBeginning = function() { - this.scroller && this.scroller.slideTo(0, !0) - }, ScrollerProtoType.toStart = function(elem, immediate) { - this.scroller && this.scroller.toStart(elem, immediate) - }, ScrollerProtoType.toCenter = function(elem, immediate) { - this.scroller && this.scroller.toCenter(elem, immediate) - }, ScrollerProtoType.scrollToPosition = function(pos, immediate) { - this.scroller && this.scroller.slideTo(pos, immediate) - }, ScrollerProtoType.getScrollPosition = function() { - if (this.scroller) return this.scroller.getScrollPosition() - }, ScrollerProtoType.getScrollSize = function() { - if (this.scroller) return this.scroller.getScrollSize() - }, ScrollerProtoType.getScrollEventName = function() { - if (this.scroller) return this.scroller.getScrollEventName() - }, ScrollerProtoType.getScrollSlider = function() { - if (this.scroller) return this.scroller.getScrollSlider() - }, ScrollerProtoType.addScrollEventListener = function(fn, options) { - this.scroller && dom.addEventListener(this.scroller.getScrollFrame(), this.scroller.getScrollEventName(), fn, options) - }, ScrollerProtoType.removeScrollEventListener = function(fn, options) { - this.scroller && dom.removeEventListener(this.scroller.getScrollFrame(), this.scroller.getScrollEventName(), fn, options) - }, ScrollerProtoType.attachedCallback = function() { - this.getAttribute("data-navcommands") && inputManager.on(this, onInputCommand); - var horizontal = "false" !== this.getAttribute("data-horizontal"), - slider = this.querySelector(".scrollSlider"); - horizontal && (slider.style["white-space"] = "nowrap"); - var bindHeader = "true" === this.getAttribute("data-bindheader"), - scrollFrame = this, - enableScrollButtons = layoutManager.desktop && horizontal && "false" !== this.getAttribute("data-scrollbuttons"), - options = { - horizontal: horizontal, - mouseDragging: 1, - mouseWheel: "false" !== this.getAttribute("data-mousewheel"), - touchDragging: 1, - slidee: slider, - scrollBy: 200, - speed: horizontal ? 270 : 240, - elasticBounds: 1, - dragHandle: 1, - scrollWidth: "auto" === this.getAttribute("data-scrollsize") ? null : 5e6, - autoImmediate: !0, - skipSlideToWhenVisible: "true" === this.getAttribute("data-skipfocuswhenvisible"), - dispatchScrollEvent: enableScrollButtons || bindHeader || "true" === this.getAttribute("data-scrollevent"), - hideScrollbar: enableScrollButtons || "true" === this.getAttribute("data-hidescrollbar"), - allowNativeSmoothScroll: "true" === this.getAttribute("data-allownativesmoothscroll") && !enableScrollButtons, - allowNativeScroll: !enableScrollButtons, - forceHideScrollbars: enableScrollButtons, - requireAnimation: enableScrollButtons && browser.edge - }; - this.scroller = new scroller(scrollFrame, options), this.scroller.init(), layoutManager.tv && this.getAttribute("data-centerfocus") && initCenterFocus(this, this.scroller), bindHeader && initHeadroom(this), enableScrollButtons && loadScrollButtons(this) - }, ScrollerProtoType.pause = function() { + + ScrollerProtoType.pause = function () { + var headroom = this.headroom; - headroom && headroom.pause() - }, ScrollerProtoType.resume = function() { + if (headroom) { + headroom.pause(); + } + }; + + ScrollerProtoType.resume = function () { + var headroom = this.headroom; - headroom && headroom.resume() - }, ScrollerProtoType.detachedCallback = function() { - this.getAttribute("data-navcommands") && inputManager.off(this, onInputCommand); + if (headroom) { + headroom.resume(); + } + }; + + ScrollerProtoType.detachedCallback = function () { + + if (this.getAttribute('data-navcommands')) { + inputManager.off(this, onInputCommand); + } + var headroom = this.headroom; - headroom && (headroom.destroy(), this.headroom = null); + if (headroom) { + headroom.destroy(); + this.headroom = null; + } + var scrollerInstance = this.scroller; - scrollerInstance && (scrollerInstance.destroy(), this.scroller = null) - }, document.registerElement("emby-scroller", { + if (scrollerInstance) { + scrollerInstance.destroy(); + this.scroller = null; + } + }; + + document.registerElement('emby-scroller', { prototype: ScrollerProtoType, - extends: "div" - }) + extends: 'div' + }); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/emby-select/emby-select.css b/src/bower_components/emby-webcomponents/emby-select/emby-select.css index 1dfe686f53..76b28c4208 100644 --- a/src/bower_components/emby-webcomponents/emby-select/emby-select.css +++ b/src/bower_components/emby-webcomponents/emby-select/emby-select.css @@ -2,91 +2,84 @@ display: block; margin: 0; margin-bottom: 0 !important; + /* Remove select styling */ + /* Font size must the 16px or larger to prevent iOS page zoom on focus */ font-size: 110%; + /* General select styles: change as needed */ font-family: inherit; font-weight: inherit; padding: .5em 1.9em .5em .5em; - -webkit-box-sizing: border-box; + /* Prevent padding from causing width overflow */ box-sizing: border-box; - outline: 0 !important; - -webkit-tap-highlight-color: transparent; - width: 100% + outline: none !important; + -webkit-tap-highlight-color: rgba(0,0,0,0); + width: 100%; } -.emby-select[disabled] { - background: 0 0 !important; - border-color: transparent !important; - color: inherit !important; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none -} + .emby-select[disabled] { + background: none !important; + border-color: transparent !important; + color: inherit !important; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + } -.selectContainer-inline>.emby-select { +.selectContainer-inline > .emby-select { padding: .3em 1.9em .3em .5em; - font-size: inherit + font-size: inherit; } -.selectContainer-inline>.emby-select[disabled] { - padding-left: 0; - padding-right: 0 -} + .selectContainer-inline > .emby-select[disabled] { + padding-left: 0; + padding-right: 0; + } .emby-select::-moz-focus-inner { - border: 0 + border: 0; } .emby-select-focusscale { - -webkit-transition: -webkit-transform 180ms ease-out !important; - -o-transition: transform 180ms ease-out !important; transition: transform 180ms ease-out !important; -webkit-transform-origin: center center; - transform-origin: center center + transform-origin: center center; } -.emby-select-focusscale:focus { - -webkit-transform: scale(1.04); - transform: scale(1.04); - z-index: 1 -} + .emby-select-focusscale:focus { + transform: scale(1.04); + z-index: 1; + } -.emby-select+.fieldDescription { - margin-top: .25em +.emby-select + .fieldDescription { + margin-top: .25em; } .selectContainer { margin-bottom: 1.8em; - position: relative + position: relative; } .selectContainer-inline { - display: -webkit-inline-box; - display: -webkit-inline-flex; display: inline-flex; margin-bottom: 0; - -webkit-box-align: center; - -webkit-align-items: center; - align-items: center + align-items: center; } .selectLabel { display: block; - margin-bottom: .25em + margin-bottom: .25em; } -.selectContainer-inline>.selectLabel { +.selectContainer-inline > .selectLabel { margin-bottom: 0; margin-right: .5em; - -webkit-flex-shrink: 0; - flex-shrink: 0 + flex-shrink: 0; } .emby-select-withcolor { -webkit-appearance: none; - -moz-appearance: none; appearance: none; - -webkit-border-radius: .2em; - border-radius: .2em + border-radius: .2em; } .selectArrowContainer { @@ -94,25 +87,24 @@ right: .3em; top: .2em; color: inherit; - pointer-events: none + pointer-events: none; } -.selectContainer-inline>.selectArrowContainer { +.selectContainer-inline > .selectArrowContainer { top: initial; bottom: .24em; - font-size: 90% + font-size: 90%; } -.emby-select[disabled]+.selectArrowContainer { - display: none +.emby-select[disabled] + .selectArrowContainer { + display: none; } .selectArrow { margin-top: .35em; - font-size: 1.7em + font-size: 1.7em; } .emby-select-iconbutton { - -webkit-align-self: flex-end; - align-self: flex-end -} \ No newline at end of file + align-self: flex-end; +} diff --git a/src/bower_components/emby-webcomponents/emby-select/emby-select.js b/src/bower_components/emby-webcomponents/emby-select/emby-select.js index 25e646fcdb..6783f2f48c 100644 --- a/src/bower_components/emby-webcomponents/emby-select/emby-select.js +++ b/src/bower_components/emby-webcomponents/emby-select/emby-select.js @@ -1,75 +1,168 @@ -define(["layoutManager", "browser", "actionsheet", "css!./emby-select", "registerElement"], function(layoutManager, browser, actionsheet) { - "use strict"; +define(['layoutManager', 'browser', 'actionsheet', 'css!./emby-select', 'registerElement'], function (layoutManager, browser, actionsheet) { + 'use strict'; + + var EmbySelectPrototype = Object.create(HTMLSelectElement.prototype); function enableNativeMenu() { - return !(!browser.edgeUwp && !browser.xboxOne) || !(browser.tizen || browser.orsay || browser.web0s) && (!!browser.tv || !layoutManager.tv) + + if (browser.edgeUwp || browser.xboxOne) { + return true; + } + + // Doesn't seem to work at all + if (browser.tizen || browser.orsay || browser.web0s) { + return false; + } + + // Take advantage of the native input methods + if (browser.tv) { + return true; + } + + if (layoutManager.tv) { + return false; + } + + return true; } function triggerChange(select) { var evt = document.createEvent("HTMLEvents"); - evt.initEvent("change", !1, !0), select.dispatchEvent(evt) + evt.initEvent("change", false, true); + select.dispatchEvent(evt); } function setValue(select, value) { - select.value = value + + select.value = value; } function showActionSheet(select) { - var labelElem = getLabel(select), - title = labelElem ? labelElem.textContent || labelElem.innerText : null; + + var labelElem = getLabel(select); + var title = labelElem ? (labelElem.textContent || labelElem.innerText) : null; + actionsheet.show({ items: select.options, positionTo: select, title: title - }).then(function(value) { - setValue(select, value), triggerChange(select) - }) + + }).then(function (value) { + setValue(select, value); + triggerChange(select); + }); } function getLabel(select) { - for (var elem = select.previousSibling; elem && "LABEL" !== elem.tagName;) elem = elem.previousSibling; - return elem + var elem = select.previousSibling; + while (elem && elem.tagName !== 'LABEL') { + elem = elem.previousSibling; + } + return elem; } function onFocus(e) { var label = getLabel(this); - label && label.classList.add("selectLabelFocused") + if (label) { + label.classList.add('selectLabelFocused'); + } } function onBlur(e) { var label = getLabel(this); - label && label.classList.remove("selectLabelFocused") + if (label) { + label.classList.remove('selectLabelFocused'); + } } function onMouseDown(e) { - e.button || enableNativeMenu() || (e.preventDefault(), showActionSheet(this)) + + // e.button=0 for primary (left) mouse button click + if (!e.button && !enableNativeMenu()) { + e.preventDefault(); + showActionSheet(this); + } } function onKeyDown(e) { + switch (e.keyCode) { + case 13: - return void(enableNativeMenu() || (e.preventDefault(), showActionSheet(this))); + if (!enableNativeMenu()) { + e.preventDefault(); + showActionSheet(this); + } + return; case 37: case 38: case 39: case 40: - return void(layoutManager.tv && e.preventDefault()) + if (layoutManager.tv) { + e.preventDefault(); + } + return; + default: + break; } } - var EmbySelectPrototype = Object.create(HTMLSelectElement.prototype), - inputId = 0; - EmbySelectPrototype.createdCallback = function() { - this.id || (this.id = "embyselect" + inputId, inputId++), browser.firefox || (this.classList.add("emby-select-withcolor"), layoutManager.tv && this.classList.add("emby-select-tv-withcolor")), layoutManager.tv && this.classList.add("emby-select-focusscale"), this.addEventListener("mousedown", onMouseDown), this.addEventListener("keydown", onKeyDown), this.addEventListener("focus", onFocus), this.addEventListener("blur", onBlur) - }, EmbySelectPrototype.attachedCallback = function() { - if (!this.classList.contains("emby-select")) { - this.classList.add("emby-select"); - var label = this.ownerDocument.createElement("label"); - label.innerHTML = this.getAttribute("label") || "", label.classList.add("selectLabel"), label.htmlFor = this.id, this.parentNode.insertBefore(label, this), this.classList.contains("emby-select-withcolor") && this.parentNode.insertAdjacentHTML("beforeend", '
0
') + + var inputId = 0; + + EmbySelectPrototype.createdCallback = function () { + + if (!this.id) { + this.id = 'embyselect' + inputId; + inputId++; } - }, EmbySelectPrototype.setLabel = function(text) { - this.parentNode.querySelector("label").innerHTML = text - }, document.registerElement("emby-select", { + + if (!browser.firefox) { + this.classList.add('emby-select-withcolor'); + + if (layoutManager.tv) { + this.classList.add('emby-select-tv-withcolor'); + } + } + + if (layoutManager.tv) { + this.classList.add('emby-select-focusscale'); + } + + this.addEventListener('mousedown', onMouseDown); + this.addEventListener('keydown', onKeyDown); + + this.addEventListener('focus', onFocus); + this.addEventListener('blur', onBlur); + }; + + EmbySelectPrototype.attachedCallback = function () { + + if (this.classList.contains('emby-select')) { + return; + } + + this.classList.add('emby-select'); + + var label = this.ownerDocument.createElement('label'); + label.innerHTML = this.getAttribute('label') || ''; + label.classList.add('selectLabel'); + label.htmlFor = this.id; + this.parentNode.insertBefore(label, this); + + if (this.classList.contains('emby-select-withcolor')) { + this.parentNode.insertAdjacentHTML('beforeend', '
0
'); + } + }; + + EmbySelectPrototype.setLabel = function (text) { + + var label = this.parentNode.querySelector('label'); + + label.innerHTML = text; + }; + + document.registerElement('emby-select', { prototype: EmbySelectPrototype, - extends: "select" - }) + extends: 'select' + }); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/emby-slider/emby-slider.css b/src/bower_components/emby-webcomponents/emby-slider/emby-slider.css index 38cad647b3..9617c6be49 100644 --- a/src/bower_components/emby-webcomponents/emby-slider/emby-slider.css +++ b/src/bower_components/emby-webcomponents/emby-slider/emby-slider.css @@ -1,7 +1,7 @@ _:-ms-input-placeholder { -ms-appearance: none; height: 2.223em; - margin: 0 + margin: 0; } .mdl-slider { @@ -11,7 +11,7 @@ _:-ms-input-placeholder { -ms-appearance: none; appearance: none; height: .2em; - background: 0 0; + background: transparent; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; @@ -20,78 +20,79 @@ _:-ms-input-placeholder { padding: 1em 0; color: #00a4dc; -webkit-align-self: center; + -ms-flex-item-align: center; align-self: center; z-index: 1; cursor: pointer; margin: 0; - -webkit-tap-highlight-color: transparent; - display: block + /* Disable webkit tap highlighting */ + -webkit-tap-highlight-color: rgba(0,0,0,0); + display: block; + /**************************** Tracks ****************************/ + /**************************** Thumbs ****************************/ + /**************************** 0-value ****************************/ + /**************************** Disabled ****************************/ } -.mdl-slider::-moz-focus-outer { - border: 0 -} + .mdl-slider::-moz-focus-outer { + border: 0; + } -.mdl-slider::-ms-tooltip { - display: none -} + .mdl-slider::-ms-tooltip { + display: none; + } -.mdl-slider::-webkit-slider-runnable-track { - background: 0 0 -} + .mdl-slider::-webkit-slider-runnable-track { + background: transparent; + } -.mdl-slider::-moz-range-track { - background: #444; - border: none -} + .mdl-slider::-moz-range-track { + background: #444; + border: none; + } -.mdl-slider::-moz-range-progress { - background: #00a4dc -} + .mdl-slider::-moz-range-progress { + background: #00a4dc; + } -.mdl-slider::-ms-track { - background: 0 0; - color: transparent; - height: .2em; - width: 100%; - border: none -} + .mdl-slider::-ms-track { + background: none; + color: transparent; + height: .2em; + width: 100%; + border: none; + } -.mdl-slider::-ms-fill-lower { - display: none -} + .mdl-slider::-ms-fill-lower { + display: none; + } -.mdl-slider::-ms-fill-upper { - display: none -} + .mdl-slider::-ms-fill-upper { + display: none; + } -.mdl-slider::-webkit-slider-thumb { - -webkit-appearance: none; - width: 1.8em; - height: 1.8em; - -webkit-box-sizing: border-box; - box-sizing: border-box; - -webkit-border-radius: 50%; - border-radius: 50%; - background: #00a4dc; - border: none; - -webkit-transition: -webkit-transform .3s cubic-bezier(.4, 0, .2, 1), border .18s cubic-bezier(.4, 0, .2, 1), -webkit-box-shadow .18s cubic-bezier(.4, 0, .2, 1), background .28s cubic-bezier(.4, 0, .2, 1); - transition: transform .3s cubic-bezier(.4, 0, .2, 1), border .18s cubic-bezier(.4, 0, .2, 1), box-shadow .18s cubic-bezier(.4, 0, .2, 1), background .28s cubic-bezier(.4, 0, .2, 1) -} + .mdl-slider::-webkit-slider-thumb { + -webkit-appearance: none; + width: 1.8em; + height: 1.8em; + box-sizing: border-box; + border-radius: 50%; + background: #00a4dc; + border: none; + transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1), border 0.18s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.18s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1); + } .mdl-slider-hoverthumb::-webkit-slider-thumb { margin-left: -.12em; - -webkit-transform: scale(.7, .7); - transform: scale(.7, .7) + transform: scale(.7, .7); } .mdl-slider:hover::-webkit-slider-thumb { - -webkit-transform: none; - transform: none + transform: none; } .slider-no-webkit-thumb::-webkit-slider-thumb { - opacity: 0 !important + opacity: 0 !important; } .mdl-slider::-moz-range-thumb { @@ -100,8 +101,9 @@ _:-ms-input-placeholder { height: 1.8em; box-sizing: border-box; border-radius: 50%; + background-image: none; background: #00a4dc; - border: none + border: none; } .mdl-slider::-ms-thumb { @@ -112,28 +114,28 @@ _:-ms-input-placeholder { border-radius: 50%; background: #00a4dc; border: none; - transition: transform .3s cubic-bezier(.4, 0, .2, 1), border .18s cubic-bezier(.4, 0, .2, 1), box-shadow .18s cubic-bezier(.4, 0, .2, 1), background .28s cubic-bezier(.4, 0, .2, 1) + transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1), border 0.18s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.18s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1); } .mdl-slider-hoverthumb::-ms-thumb { margin-left: -.4em; - transform: scale(.5, .5) + transform: scale(.5, .5); } .mdl-slider:hover::-ms-thumb { - transform: none + transform: none; } .mdl-slider[disabled]::-webkit-slider-thumb { - display: none + display: none; } .mdl-slider[disabled]::-moz-range-thumb { - display: none + display: none; } .mdl-slider[disabled]::-ms-thumb { - display: none + display: none; } .mdl-slider-ie-container { @@ -141,20 +143,15 @@ _:-ms-input-placeholder { overflow: visible; border: none; margin: 0; - padding: 0 + padding: 0; } .mdl-slider-container { height: 1.25em; position: relative; - background: 0 0; - display: -webkit-box; - display: -webkit-flex; + background: none; display: flex; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -webkit-flex-direction: row; - flex-direction: row + flex-direction: row; } .mdl-slider-background-flex { @@ -165,70 +162,62 @@ _:-ms-input-placeholder { width: 100%; top: 50%; left: 0; - display: -webkit-box; - display: -webkit-flex; display: flex; overflow: hidden; border: 0; - padding: 0 + padding: 0; } .mdl-slider-background-flex-inner { position: relative; - width: 100% + width: 100%; } .mdl-slider-background-lower { + /*transition: width 0.18s cubic-bezier(0.4, 0, 0.2, 1);*/ position: absolute; left: 0; width: 0; top: 0; bottom: 0; - background-color: #00a4dc + background-color: #00a4dc; } .mdl-slider-background-lower-clear { - background-color: transparent + background-color: transparent; } .mdl-slider-background-lower-withtransform { width: 100%; - -webkit-transform-origin: left center; + /*transition: transform 0.18s cubic-bezier(0.4, 0, 0.2, 1);*/ transform-origin: left center; - -webkit-transform: scaleX(0); - transform: scaleX(0) + transform: scaleX(0); } .mdl-slider-background-upper { + /*transition: left 0.18s cubic-bezier(0.4, 0, 0.2, 1), width 0.18s cubic-bezier(0.4, 0, 0.2, 1);*/ background: #666; background: rgba(255, 255, 255, .4); position: absolute; left: 0; width: 0; top: 0; - bottom: 0 + bottom: 0; } .sliderBubble { position: absolute; top: 0; left: 0; - -webkit-transform: translate3d(-48%, -120%, 0); transform: translate3d(-48%, -120%, 0); background: #282828; color: #fff; - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; - justify-content: center + justify-content: center; } .sliderBubbleText { margin: 0; - padding: .5em .75em -} \ No newline at end of file + padding: .5em .75em; +} diff --git a/src/bower_components/emby-webcomponents/emby-slider/emby-slider.js b/src/bower_components/emby-webcomponents/emby-slider/emby-slider.js index 74e160d5b7..06fcdcfea1 100644 --- a/src/bower_components/emby-webcomponents/emby-slider/emby-slider.js +++ b/src/bower_components/emby-webcomponents/emby-slider/emby-slider.js @@ -1,101 +1,270 @@ -define(["browser", "dom", "layoutManager", "css!./emby-slider", "registerElement", "emby-input"], function(browser, dom, layoutManager) { - "use strict"; +define(['browser', 'dom', 'layoutManager', 'css!./emby-slider', 'registerElement', 'emby-input'], function (browser, dom, layoutManager) { + 'use strict'; + + var EmbySliderPrototype = Object.create(HTMLInputElement.prototype); + + var supportsNativeProgressStyle = browser.firefox; + var supportsValueSetOverride = false; + + var enableWidthWithTransform; + + if (Object.getOwnPropertyDescriptor && Object.defineProperty) { + + var descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value'); + // descriptor returning null in webos + if (descriptor && descriptor.configurable) { + supportsValueSetOverride = true; + } + } function updateValues() { - var range = this, - value = range.value; - requestAnimationFrame(function() { + + var range = this; + var value = range.value; + + // put this on a callback. Doing it within the event sometimes causes the slider to get hung up and not respond + requestAnimationFrame(function () { + var backgroundLower = range.backgroundLower; + if (backgroundLower) { var fraction = (value - range.min) / (range.max - range.min); - enableWidthWithTransform ? backgroundLower.style.transform = "scaleX(" + fraction + ")" : (fraction *= 100, backgroundLower.style.width = fraction + "%") + + if (enableWidthWithTransform) { + backgroundLower.style.transform = 'scaleX(' + (fraction) + ')'; + } else { + fraction *= 100; + backgroundLower.style.width = fraction + '%'; + } } - }) + }); } function updateBubble(range, value, bubble, bubbleText) { - requestAnimationFrame(function() { - bubble.style.left = value + "%", range.getBubbleHtml ? value = range.getBubbleHtml(value) : (value = range.getBubbleText ? range.getBubbleText(value) : Math.round(value), value = '

' + value + "

"), bubble.innerHTML = value - }) + + requestAnimationFrame(function () { + + bubble.style.left = value + '%'; + + if (range.getBubbleHtml) { + value = range.getBubbleHtml(value); + } else { + if (range.getBubbleText) { + value = range.getBubbleText(value); + } else { + value = Math.round(value); + } + value = '

' + value + '

'; + } + + bubble.innerHTML = value; + }); } + EmbySliderPrototype.attachedCallback = function () { + + if (this.getAttribute('data-embyslider') === 'true') { + return; + } + + if (enableWidthWithTransform == null) { + //enableWidthWithTransform = browser.supportsCssAnimation(); + } + + this.setAttribute('data-embyslider', 'true'); + + this.classList.add('mdl-slider'); + this.classList.add('mdl-js-slider'); + + if (browser.noFlex) { + this.classList.add('slider-no-webkit-thumb'); + } + if (!layoutManager.mobile) { + this.classList.add('mdl-slider-hoverthumb'); + } + + var containerElement = this.parentNode; + containerElement.classList.add('mdl-slider-container'); + + var htmlToInsert = ''; + + if (!supportsNativeProgressStyle) { + htmlToInsert += '
'; + htmlToInsert += '
'; + + // the more of these, the more ranges we can display + htmlToInsert += '
'; + + if (enableWidthWithTransform) { + htmlToInsert += '
'; + } else { + htmlToInsert += '
'; + } + + htmlToInsert += '
'; + htmlToInsert += '
'; + } + + htmlToInsert += '
'; + + containerElement.insertAdjacentHTML('beforeend', htmlToInsert); + + this.backgroundLower = containerElement.querySelector('.mdl-slider-background-lower'); + this.backgroundUpper = containerElement.querySelector('.mdl-slider-background-upper'); + var sliderBubble = containerElement.querySelector('.sliderBubble'); + + var hasHideClass = sliderBubble.classList.contains('hide'); + + dom.addEventListener(this, 'input', function (e) { + this.dragging = true; + + updateBubble(this, this.value, sliderBubble); + + if (hasHideClass) { + sliderBubble.classList.remove('hide'); + hasHideClass = false; + } + }, { + passive: true + }); + + dom.addEventListener(this, 'change', function () { + this.dragging = false; + updateValues.call(this); + + sliderBubble.classList.add('hide'); + hasHideClass = true; + + }, { + passive: true + }); + + // In firefox this feature disrupts the ability to move the slider + if (!browser.firefox) { + dom.addEventListener(this, (window.PointerEvent ? 'pointermove' : 'mousemove'), function (e) { + + if (!this.dragging) { + var rect = this.getBoundingClientRect(); + var clientX = e.clientX; + var bubbleValue = (clientX - rect.left) / rect.width; + bubbleValue *= 100; + updateBubble(this, bubbleValue, sliderBubble); + + if (hasHideClass) { + sliderBubble.classList.remove('hide'); + hasHideClass = false; + } + } + + }, { + passive: true + }); + + dom.addEventListener(this, (window.PointerEvent ? 'pointerleave' : 'mouseleave'), function () { + sliderBubble.classList.add('hide'); + hasHideClass = true; + }, { + passive: true + }); + } + + if (!supportsNativeProgressStyle) { + + if (supportsValueSetOverride) { + this.addEventListener('valueset', updateValues); + } else { + startInterval(this); + } + } + }; + function setRange(elem, startPercent, endPercent) { + var style = elem.style; - style.left = Math.max(startPercent, 0) + "%"; + style.left = Math.max(startPercent, 0) + '%'; + var widthPercent = endPercent - startPercent; - style.width = Math.max(Math.min(widthPercent, 100), 0) + "%" + style.width = Math.max(Math.min(widthPercent, 100), 0) + '%'; } function mapRangesFromRuntimeToPercent(ranges, runtime) { - return runtime ? ranges.map(function(r) { + + if (!runtime) { + return []; + } + + return ranges.map(function (r) { + return { - start: r.start / runtime * 100, - end: r.end / runtime * 100 - } - }) : [] + start: (r.start / runtime) * 100, + end: (r.end / runtime) * 100 + }; + }); } + EmbySliderPrototype.setBufferedRanges = function (ranges, runtime, position) { + + var elem = this.backgroundUpper; + if (!elem) { + return; + } + + if (runtime != null) { + ranges = mapRangesFromRuntimeToPercent(ranges, runtime); + + position = (position / runtime) * 100; + } + + for (var i = 0, length = ranges.length; i < length; i++) { + + var range = ranges[i]; + + if (position != null) { + if (position >= range.end) { + continue; + } + } + + setRange(elem, range.start, range.end); + return; + } + + setRange(elem, 0, 0); + }; + + EmbySliderPrototype.setIsClear = function (isClear) { + + var backgroundLower = this.backgroundLower; + if (backgroundLower) { + if (isClear) { + backgroundLower.classList.add('mdl-slider-background-lower-clear'); + } else { + backgroundLower.classList.remove('mdl-slider-background-lower-clear'); + } + } + }; + function startInterval(range) { var interval = range.interval; - interval && clearInterval(interval), range.interval = setInterval(updateValues.bind(range), 100) - } - var enableWidthWithTransform, EmbySliderPrototype = Object.create(HTMLInputElement.prototype), - supportsNativeProgressStyle = browser.firefox, - supportsValueSetOverride = !1; - if (Object.getOwnPropertyDescriptor && Object.defineProperty) { - var descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value"); - descriptor && descriptor.configurable && (supportsValueSetOverride = !0) - } - EmbySliderPrototype.attachedCallback = function() { - if ("true" !== this.getAttribute("data-embyslider")) { - this.setAttribute("data-embyslider", "true"), this.classList.add("mdl-slider"), this.classList.add("mdl-js-slider"), browser.noFlex && this.classList.add("slider-no-webkit-thumb"), layoutManager.mobile || this.classList.add("mdl-slider-hoverthumb"); - var containerElement = this.parentNode; - containerElement.classList.add("mdl-slider-container"); - var htmlToInsert = ""; - supportsNativeProgressStyle || (htmlToInsert += '
', htmlToInsert += '
', htmlToInsert += '
', htmlToInsert += enableWidthWithTransform ? '
' : '
', htmlToInsert += "
", htmlToInsert += "
"), htmlToInsert += '
', containerElement.insertAdjacentHTML("beforeend", htmlToInsert), this.backgroundLower = containerElement.querySelector(".mdl-slider-background-lower"), this.backgroundUpper = containerElement.querySelector(".mdl-slider-background-upper"); - var sliderBubble = containerElement.querySelector(".sliderBubble"), - hasHideClass = sliderBubble.classList.contains("hide"); - dom.addEventListener(this, "input", function(e) { - this.dragging = !0, updateBubble(this, this.value, sliderBubble), hasHideClass && (sliderBubble.classList.remove("hide"), hasHideClass = !1) - }, { - passive: !0 - }), dom.addEventListener(this, "change", function() { - this.dragging = !1, updateValues.call(this), sliderBubble.classList.add("hide"), hasHideClass = !0 - }, { - passive: !0 - }), browser.firefox || (dom.addEventListener(this, window.PointerEvent ? "pointermove" : "mousemove", function(e) { - if (!this.dragging) { - var rect = this.getBoundingClientRect(), - clientX = e.clientX, - bubbleValue = (clientX - rect.left) / rect.width; - bubbleValue *= 100, updateBubble(this, bubbleValue, sliderBubble), hasHideClass && (sliderBubble.classList.remove("hide"), hasHideClass = !1) - } - }, { - passive: !0 - }), dom.addEventListener(this, window.PointerEvent ? "pointerleave" : "mouseleave", function() { - sliderBubble.classList.add("hide"), hasHideClass = !0 - }, { - passive: !0 - })), supportsNativeProgressStyle || (supportsValueSetOverride ? this.addEventListener("valueset", updateValues) : startInterval(this)) + if (interval) { + clearInterval(interval); } - }, EmbySliderPrototype.setBufferedRanges = function(ranges, runtime, position) { - var elem = this.backgroundUpper; - if (elem) { - null != runtime && (ranges = mapRangesFromRuntimeToPercent(ranges, runtime), position = position / runtime * 100); - for (var i = 0, length = ranges.length; i < length; i++) { - var range = ranges[i]; - if (!(null != position && position >= range.end)) return void setRange(elem, range.start, range.end) - } - setRange(elem, 0, 0) - } - }, EmbySliderPrototype.setIsClear = function(isClear) { - var backgroundLower = this.backgroundLower; - backgroundLower && (isClear ? backgroundLower.classList.add("mdl-slider-background-lower-clear") : backgroundLower.classList.remove("mdl-slider-background-lower-clear")) - }, EmbySliderPrototype.detachedCallback = function() { + range.interval = setInterval(updateValues.bind(range), 100); + } + + EmbySliderPrototype.detachedCallback = function () { + var interval = this.interval; - interval && clearInterval(interval), this.interval = null, this.backgroundUpper = null, this.backgroundLower = null - }, document.registerElement("emby-slider", { + if (interval) { + clearInterval(interval); + } + this.interval = null; + this.backgroundUpper = null; + this.backgroundLower = null; + }; + + document.registerElement('emby-slider', { prototype: EmbySliderPrototype, - extends: "input" - }) + extends: 'input' + }); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/emby-tabs/emby-tabs.css b/src/bower_components/emby-webcomponents/emby-tabs/emby-tabs.css index 56b5124d11..49881994bd 100644 --- a/src/bower_components/emby-webcomponents/emby-tabs/emby-tabs.css +++ b/src/bower_components/emby-webcomponents/emby-tabs/emby-tabs.css @@ -1,43 +1,47 @@ -.emby-tab-button, -.emby-tabs-slider { - position: relative -} - -.emby-tab-button { - background: 0 0; - -webkit-box-shadow: none; +.emby-tab-button { + background: transparent; box-shadow: none; cursor: pointer; - outline: 0 !important; + outline: none !important; width: auto; font-family: inherit; font-size: inherit; display: inline-block; vertical-align: middle; - -webkit-flex-shrink: 0; flex-shrink: 0; margin: 0; padding: 1em .9em; + position: relative; height: auto; min-width: initial; line-height: initial; - -webkit-border-radius: 0 !important; border-radius: 0 !important; overflow: hidden; - font-weight: 600 + font-weight: 600; } -.emby-tab-button.emby-button-tv:focus { - -webkit-transform: scale(1.32); - transform: scale(1.32); - -webkit-transform-origin: center center; - transform-origin: center center + /*.emby-tab-button-active { + color: #52B54B; +} + + .emby-tab-button-active.emby-button-tv { + color: #fff; + }*/ + + .emby-tab-button.emby-button-tv:focus { + /*color: #52B54B;*/ + transform: scale(1.32); + transform-origin: center center; + } + +.emby-tabs-slider { + position: relative; } .emby-tab-button-ripple-effect { - background: rgba(0, 0, 0, .7) !important + background: rgba(0,0,0,.7) !important; } .tabContent:not(.is-active) { - display: none -} \ No newline at end of file + display: none; +} diff --git a/src/bower_components/emby-webcomponents/emby-tabs/emby-tabs.js b/src/bower_components/emby-webcomponents/emby-tabs/emby-tabs.js index 3408072a8a..3642facd2a 100644 --- a/src/bower_components/emby-webcomponents/emby-tabs/emby-tabs.js +++ b/src/bower_components/emby-webcomponents/emby-tabs/emby-tabs.js @@ -1,191 +1,370 @@ -define(["dom", "scroller", "browser", "layoutManager", "focusManager", "registerElement", "css!./emby-tabs", "scrollStyles"], function(dom, scroller, browser, layoutManager, focusManager) { - "use strict"; +define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'registerElement', 'css!./emby-tabs', 'scrollStyles'], function (dom, scroller, browser, layoutManager, focusManager) { + 'use strict'; + + var EmbyTabs = Object.create(HTMLDivElement.prototype); + var buttonClass = 'emby-tab-button'; + var activeButtonClass = buttonClass + '-active'; function setActiveTabButton(tabs, newButton, oldButton, animate) { - newButton.classList.add(activeButtonClass) + + newButton.classList.add(activeButtonClass); } function getFocusCallback(tabs, e) { - return function() { - onClick.call(tabs, e) - } + return function () { + onClick.call(tabs, e); + }; } function onFocus(e) { - layoutManager.tv && (this.focusTimeout && clearTimeout(this.focusTimeout), this.focusTimeout = setTimeout(getFocusCallback(this, e), 700)) + + if (layoutManager.tv) { + + if (this.focusTimeout) { + clearTimeout(this.focusTimeout); + } + this.focusTimeout = setTimeout(getFocusCallback(this, e), 700); + } } function getTabPanel(tabs, index) { - return null + + return null; } function removeActivePanelClass(tabs, index) { var tabPanel = getTabPanel(tabs, index); - tabPanel && tabPanel.classList.remove("is-active") + if (tabPanel) { + tabPanel.classList.remove('is-active'); + } + } + + function addActivePanelClass(tabs, index) { + var tabPanel = getTabPanel(tabs, index); + if (tabPanel) { + tabPanel.classList.add('is-active'); + } } function fadeInRight(elem) { - var pct = browser.mobile ? "4%" : "0.5%", - keyframes = [{ - opacity: "0", - transform: "translate3d(" + pct + ", 0, 0)", - offset: 0 - }, { - opacity: "1", - transform: "none", - offset: 1 - }]; + + var pct = browser.mobile ? '4%' : '0.5%'; + + var keyframes = [ + { opacity: '0', transform: 'translate3d(' + pct + ', 0, 0)', offset: 0 }, + { opacity: '1', transform: 'none', offset: 1 }]; + elem.animate(keyframes, { duration: 160, iterations: 1, - easing: "ease-out" - }) + easing: 'ease-out' + }); } function triggerBeforeTabChange(tabs, index, previousIndex) { + tabs.dispatchEvent(new CustomEvent("beforetabchange", { detail: { selectedTabIndex: index, previousIndex: previousIndex } - })), null != previousIndex && previousIndex !== index && removeActivePanelClass(tabs, previousIndex); + })); + if (previousIndex != null && previousIndex !== index) { + removeActivePanelClass(tabs, previousIndex); + } + var newPanel = getTabPanel(tabs, index); - newPanel && (newPanel.animate && fadeInRight(newPanel), newPanel.classList.add("is-active")) + + if (newPanel) { + // animate new panel ? + if (newPanel.animate) { + fadeInRight(newPanel); + } + + newPanel.classList.add('is-active'); + } } function onClick(e) { - this.focusTimeout && clearTimeout(this.focusTimeout); - var tabs = this, - current = tabs.querySelector("." + activeButtonClass), - tabButton = dom.parentWithClass(e.target, buttonClass); + + if (this.focusTimeout) { + clearTimeout(this.focusTimeout); + } + + var tabs = this; + + var current = tabs.querySelector('.' + activeButtonClass); + var tabButton = dom.parentWithClass(e.target, buttonClass); + if (tabButton && tabButton !== current) { - current && current.classList.remove(activeButtonClass); - var previousIndex = current ? parseInt(current.getAttribute("data-index")) : null; - setActiveTabButton(tabs, tabButton, current, !0); - var index = parseInt(tabButton.getAttribute("data-index")); - triggerBeforeTabChange(tabs, index, previousIndex), setTimeout(function() { - tabs.selectedTabIndex = index, tabs.dispatchEvent(new CustomEvent("tabchange", { + + if (current) { + current.classList.remove(activeButtonClass); + } + + var previousIndex = current ? parseInt(current.getAttribute('data-index')) : null; + + setActiveTabButton(tabs, tabButton, current, true); + + var index = parseInt(tabButton.getAttribute('data-index')); + + triggerBeforeTabChange(tabs, index, previousIndex); + + // If toCenter is called syncronously within the click event, it sometimes ends up canceling it + setTimeout(function () { + + tabs.selectedTabIndex = index; + + tabs.dispatchEvent(new CustomEvent("tabchange", { detail: { selectedTabIndex: index, previousIndex: previousIndex } - })) - }, 120), tabs.scroller && tabs.scroller.toCenter(tabButton, !1) + })); + }, 120); + + if (tabs.scroller) { + tabs.scroller.toCenter(tabButton, false); + } + } } function initScroller(tabs) { - if (!tabs.scroller) { - var contentScrollSlider = tabs.querySelector(".emby-tabs-slider"); - contentScrollSlider ? (tabs.scroller = new scroller(tabs, { + + if (tabs.scroller) { + return; + } + + var contentScrollSlider = tabs.querySelector('.emby-tabs-slider'); + if (contentScrollSlider) { + tabs.scroller = new scroller(tabs, { horizontal: 1, itemNav: 0, mouseDragging: 1, touchDragging: 1, slidee: contentScrollSlider, - smart: !0, - releaseSwing: !0, + smart: true, + releaseSwing: true, scrollBy: 200, speed: 120, elasticBounds: 1, dragHandle: 1, dynamicHandle: 1, clickBar: 1, - hiddenScroll: !0, + hiddenScroll: true, + + // In safari the transform is causing the headers to occasionally disappear or flicker requireAnimation: !browser.safari, - allowNativeSmoothScroll: !0 - }), tabs.scroller.init()) : (tabs.classList.add("scrollX"), tabs.classList.add("hiddenScrollX"), tabs.classList.add("smoothScrollX")) - } + allowNativeSmoothScroll: true + }); + tabs.scroller.init(); + } else { + tabs.classList.add('scrollX'); + tabs.classList.add('hiddenScrollX'); + tabs.classList.add('smoothScrollX'); + } } + EmbyTabs.createdCallback = function () { + + if (this.classList.contains('emby-tabs')) { + return; + } + this.classList.add('emby-tabs'); + this.classList.add('focusable'); + + dom.addEventListener(this, 'click', onClick, { + passive: true + }); + dom.addEventListener(this, 'focus', onFocus, { + passive: true, + capture: true + }); + }; + + EmbyTabs.focus = function () { + + var selected = this.querySelector('.' + activeButtonClass); + + if (selected) { + focusManager.focus(selected); + } else { + focusManager.autoFocus(this); + } + }; + + EmbyTabs.refresh = function () { + + if (this.scroller) { + this.scroller.reload(); + } + }; + + EmbyTabs.attachedCallback = function () { + + initScroller(this); + + var current = this.querySelector('.' + activeButtonClass); + var currentIndex = current ? parseInt(current.getAttribute('data-index')) : parseInt(this.getAttribute('data-index') || '0'); + + if (currentIndex !== -1) { + + this.selectedTabIndex = currentIndex; + + var tabButtons = this.querySelectorAll('.' + buttonClass); + + var newTabButton = tabButtons[currentIndex]; + + if (newTabButton) { + setActiveTabButton(this, newTabButton, current, false); + } + } + + if (!this.readyFired) { + this.readyFired = true; + this.dispatchEvent(new CustomEvent("ready", {})); + } + }; + + EmbyTabs.detachedCallback = function () { + + if (this.scroller) { + this.scroller.destroy(); + this.scroller = null; + } + + dom.removeEventListener(this, 'click', onClick, { + passive: true + }); + dom.removeEventListener(this, 'focus', onFocus, { + passive: true, + capture: true + }); + }; + function getSelectedTabButton(elem) { - return elem.querySelector("." + activeButtonClass) + + return elem.querySelector('.' + activeButtonClass); } - function getSibling(elem, method) { - for (var sibling = elem[method]; sibling;) { - if (sibling.classList.contains(buttonClass) && !sibling.classList.contains("hide")) return sibling; - sibling = sibling[method] - } - return null - } - var EmbyTabs = Object.create(HTMLDivElement.prototype), - buttonClass = "emby-tab-button", - activeButtonClass = buttonClass + "-active"; - EmbyTabs.createdCallback = function() { - this.classList.contains("emby-tabs") || (this.classList.add("emby-tabs"), this.classList.add("focusable"), dom.addEventListener(this, "click", onClick, { - passive: !0 - }), dom.addEventListener(this, "focus", onFocus, { - passive: !0, - capture: !0 - })) - }, EmbyTabs.focus = function() { - var selected = this.querySelector("." + activeButtonClass); - selected ? focusManager.focus(selected) : focusManager.autoFocus(this) - }, EmbyTabs.refresh = function() { - this.scroller && this.scroller.reload() - }, EmbyTabs.attachedCallback = function() { - initScroller(this); - var current = this.querySelector("." + activeButtonClass), - currentIndex = current ? parseInt(current.getAttribute("data-index")) : parseInt(this.getAttribute("data-index") || "0"); - if (-1 !== currentIndex) { - this.selectedTabIndex = currentIndex; - var tabButtons = this.querySelectorAll("." + buttonClass), - newTabButton = tabButtons[currentIndex]; - newTabButton && setActiveTabButton(this, newTabButton, current, !1) - } - this.readyFired || (this.readyFired = !0, this.dispatchEvent(new CustomEvent("ready", {}))) - }, EmbyTabs.detachedCallback = function() { - this.scroller && (this.scroller.destroy(), this.scroller = null), dom.removeEventListener(this, "click", onClick, { - passive: !0 - }), dom.removeEventListener(this, "focus", onFocus, { - passive: !0, - capture: !0 - }) - }, EmbyTabs.selectedIndex = function(selected, triggerEvent) { + EmbyTabs.selectedIndex = function (selected, triggerEvent) { + var tabs = this; - if (null == selected) return tabs.selectedTabIndex || 0; + + if (selected == null) { + + return tabs.selectedTabIndex || 0; + } + var current = tabs.selectedIndex(); + tabs.selectedTabIndex = selected; - var tabButtons = tabs.querySelectorAll("." + buttonClass); - if (current === selected || !1 === triggerEvent) { - triggerBeforeTabChange(tabs, selected, current), tabs.dispatchEvent(new CustomEvent("tabchange", { + + var tabButtons = tabs.querySelectorAll('.' + buttonClass); + + if (current === selected || triggerEvent === false) { + + triggerBeforeTabChange(tabs, selected, current); + + tabs.dispatchEvent(new CustomEvent("tabchange", { detail: { selectedTabIndex: selected } })); + var currentTabButton = tabButtons[current]; - setActiveTabButton(tabs, tabButtons[selected], currentTabButton, !1), current !== selected && currentTabButton && currentTabButton.classList.remove(activeButtonClass) - } else onClick.call(tabs, { - target: tabButtons[selected] - }) - }, EmbyTabs.selectNext = function() { - var current = getSelectedTabButton(this), - sibling = getSibling(current, "nextSibling"); - sibling && onClick.call(this, { - target: sibling - }) - }, EmbyTabs.selectPrevious = function() { - var current = getSelectedTabButton(this), - sibling = getSibling(current, "previousSibling"); - sibling && onClick.call(this, { - target: sibling - }) - }, EmbyTabs.triggerBeforeTabChange = function(selected) { + setActiveTabButton(tabs, tabButtons[selected], currentTabButton, false); + + if (current !== selected && currentTabButton) { + currentTabButton.classList.remove(activeButtonClass); + } + + } else { + + onClick.call(tabs, { + target: tabButtons[selected] + }); + //tabButtons[selected].click(); + } + }; + + function getSibling(elem, method) { + + var sibling = elem[method]; + + while (sibling) { + if (sibling.classList.contains(buttonClass)) { + + if (!sibling.classList.contains('hide')) { + return sibling; + } + } + + sibling = sibling[method]; + } + + return null; + } + + EmbyTabs.selectNext = function () { + + var current = getSelectedTabButton(this); + + var sibling = getSibling(current, 'nextSibling'); + + if (sibling) { + onClick.call(this, { + target: sibling + }); + } + }; + + EmbyTabs.selectPrevious = function () { + + var current = getSelectedTabButton(this); + + var sibling = getSibling(current, 'previousSibling'); + + if (sibling) { + onClick.call(this, { + target: sibling + }); + } + }; + + EmbyTabs.triggerBeforeTabChange = function (selected) { + var tabs = this; - triggerBeforeTabChange(tabs, tabs.selectedIndex()) - }, EmbyTabs.triggerTabChange = function(selected) { + + triggerBeforeTabChange(tabs, tabs.selectedIndex()); + }; + + EmbyTabs.triggerTabChange = function (selected) { + var tabs = this; + tabs.dispatchEvent(new CustomEvent("tabchange", { detail: { selectedTabIndex: tabs.selectedIndex() } - })) - }, EmbyTabs.setTabEnabled = function(index, enabled) { + })); + }; + + EmbyTabs.setTabEnabled = function (index, enabled) { + + var tabs = this; var btn = this.querySelector('.emby-tab-button[data-index="' + index + '"]'); - enabled ? btn.classList.remove("hide") : btn.classList.remove("add") - }, document.registerElement("emby-tabs", { + + if (enabled) { + btn.classList.remove('hide'); + } else { + btn.classList.remove('add'); + } + }; + + document.registerElement('emby-tabs', { prototype: EmbyTabs, - extends: "div" - }) + extends: 'div' + }); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/emby-textarea/emby-textarea.css b/src/bower_components/emby-webcomponents/emby-textarea/emby-textarea.css index 18313cffd1..6ac8262236 100644 --- a/src/bower_components/emby-webcomponents/emby-textarea/emby-textarea.css +++ b/src/bower_components/emby-webcomponents/emby-textarea/emby-textarea.css @@ -2,30 +2,31 @@ display: block; margin: 0; margin-bottom: 0 !important; + /* Remove select styling */ + /* Font size must the 16px or larger to prevent iOS page zoom on focus */ font-size: inherit; + /* General select styles: change as needed */ font-family: inherit; font-weight: inherit; color: inherit; padding: .35em .25em; - -webkit-box-sizing: border-box; + /* Prevent padding from causing width overflow */ box-sizing: border-box; - outline: 0 !important; - -webkit-tap-highlight-color: transparent; - width: 100% + outline: none !important; + -webkit-tap-highlight-color: rgba(0,0,0,0); + width: 100%; } -.emby-textarea::-moz-focus-inner { - border: 0 -} + .emby-textarea::-moz-focus-inner { + border: 0; + } .textareaLabel { display: inline-block; - -webkit-transition: all .2s ease-out; - -o-transition: all .2s ease-out; transition: all .2s ease-out; - margin-bottom: .25em + margin-bottom: .25em; } -.emby-textarea+.fieldDescription { - margin-top: .25em -} \ No newline at end of file +.emby-textarea + .fieldDescription { + margin-top: .25em; +} diff --git a/src/bower_components/emby-webcomponents/emby-textarea/emby-textarea.js b/src/bower_components/emby-webcomponents/emby-textarea/emby-textarea.js index f5725c2301..130d6c3980 100644 --- a/src/bower_components/emby-webcomponents/emby-textarea/emby-textarea.js +++ b/src/bower_components/emby-webcomponents/emby-textarea/emby-textarea.js @@ -1,55 +1,138 @@ -define(["layoutManager", "browser", "css!./emby-textarea", "registerElement", "emby-input"], function(layoutManager, browser) { - "use strict"; +define(['layoutManager', 'browser', 'css!./emby-textarea', 'registerElement', 'emby-input'], function (layoutManager, browser) { + 'use strict'; function autoGrow(textarea, maxLines) { + var self = this; + + if (maxLines === undefined) { + maxLines = 999; + } + + /** + * Calculates the vertical padding of the element + * @param textarea + * @returns {number} + */ + self.getOffset = function (textarea) { + var style = window.getComputedStyle(textarea, null), + props = ['paddingTop', 'paddingBottom'], + offset = 0; + + for (var i = 0; i < props.length; i++) { + offset += parseInt(style[props[i]]); + } + return offset; + }; + + var offset; function reset() { - textarea.rows = 1, offset = self.getOffset(textarea), self.rows = textarea.rows || 1, self.lineHeight = textarea.scrollHeight / self.rows - offset / self.rows, self.maxAllowedHeight = self.lineHeight * maxLines - offset + textarea.rows = 1; + offset = self.getOffset(textarea); + self.rows = textarea.rows || 1; + self.lineHeight = (textarea.scrollHeight / self.rows) - (offset / self.rows); + self.maxAllowedHeight = (self.lineHeight * maxLines) - offset; } function autogrowFn() { - if ((!self.lineHeight || self.lineHeight <= 0) && reset(), self.lineHeight <= 0) return textarea.style.overflowY = "scroll", textarea.style.height = "auto", void(textarea.rows = 3); - var newHeight = 0; - textarea.scrollHeight - offset > self.maxAllowedHeight ? (textarea.style.overflowY = "scroll", newHeight = self.maxAllowedHeight) : (textarea.style.overflowY = "hidden", textarea.style.height = "auto", newHeight = textarea.scrollHeight), textarea.style.height = newHeight + "px" + if (!self.lineHeight || self.lineHeight <= 0) { + reset(); + } + if (self.lineHeight <= 0) { + textarea.style.overflowY = 'scroll'; + textarea.style.height = 'auto'; + textarea.rows = 3; + return; + } + var newHeight = 0, hasGrown = false; + + if ((textarea.scrollHeight - offset) > self.maxAllowedHeight) { + textarea.style.overflowY = 'scroll'; + newHeight = self.maxAllowedHeight; + } + else { + textarea.style.overflowY = 'hidden'; + textarea.style.height = 'auto'; + newHeight = textarea.scrollHeight/* - offset*/; + hasGrown = true; + } + textarea.style.height = newHeight + 'px'; } - var self = this; - void 0 === maxLines && (maxLines = 999), self.getOffset = function(textarea) { - for (var style = window.getComputedStyle(textarea, null), props = ["paddingTop", "paddingBottom"], offset = 0, i = 0; i < props.length; i++) offset += parseInt(style[props[i]]); - return offset - }; - var offset; - textarea.addEventListener("input", autogrowFn), textarea.addEventListener("focus", autogrowFn), textarea.addEventListener("valueset", autogrowFn), autogrowFn() + + // Call autogrowFn() when textarea's value is changed + textarea.addEventListener('input', autogrowFn); + textarea.addEventListener('focus', autogrowFn); + textarea.addEventListener('valueset', autogrowFn); + + autogrowFn(); } - var EmbyTextAreaPrototype = Object.create(HTMLTextAreaElement.prototype), - elementId = 0; + + var EmbyTextAreaPrototype = Object.create(HTMLTextAreaElement.prototype); + + var elementId = 0; + if (Object.getOwnPropertyDescriptor && Object.defineProperty) { - var descriptor = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "value"); + + var descriptor = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value'); + + // descriptor returning null in webos if (descriptor && descriptor.configurable) { var baseSetMethod = descriptor.set; - descriptor.set = function(value) { - baseSetMethod.call(this, value), this.dispatchEvent(new CustomEvent("valueset", { - bubbles: !1, - cancelable: !1 - })) - }, Object.defineProperty(HTMLTextAreaElement.prototype, "value", descriptor) + descriptor.set = function (value) { + baseSetMethod.call(this, value); + + this.dispatchEvent(new CustomEvent('valueset', { + bubbles: false, + cancelable: false + })); + }; + + Object.defineProperty(HTMLTextAreaElement.prototype, 'value', descriptor); } } - EmbyTextAreaPrototype.createdCallback = function() { - this.id || (this.id = "embytextarea" + elementId, elementId++) - }, EmbyTextAreaPrototype.attachedCallback = function() { - if (!this.classList.contains("emby-textarea")) { - this.rows = 1, this.classList.add("emby-textarea"); - var parentNode = this.parentNode, - label = this.ownerDocument.createElement("label"); - label.innerHTML = this.getAttribute("label") || "", label.classList.add("textareaLabel"), label.htmlFor = this.id, parentNode.insertBefore(label, this), this.addEventListener("focus", function() { - label.classList.add("textareaLabelFocused"), label.classList.remove("textareaLabelUnfocused") - }), this.addEventListener("blur", function() { - label.classList.remove("textareaLabelFocused"), label.classList.add("textareaLabelUnfocused") - }), this.label = function(text) { - label.innerHTML = text - }, new autoGrow(this) + + EmbyTextAreaPrototype.createdCallback = function () { + + if (!this.id) { + this.id = 'embytextarea' + elementId; + elementId++; } - }, document.registerElement("emby-textarea", { + }; + + EmbyTextAreaPrototype.attachedCallback = function () { + + if (this.classList.contains('emby-textarea')) { + return; + } + + this.rows = 1; + this.classList.add('emby-textarea'); + + var parentNode = this.parentNode; + var label = this.ownerDocument.createElement('label'); + label.innerHTML = this.getAttribute('label') || ''; + label.classList.add('textareaLabel'); + + label.htmlFor = this.id; + parentNode.insertBefore(label, this); + + this.addEventListener('focus', function () { + label.classList.add('textareaLabelFocused'); + label.classList.remove('textareaLabelUnfocused'); + }); + this.addEventListener('blur', function () { + label.classList.remove('textareaLabelFocused'); + label.classList.add('textareaLabelUnfocused'); + }); + + this.label = function (text) { + label.innerHTML = text; + }; + + new autoGrow(this); + }; + + document.registerElement('emby-textarea', { prototype: EmbyTextAreaPrototype, - extends: "textarea" - }) + extends: 'textarea' + }); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/emby-toggle/emby-toggle.css b/src/bower_components/emby-webcomponents/emby-toggle/emby-toggle.css index c03115e976..fc3d04ebc9 100644 --- a/src/bower_components/emby-webcomponents/emby-toggle/emby-toggle.css +++ b/src/bower_components/emby-webcomponents/emby-toggle/emby-toggle.css @@ -2,13 +2,8 @@ position: relative; z-index: 1; vertical-align: middle; - display: -webkit-inline-box; - display: -webkit-inline-flex; display: inline-flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - -webkit-box-sizing: border-box; box-sizing: border-box; width: 100%; margin: 0; @@ -19,17 +14,12 @@ -moz-user-select: none; -ms-user-select: none; user-select: none; - -webkit-box-orient: horizontal; - -webkit-box-direction: reverse; - -webkit-flex-direction: row-reverse; flex-direction: row-reverse; - -webkit-box-pack: end; - -webkit-justify-content: flex-end; - justify-content: flex-end + justify-content: flex-end; } .toggleContainer { - margin-bottom: 1.8em + margin-bottom: 1.8em; } .mdl-switch__input { @@ -42,29 +32,28 @@ -moz-appearance: none; -webkit-appearance: none; appearance: none; - border: none + border: none; } .mdl-switch__trackContainer { position: relative; - width: 2.9em + width: 2.9em; } .mdl-switch__track { - background: rgba(0, 0, 0, .2); + background: rgba(0,0,0, 0.2); height: 1em; - -webkit-border-radius: 1em; border-radius: 1em; - cursor: pointer + cursor: pointer; } -.mdl-switch__input:checked+.mdl-switch__label+.mdl-switch__trackContainer>.mdl-switch__track { - background: rgba(0,164,220, .5) +.mdl-switch__input:checked + .mdl-switch__label + .mdl-switch__trackContainer > .mdl-switch__track { + background: rgba(0, 164, 220, 0.5); } -.mdl-switch__input[disabled]+.mdl-switch__label+.mdl-switch__trackContainer>.mdl-switch__track { - background: rgba(0, 0, 0, .12); - cursor: auto +.mdl-switch__input[disabled] + .mdl-switch__label + .mdl-switch__trackContainer > .mdl-switch__track { + background: rgba(0,0,0, 0.12); + cursor: auto; } .mdl-switch__thumb { @@ -74,41 +63,26 @@ top: -.25em; height: 1.44em; width: 1.44em; - -webkit-border-radius: 50%; border-radius: 50%; cursor: pointer; - -webkit-box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12); - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12); - -webkit-transition-duration: .28s; - -o-transition-duration: .28s; - transition-duration: .28s; - -webkit-transition-timing-function: cubic-bezier(.4, 0, .2, 1); - -o-transition-timing-function: cubic-bezier(.4, 0, .2, 1); - transition-timing-function: cubic-bezier(.4, 0, .2, 1); - -webkit-transition-property: left; - -o-transition-property: left; + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); + transition-duration: 0.28s; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-property: left; - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; - justify-content: center + justify-content: center; } -.mdl-switch__input:checked+.mdl-switch__label+.mdl-switch__trackContainer>.mdl-switch__thumb { +.mdl-switch__input:checked + .mdl-switch__label + .mdl-switch__trackContainer > .mdl-switch__thumb { background: #00a4dc; left: 1.466em; - -webkit-box-shadow: 0 3px .28em 0 rgba(0, 0, 0, .14), 0 3px 3px -2px rgba(0, 0, 0, .2), 0 1px .56em 0 rgba(0, 0, 0, .12); - box-shadow: 0 3px .28em 0 rgba(0, 0, 0, .14), 0 3px 3px -2px rgba(0, 0, 0, .2), 0 1px .56em 0 rgba(0, 0, 0, .12) + box-shadow: 0 3px 0.28em 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.2), 0 1px .56em 0 rgba(0, 0, 0, 0.12); } -.mdl-switch__input[disabled]+.mdl-switch__label+.mdl-switch__trackContainer>.mdl-switch__thumb { - background: #bdbdbd; - cursor: auto +.mdl-switch__input[disabled] + .mdl-switch__label + .mdl-switch__trackContainer > .mdl-switch__thumb { + background: rgb(189,189,189); + cursor: auto; } .mdl-switch__focus-helper { @@ -118,38 +92,31 @@ -webkit-transform: translate(-50%, -50%); transform: translate(-50%, -50%); display: inline-block; - -webkit-box-sizing: border-box; box-sizing: border-box; width: .6em; height: .6em; - -webkit-border-radius: 50%; border-radius: 50%; - background-color: transparent + background-color: transparent; } -.mdl-switch__input:focus+.mdl-switch__label+.mdl-switch__trackContainer .mdl-switch__focus-helper { - -webkit-box-shadow: 0 0 0 1.39em rgba(0, 0, 0, .05); - box-shadow: 0 0 0 1.39em rgba(0, 0, 0, .05) +.mdl-switch__input:focus + .mdl-switch__label + .mdl-switch__trackContainer .mdl-switch__focus-helper { + box-shadow: 0 0 0 1.39em rgba(0, 0, 0, .05); } -.mdl-switch__input:checked:focus+.mdl-switch__label+.mdl-switch__trackContainer .mdl-switch__focus-helper { - -webkit-box-shadow: 0 0 0 1.39em rgba(0,164,220, .26); - box-shadow: 0 0 0 1.39em rgba(0,164,220, .26); - background-color: rgba(0,164,220, .26) +.mdl-switch__input:checked:focus + .mdl-switch__label + .mdl-switch__trackContainer .mdl-switch__focus-helper { + box-shadow: 0 0 0 1.39em rgba(0, 164, 220, 0.26); + background-color: rgba(0, 164, 220, 0.26); } .mdl-switch__label { cursor: pointer; - margin: 0 0 0 .7em; - display: -webkit-inline-box; - display: -webkit-inline-flex; + margin: 0; display: inline-flex; - -webkit-box-align: center; - -webkit-align-items: center; - align-items: center + align-items: center; + margin-left: .7em; } .mdl-switch__input[disabled] .mdl-switch__label { - color: #bdbdbd; - cursor: auto -} \ No newline at end of file + color: rgb(189,189,189); + cursor: auto; +} diff --git a/src/bower_components/emby-webcomponents/emby-toggle/emby-toggle.js b/src/bower_components/emby-webcomponents/emby-toggle/emby-toggle.js index f62899902d..d6d31957b2 100644 --- a/src/bower_components/emby-webcomponents/emby-toggle/emby-toggle.js +++ b/src/bower_components/emby-webcomponents/emby-toggle/emby-toggle.js @@ -1,22 +1,50 @@ -define(["css!./emby-toggle", "registerElement"], function() { - "use strict"; +define(['css!./emby-toggle', 'registerElement'], function () { + 'use strict'; + + var EmbyTogglePrototype = Object.create(HTMLInputElement.prototype); function onKeyDown(e) { - if (13 === e.keyCode) return e.preventDefault(), this.checked = !this.checked, this.dispatchEvent(new CustomEvent("change", { - bubbles: !0 - })), !1 - } - var EmbyTogglePrototype = Object.create(HTMLInputElement.prototype); - EmbyTogglePrototype.attachedCallback = function() { - if ("true" !== this.getAttribute("data-embytoggle")) { - this.setAttribute("data-embytoggle", "true"), this.classList.add("mdl-switch__input"); - var labelElement = this.parentNode; - labelElement.classList.add("mdl-switch"), labelElement.classList.add("mdl-js-switch"); - var labelTextElement = labelElement.querySelector("span"); - labelElement.insertAdjacentHTML("beforeend", '
'), labelTextElement.classList.add("toggleButtonLabel"), labelTextElement.classList.add("mdl-switch__label"), this.addEventListener("keydown", onKeyDown) + + // Don't submit form on enter + if (e.keyCode === 13) { + e.preventDefault(); + + this.checked = !this.checked; + + this.dispatchEvent(new CustomEvent('change', { + bubbles: true + })); + + return false; } - }, document.registerElement("emby-toggle", { + } + + EmbyTogglePrototype.attachedCallback = function () { + + if (this.getAttribute('data-embytoggle') === 'true') { + return; + } + + this.setAttribute('data-embytoggle', 'true'); + + this.classList.add('mdl-switch__input'); + + var labelElement = this.parentNode; + labelElement.classList.add('mdl-switch'); + labelElement.classList.add('mdl-js-switch'); + + var labelTextElement = labelElement.querySelector('span'); + + labelElement.insertAdjacentHTML('beforeend', '
'); + + labelTextElement.classList.add('toggleButtonLabel'); + labelTextElement.classList.add('mdl-switch__label'); + + this.addEventListener('keydown', onKeyDown); + }; + + document.registerElement('emby-toggle', { prototype: EmbyTogglePrototype, - extends: "input" - }) + extends: 'input' + }); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/fetchhelper.js b/src/bower_components/emby-webcomponents/fetchhelper.js index ed0c74d6c4..64bd5159a5 100644 --- a/src/bower_components/emby-webcomponents/fetchhelper.js +++ b/src/bower_components/emby-webcomponents/fetchhelper.js @@ -1,54 +1,132 @@ -define([], function() { - "use strict"; +define([], function () { + 'use strict'; function getFetchPromise(request) { + var headers = request.headers || {}; - "json" === request.dataType && (headers.accept = "application/json"); + + if (request.dataType === 'json') { + headers.accept = 'application/json'; + } + var fetchRequest = { - headers: headers, - method: request.type, - credentials: "same-origin" - }, - contentType = request.contentType; - request.data && ("string" == typeof request.data ? fetchRequest.body = request.data : (fetchRequest.body = paramsToString(request.data), contentType = contentType || "application/x-www-form-urlencoded; charset=UTF-8")), contentType && (headers["Content-Type"] = contentType); + headers: headers, + method: request.type, + credentials: 'same-origin' + }; + + var contentType = request.contentType; + + if (request.data) { + + if (typeof request.data === 'string') { + fetchRequest.body = request.data; + } else { + fetchRequest.body = paramsToString(request.data); + + contentType = contentType || 'application/x-www-form-urlencoded; charset=UTF-8'; + } + } + + if (contentType) { + + headers['Content-Type'] = contentType; + } + var url = request.url; + if (request.query) { var paramString = paramsToString(request.query); - paramString && (url += "?" + paramString) + if (paramString) { + url += '?' + paramString; + } } - return request.timeout ? fetchWithTimeout(url, fetchRequest, request.timeout) : fetch(url, fetchRequest) + + if (!request.timeout) { + return fetch(url, fetchRequest); + } + + return fetchWithTimeout(url, fetchRequest, request.timeout); } function fetchWithTimeout(url, options, timeoutMs) { - return console.log("fetchWithTimeout: timeoutMs: " + timeoutMs + ", url: " + url), new Promise(function(resolve, reject) { + + console.log('fetchWithTimeout: timeoutMs: ' + timeoutMs + ', url: ' + url); + + return new Promise(function (resolve, reject) { + var timeout = setTimeout(reject, timeoutMs); - options = options || {}, options.credentials = "same-origin", fetch(url, options).then(function(response) { - clearTimeout(timeout), console.log("fetchWithTimeout: succeeded connecting to url: " + url), resolve(response) - }, function(error) { - clearTimeout(timeout), console.log("fetchWithTimeout: timed out connecting to url: " + url), reject() - }) - }) + + options = options || {}; + options.credentials = 'same-origin'; + + fetch(url, options).then(function (response) { + clearTimeout(timeout); + + console.log('fetchWithTimeout: succeeded connecting to url: ' + url); + + resolve(response); + }, function (error) { + + clearTimeout(timeout); + + console.log('fetchWithTimeout: timed out connecting to url: ' + url); + + reject(); + }); + }); } function paramsToString(params) { + var values = []; + for (var key in params) { + var value = params[key]; - null !== value && void 0 !== value && "" !== value && values.push(encodeURIComponent(key) + "=" + encodeURIComponent(value)) + + if (value !== null && value !== undefined && value !== '') { + values.push(encodeURIComponent(key) + "=" + encodeURIComponent(value)); + } } - return values.join("&") + return values.join('&'); } function ajax(request) { - if (!request) throw new Error("Request cannot be null"); - return request.headers = request.headers || {}, console.log("requesting url: " + request.url), getFetchPromise(request).then(function(response) { - return console.log("response status: " + response.status + ", url: " + request.url), response.status < 400 ? "json" === request.dataType || "application/json" === request.headers.accept ? response.json() : "text" === request.dataType || 0 === (response.headers.get("Content-Type") || "").toLowerCase().indexOf("text/") ? response.text() : response : Promise.reject(response) - }, function(err) { - throw console.log("request failed to url: " + request.url), err - }) + + if (!request) { + throw new Error("Request cannot be null"); + } + + request.headers = request.headers || {}; + + console.log('requesting url: ' + request.url); + + return getFetchPromise(request).then(function (response) { + + console.log('response status: ' + response.status + ', url: ' + request.url); + + if (response.status < 400) { + + if (request.dataType === 'json' || request.headers.accept === 'application/json') { + return response.json(); + } else if (request.dataType === 'text' || (response.headers.get('Content-Type') || '').toLowerCase().indexOf('text/') === 0) { + return response.text(); + } else { + return response; + } + } else { + return Promise.reject(response); + } + + }, function (err) { + + console.log('request failed to url: ' + request.url); + throw err; + }); } return { getFetchPromise: getFetchPromise, ajax: ajax - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/filedownloader.js b/src/bower_components/emby-webcomponents/filedownloader.js index ea7a1ff99f..ebd004da68 100644 --- a/src/bower_components/emby-webcomponents/filedownloader.js +++ b/src/bower_components/emby-webcomponents/filedownloader.js @@ -1,10 +1,12 @@ -define(["multi-download"], function(multiDownload) { - "use strict"; +define(['multi-download'], function (multiDownload) { + 'use strict'; + return { - download: function(items) { - multiDownload(items.map(function(item) { - return item.url - })) + download: function (items) { + + multiDownload(items.map(function (item) { + return item.url; + })); } - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/filesystem.js b/src/bower_components/emby-webcomponents/filesystem.js index 7f9599cc14..4489d2921f 100644 --- a/src/bower_components/emby-webcomponents/filesystem.js +++ b/src/bower_components/emby-webcomponents/filesystem.js @@ -1,11 +1,12 @@ -define([], function() { - "use strict"; +define([], function () { + 'use strict'; + return { - fileExists: function(path) { - return Promise.reject() + fileExists: function (path) { + return Promise.reject(); }, - directoryExists: function(path) { - return Promise.reject() + directoryExists: function (path) { + return Promise.reject(); } - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/filtermenu/filtermenu.js b/src/bower_components/emby-webcomponents/filtermenu/filtermenu.js index e0fe446ebe..e405cc0943 100644 --- a/src/bower_components/emby-webcomponents/filtermenu/filtermenu.js +++ b/src/bower_components/emby-webcomponents/filtermenu/filtermenu.js @@ -1,125 +1,348 @@ -define(["require", "dom", "focusManager", "dialogHelper", "loading", "apphost", "inputManager", "layoutManager", "connectionManager", "appRouter", "globalize", "userSettings", "emby-checkbox", "emby-input", "paper-icon-button-light", "emby-select", "material-icons", "css!./../formdialog", "emby-button", "emby-linkbutton", "flexStyles"], function(require, dom, focusManager, dialogHelper, loading, appHost, inputManager, layoutManager, connectionManager, appRouter, globalize, userSettings) { - "use strict"; +define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'apphost', 'inputManager', 'layoutManager', 'connectionManager', 'appRouter', 'globalize', 'userSettings', 'emby-checkbox', 'emby-input', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button', 'emby-linkbutton', 'flexStyles'], function (require, dom, focusManager, dialogHelper, loading, appHost, inputManager, layoutManager, connectionManager, appRouter, globalize, userSettings) { + 'use strict'; function onSubmit(e) { - return e.preventDefault(), !1 + + e.preventDefault(); + return false; } function renderOptions(context, selector, cssClass, items, isCheckedFn) { + var elem = context.querySelector(selector); - items.length ? elem.classList.remove("hide") : elem.classList.add("hide"); - var html = ""; - html += items.map(function(filter) { - var itemHtml = "", - checkedHtml = isCheckedFn(filter) ? " checked" : ""; - return itemHtml += "" - }).join(""), elem.querySelector(".filterOptions").innerHTML = html + + if (items.length) { + + elem.classList.remove('hide'); + + } else { + elem.classList.add('hide'); + } + + var html = ''; + + html += items.map(function (filter) { + + var itemHtml = ''; + + var checkedHtml = isCheckedFn(filter) ? ' checked' : ''; + itemHtml += ''; + + return itemHtml; + + }).join(''); + + elem.querySelector('.filterOptions').innerHTML = html; } function renderDynamicFilters(context, result, options) { - renderOptions(context, ".genreFilters", "chkGenreFilter", result.Genres, function(i) { - var delimeter = -1 === (options.settings.GenreIds || "").indexOf("|") ? "," : "|"; - return -1 !== (delimeter + (options.settings.GenreIds || "") + delimeter).indexOf(delimeter + i.Id + delimeter) - }) + + // If there's a huge number of these they will be really show to render + //if (result.Tags) { + // result.Tags.length = Math.min(result.Tags.length, 50); + //} + + renderOptions(context, '.genreFilters', 'chkGenreFilter', result.Genres, function (i) { + + // Switching from | to , + var delimeter = (options.settings.GenreIds || '').indexOf('|') === -1 ? ',' : '|'; + return (delimeter + (options.settings.GenreIds || '') + delimeter).indexOf(delimeter + i.Id + delimeter) !== -1; + }); + + //renderOptions(context, '.officialRatingFilters', 'chkOfficialRatingFilter', result.OfficialRatings, function (i) { + // var delimeter = '|'; + // return (delimeter + (query.OfficialRatings || '') + delimeter).indexOf(delimeter + i + delimeter) != -1; + //}); + + //renderOptions(context, '.tagFilters', 'chkTagFilter', result.Tags, function (i) { + // var delimeter = '|'; + // return (delimeter + (query.Tags || '') + delimeter).indexOf(delimeter + i + delimeter) != -1; + //}); + + //renderOptions(context, '.yearFilters', 'chkYearFilter', result.Years, function (i) { + + // var delimeter = ','; + // return (delimeter + (query.Years || '') + delimeter).indexOf(delimeter + i + delimeter) != -1; + //}); } function loadDynamicFilters(context, options) { - var apiClient = connectionManager.getApiClient(options.serverId), - filterMenuOptions = Object.assign(options.filterMenuOptions, { - UserId: apiClient.getCurrentUserId(), - ParentId: options.parentId, - IncludeItemTypes: options.itemTypes.join(",") - }); - apiClient.getFilters(filterMenuOptions).then(function(result) { - renderDynamicFilters(context, result, options) - }, function() {}) + + var apiClient = connectionManager.getApiClient(options.serverId); + + var filterMenuOptions = Object.assign(options.filterMenuOptions, { + + UserId: apiClient.getCurrentUserId(), + ParentId: options.parentId, + IncludeItemTypes: options.itemTypes.join(',') + }); + + apiClient.getFilters(filterMenuOptions).then(function (result) { + + renderDynamicFilters(context, result, options); + }, function () { + + // older server + }); } function initEditor(context, settings) { - context.querySelector("form").addEventListener("submit", onSubmit); - var i, length, elems = context.querySelectorAll(".simpleFilter"); - for (i = 0, length = elems.length; i < length; i++) "INPUT" === elems[i].tagName ? elems[i].checked = settings[elems[i].getAttribute("data-settingname")] || !1 : elems[i].querySelector("input").checked = settings[elems[i].getAttribute("data-settingname")] || !1; - var videoTypes = settings.VideoTypes ? settings.VideoTypes.split(",") : []; - for (elems = context.querySelectorAll(".chkVideoTypeFilter"), i = 0, length = elems.length; i < length; i++) elems[i].checked = -1 !== videoTypes.indexOf(elems[i].getAttribute("data-filter")); - var seriesStatuses = settings.SeriesStatus ? settings.SeriesStatus.split(",") : []; - for (elems = context.querySelectorAll(".chkSeriesStatus"), i = 0, length = elems.length; i < length; i++) elems[i].checked = -1 !== seriesStatuses.indexOf(elems[i].getAttribute("data-filter")); - context.querySelector(".basicFilterSection .viewSetting:not(.hide)") ? context.querySelector(".basicFilterSection").classList.remove("hide") : context.querySelector(".basicFilterSection").classList.add("hide"), context.querySelector(".featureSection .viewSetting:not(.hide)") ? context.querySelector(".featureSection").classList.remove("hide") : context.querySelector(".featureSection").classList.add("hide") + + context.querySelector('form').addEventListener('submit', onSubmit); + + var elems = context.querySelectorAll('.simpleFilter'); + var i, length; + + for (i = 0, length = elems.length; i < length; i++) { + + if (elems[i].tagName === 'INPUT') { + elems[i].checked = settings[elems[i].getAttribute('data-settingname')] || false; + } else { + elems[i].querySelector('input').checked = settings[elems[i].getAttribute('data-settingname')] || false; + } + } + + var videoTypes = settings.VideoTypes ? settings.VideoTypes.split(',') : []; + elems = context.querySelectorAll('.chkVideoTypeFilter'); + + for (i = 0, length = elems.length; i < length; i++) { + + elems[i].checked = videoTypes.indexOf(elems[i].getAttribute('data-filter')) !== -1; + } + + var seriesStatuses = settings.SeriesStatus ? settings.SeriesStatus.split(',') : []; + elems = context.querySelectorAll('.chkSeriesStatus'); + + for (i = 0, length = elems.length; i < length; i++) { + + elems[i].checked = seriesStatuses.indexOf(elems[i].getAttribute('data-filter')) !== -1; + } + + if (context.querySelector('.basicFilterSection .viewSetting:not(.hide)')) { + context.querySelector('.basicFilterSection').classList.remove('hide'); + } else { + context.querySelector('.basicFilterSection').classList.add('hide'); + } + + if (context.querySelector('.featureSection .viewSetting:not(.hide)')) { + context.querySelector('.featureSection').classList.remove('hide'); + } else { + context.querySelector('.featureSection').classList.add('hide'); + } } function saveValues(context, settings, settingsKey) { - var i, length, elems = context.querySelectorAll(".simpleFilter"); - for (i = 0, length = elems.length; i < length; i++) "INPUT" === elems[i].tagName ? setBasicFilter(context, settingsKey + "-filter-" + elems[i].getAttribute("data-settingname"), elems[i]) : setBasicFilter(context, settingsKey + "-filter-" + elems[i].getAttribute("data-settingname"), elems[i].querySelector("input")); + + var elems = context.querySelectorAll('.simpleFilter'); + var i, length; + for (i = 0, length = elems.length; i < length; i++) { + + if (elems[i].tagName === 'INPUT') { + setBasicFilter(context, settingsKey + '-filter-' + elems[i].getAttribute('data-settingname'), elems[i]); + } else { + setBasicFilter(context, settingsKey + '-filter-' + elems[i].getAttribute('data-settingname'), elems[i].querySelector('input')); + } + } + + // Video type var videoTypes = []; - for (elems = context.querySelectorAll(".chkVideoTypeFilter"), i = 0, length = elems.length; i < length; i++) elems[i].checked && videoTypes.push(elems[i].getAttribute("data-filter")); - userSettings.setFilter(settingsKey + "-filter-VideoTypes", videoTypes.join(",")); + elems = context.querySelectorAll('.chkVideoTypeFilter'); + + for (i = 0, length = elems.length; i < length; i++) { + + if (elems[i].checked) { + videoTypes.push(elems[i].getAttribute('data-filter')); + } + } + userSettings.setFilter(settingsKey + '-filter-VideoTypes', videoTypes.join(',')); + + // Series status var seriesStatuses = []; - for (elems = context.querySelectorAll(".chkSeriesStatus"), i = 0, length = elems.length; i < length; i++) elems[i].checked && seriesStatuses.push(elems[i].getAttribute("data-filter")); + elems = context.querySelectorAll('.chkSeriesStatus'); + + for (i = 0, length = elems.length; i < length; i++) { + + if (elems[i].checked) { + seriesStatuses.push(elems[i].getAttribute('data-filter')); + } + } + + // Genres var genres = []; - for (elems = context.querySelectorAll(".chkGenreFilter"), i = 0, length = elems.length; i < length; i++) elems[i].checked && genres.push(elems[i].getAttribute("data-filter")); - userSettings.setFilter(settingsKey + "-filter-GenreIds", genres.join(",")) + elems = context.querySelectorAll('.chkGenreFilter'); + + for (i = 0, length = elems.length; i < length; i++) { + + if (elems[i].checked) { + genres.push(elems[i].getAttribute('data-filter')); + } + } + userSettings.setFilter(settingsKey + '-filter-GenreIds', genres.join(',')); } function setBasicFilter(context, key, elem) { + var value = elem.checked; - value = value || null, userSettings.setFilter(key, value) + value = value ? value : null; + userSettings.setFilter(key, value); } function centerFocus(elem, horiz, on) { - require(["scrollHelper"], function(scrollHelper) { - var fn = on ? "on" : "off"; - scrollHelper.centerFocus[fn](elem, horiz) - }) + require(['scrollHelper'], function (scrollHelper) { + var fn = on ? 'on' : 'off'; + scrollHelper.centerFocus[fn](elem, horiz); + }); } function moveCheckboxFocus(elem, offset) { - for (var parent = dom.parentWithClass(elem, "checkboxList-verticalwrap"), elems = focusManager.getFocusableElements(parent), index = -1, i = 0, length = elems.length; i < length; i++) + + var parent = dom.parentWithClass(elem, 'checkboxList-verticalwrap'); + var elems = focusManager.getFocusableElements(parent); + + var index = -1; + for (var i = 0, length = elems.length; i < length; i++) { if (elems[i] === elem) { index = i; - break - } index += offset, index = Math.min(elems.length - 1, index), index = Math.max(0, index); + break; + } + } + + index += offset; + + index = Math.min(elems.length - 1, index); + index = Math.max(0, index); + var newElem = elems[index]; - newElem && focusManager.focus(newElem) + if (newElem) { + focusManager.focus(newElem); + } } function onInputCommand(e) { switch (e.detail.command) { - case "left": - moveCheckboxFocus(e.target, -1), e.preventDefault(); + + case 'left': + moveCheckboxFocus(e.target, -1); + e.preventDefault(); + break; + case 'right': + moveCheckboxFocus(e.target, 1); + e.preventDefault(); + break; + default: break; - case "right": - moveCheckboxFocus(e.target, 1), e.preventDefault() } } - function FilterMenu() {} + function FilterMenu() { + + } function bindCheckboxInput(context, on) { - for (var elems = context.querySelectorAll(".checkboxList-verticalwrap"), i = 0, length = elems.length; i < length; i++) on ? inputManager.on(elems[i], onInputCommand) : inputManager.off(elems[i], onInputCommand) + + var elems = context.querySelectorAll('.checkboxList-verticalwrap'); + for (var i = 0, length = elems.length; i < length; i++) { + if (on) { + inputManager.on(elems[i], onInputCommand); + } else { + inputManager.off(elems[i], onInputCommand); + } + } } - return FilterMenu.prototype.show = function(options) { - return new Promise(function(resolve, reject) { - require(["text!./filtermenu.template.html"], function(template) { + + FilterMenu.prototype.show = function (options) { + + return new Promise(function (resolve, reject) { + + require(['text!./filtermenu.template.html'], function (template) { + var dialogOptions = { - removeOnClose: !0, - scrollY: !1 + removeOnClose: true, + scrollY: false }; - layoutManager.tv ? dialogOptions.size = "fullscreen" : dialogOptions.size = "small"; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'small'; + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"); - var html = ""; - html += '
', html += '', html += '

${Filters}

', html += "
", html += template, dlg.innerHTML = globalize.translateDocument(html, "sharedcomponents"); - for (var settingElements = dlg.querySelectorAll(".viewSetting"), i = 0, length = settingElements.length; i < length; i++) - 1 === options.visibleSettings.indexOf(settingElements[i].getAttribute("data-settingname")) ? settingElements[i].classList.add("hide") : settingElements[i].classList.remove("hide"); - initEditor(dlg, options.settings), loadDynamicFilters(dlg, options), bindCheckboxInput(dlg, !0), dlg.querySelector(".btnCancel").addEventListener("click", function() { - dialogHelper.close(dlg) - }), layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !0); + + dlg.classList.add('formDialog'); + + var html = ''; + + html += '
'; + html += ''; + html += '

${Filters}

'; + + html += '
'; + + html += template; + + dlg.innerHTML = globalize.translateDocument(html, 'sharedcomponents'); + + var settingElements = dlg.querySelectorAll('.viewSetting'); + for (var i = 0, length = settingElements.length; i < length; i++) { + if (options.visibleSettings.indexOf(settingElements[i].getAttribute('data-settingname')) === -1) { + settingElements[i].classList.add('hide'); + } else { + settingElements[i].classList.remove('hide'); + } + } + + initEditor(dlg, options.settings); + loadDynamicFilters(dlg, options); + + bindCheckboxInput(dlg, true); + + dlg.querySelector('.btnCancel').addEventListener('click', function () { + + dialogHelper.close(dlg); + }); + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, true); + } + var submitted; - dlg.querySelector("form").addEventListener("change", function() { - submitted = !0 - }, !0), dialogHelper.open(dlg).then(function() { - if (bindCheckboxInput(dlg, !1), layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !1), submitted) return saveValues(dlg, options.settings, options.settingsKey), void resolve(); - reject() - }) - }) - }) - }, FilterMenu + + dlg.querySelector('form').addEventListener('change', function () { + + submitted = true; + //if (options.onChange) { + // saveValues(dlg, options.settings, options.settingsKey); + // options.onChange(); + //} + + }, true); + + dialogHelper.open(dlg).then(function () { + + bindCheckboxInput(dlg, false); + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, false); + } + + if (submitted) { + + //if (!options.onChange) { + saveValues(dlg, options.settings, options.settingsKey); + resolve(); + //} + return; + } + + reject(); + }); + }); + }); + }; + + return FilterMenu; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/flexstyles.css b/src/bower_components/emby-webcomponents/flexstyles.css index b801609a6f..b35e25d57b 100644 --- a/src/bower_components/emby-webcomponents/flexstyles.css +++ b/src/bower_components/emby-webcomponents/flexstyles.css @@ -1,70 +1,47 @@ .flex { - display: -webkit-box; - display: -webkit-flex; - display: flex + display: flex; } .inline-flex { - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: inline-flex + display: inline-flex; } .flex-direction-column { - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -webkit-flex-direction: column; - flex-direction: column + flex-direction: column; } .flex-direction-row { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -webkit-flex-direction: row; - flex-direction: row + flex-direction: row; } .flex-grow { - -webkit-box-flex: 1; - -webkit-flex-grow: 1; - flex-grow: 1 + flex-grow: 1; } .flex-shrink-zero { - -webkit-flex-shrink: 0; - flex-shrink: 0 + flex-shrink: 0; } .align-items-center { - -webkit-box-align: center; - -webkit-align-items: center; - align-items: center + align-items: center; } .align-items-flex-start { - -webkit-box-align: start; - -webkit-align-items: flex-start; - align-items: flex-start + align-items: flex-start; } .justify-content-center { - -webkit-box-pack: center; - -webkit-justify-content: center; - justify-content: center + justify-content: center; } .justify-content-flex-end { - -webkit-box-pack: end; - -webkit-justify-content: flex-end; - justify-content: flex-end + justify-content: flex-end; } .flex-wrap-wrap { - -webkit-flex-wrap: wrap; - flex-wrap: wrap + flex-wrap: wrap; } .align-self-flex-end { - -webkit-align-self: flex-end; - align-self: flex-end + align-self: flex-end; } \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/flvjs/flv.min.js b/src/bower_components/emby-webcomponents/flvjs/flv.min.js index bc71e64626..4a3efed93f 100644 --- a/src/bower_components/emby-webcomponents/flvjs/flv.min.js +++ b/src/bower_components/emby-webcomponents/flvjs/flv.min.js @@ -1,6277 +1,7 @@ -! function(e) { - if ("object" == typeof exports && "undefined" != typeof module) module.exports = e(); - else if ("function" == typeof define && define.amd) define([], e); - else { - var t; - t = "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof self ? self : this, t.flvjs = e() - } -}(function() { - var e; - return function e(t, n, i) { - function r(a, o) { - if (!n[a]) { - if (!t[a]) { - var u = "function" == typeof require && require; - if (!o && u) return u(a, !0); - if (s) return s(a, !0); - var l = new Error("Cannot find module '" + a + "'"); - throw l.code = "MODULE_NOT_FOUND", l - } - var d = n[a] = { - exports: {} - }; - t[a][0].call(d.exports, function(e) { - var n = t[a][1][e]; - return r(n || e) - }, d, d.exports, e, t, n, i) - } - return n[a].exports - } - for (var s = "function" == typeof require && require, a = 0; a < i.length; a++) r(i[a]); - return r - }({ - 1: [function(t, n, i) { - (function(r, s) { - ! function(t, r) { - "object" == typeof i && void 0 !== n ? n.exports = r() : "function" == typeof e && e.amd ? e(r) : t.ES6Promise = r() - }(this, function() { - "use strict"; - - function e(e) { - return "function" == typeof e || "object" == typeof e && null !== e - } - - function n(e) { - return "function" == typeof e - } - - function i(e) { - K = e - } - - function a(e) { - q = e - } - - function o() { - return void 0 !== H ? function() { - H(l) - } : u() - } - - function u() { - var e = setTimeout; - return function() { - return e(l, 1) - } - } - - function l() { - for (var e = 0; e < z; e += 2) { - (0, J[e])(J[e + 1]), J[e] = void 0, J[e + 1] = void 0 - } - z = 0 - } - - function d(e, t) { - var n = arguments, - i = this, - r = new this.constructor(f); - void 0 === r[ee] && I(r); - var s = i._state; - return s ? function() { - var e = n[s - 1]; - q(function() { - return O(s, r, e, i._result) - }) - }() : L(i, r, e, t), r - } - - function h(e) { - var t = this; - if (e && "object" == typeof e && e.constructor === t) return e; - var n = new t(f); - return E(n, e), n - } - - function f() {} - - function c() { - return new TypeError("You cannot resolve a promise with itself") - } - - function _() { - return new TypeError("A promises callback cannot return that same promise.") - } - - function m(e) { - try { - return e.then - } catch (e) { - return re.error = e, re - } - } - - function p(e, t, n, i) { - try { - e.call(t, n, i) - } catch (e) { - return e - } - } - - function v(e, t, n) { - q(function(e) { - var i = !1, - r = p(n, t, function(n) { - i || (i = !0, t !== n ? E(e, n) : S(e, n)) - }, function(t) { - i || (i = !0, k(e, t)) - }, "Settle: " + (e._label || " unknown promise")); - !i && r && (i = !0, k(e, r)) - }, e) - } - - function g(e, t) { - t._state === ne ? S(e, t._result) : t._state === ie ? k(e, t._result) : L(t, void 0, function(t) { - return E(e, t) - }, function(t) { - return k(e, t) - }) - } - - function y(e, t, i) { - t.constructor === e.constructor && i === d && t.constructor.resolve === h ? g(e, t) : i === re ? (k(e, re.error), re.error = null) : void 0 === i ? S(e, t) : n(i) ? v(e, t, i) : S(e, t) - } - - function E(t, n) { - t === n ? k(t, c()) : e(n) ? y(t, n, m(n)) : S(t, n) - } - - function b(e) { - e._onerror && e._onerror(e._result), w(e) - } - - function S(e, t) { - e._state === te && (e._result = t, e._state = ne, 0 !== e._subscribers.length && q(w, e)) - } - - function k(e, t) { - e._state === te && (e._state = ie, e._result = t, q(b, e)) - } - - function L(e, t, n, i) { - var r = e._subscribers, - s = r.length; - e._onerror = null, r[s] = t, r[s + ne] = n, r[s + ie] = i, 0 === s && e._state && q(w, e) - } - - function w(e) { - var t = e._subscribers, - n = e._state; - if (0 !== t.length) { - for (var i = void 0, r = void 0, s = e._result, a = 0; a < t.length; a += 3) i = t[a], r = t[a + n], i ? O(n, i, r, s) : r(s); - e._subscribers.length = 0 - } - } - - function R() { - this.error = null - } - - function A(e, t) { - try { - return e(t) - } catch (e) { - return se.error = e, se - } - } - - function O(e, t, i, r) { - var s = n(i), - a = void 0, - o = void 0, - u = void 0, - l = void 0; - if (s) { - if (a = A(i, r), a === se ? (l = !0, o = a.error, a.error = null) : u = !0, t === a) return void k(t, _()) - } else a = r, u = !0; - t._state !== te || (s && u ? E(t, a) : l ? k(t, o) : e === ne ? S(t, a) : e === ie && k(t, a)) - } - - function T(e, t) { - try { - t(function(t) { - E(e, t) - }, function(t) { - k(e, t) - }) - } catch (t) { - k(e, t) - } - } - - function C() { - return ae++ - } - - function I(e) { - e[ee] = ae++, e._state = void 0, e._result = void 0, e._subscribers = [] - } - - function x(e, t) { - this._instanceConstructor = e, this.promise = new e(f), this.promise[ee] || I(this.promise), V(t) ? (this._input = t, this.length = t.length, this._remaining = t.length, this._result = new Array(this.length), 0 === this.length ? S(this.promise, this._result) : (this.length = this.length || 0, this._enumerate(), 0 === this._remaining && S(this.promise, this._result))) : k(this.promise, M()) - } - - function M() { - return new Error("Array Methods must be provided an Array") - } - - function D(e) { - return new x(this, e).promise - } - - function B(e) { - var t = this; - return new t(V(e) ? function(n, i) { - for (var r = e.length, s = 0; s < r; s++) t.resolve(e[s]).then(n, i) - } : function(e, t) { - return t(new TypeError("You must pass an array to race.")) - }) - } - - function j(e) { - var t = this, - n = new t(f); - return k(n, e), n - } - - function P() { - throw new TypeError("You must pass a resolver function as the first argument to the promise constructor") - } - - function U() { - throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.") - } - - function N(e) { - this[ee] = C(), this._result = this._state = void 0, this._subscribers = [], f !== e && ("function" != typeof e && P(), this instanceof N ? T(this, e) : U()) - } - - function F() { - var e = void 0; - if (void 0 !== s) e = s; - else if ("undefined" != typeof self) e = self; - else try { - e = Function("return this")() - } catch (e) { - throw new Error("polyfill failed because global object is unavailable in this environment") - } - var t = e.Promise; - if (t) { - var n = null; - try { - n = Object.prototype.toString.call(t.resolve()) - } catch (e) {} - if ("[object Promise]" === n && !t.cast) return - } - e.Promise = N - } - var G = void 0; - G = Array.isArray ? Array.isArray : function(e) { - return "[object Array]" === Object.prototype.toString.call(e) - }; - var V = G, - z = 0, - H = void 0, - K = void 0, - q = function(e, t) { - J[z] = e, J[z + 1] = t, 2 === (z += 2) && (K ? K(l) : $()) - }, - W = "undefined" != typeof window ? window : void 0, - X = W || {}, - Y = X.MutationObserver || X.WebKitMutationObserver, - Z = "undefined" == typeof self && void 0 !== r && "[object process]" === {}.toString.call(r), - Q = "undefined" != typeof Uint8ClampedArray && "undefined" != typeof importScripts && "undefined" != typeof MessageChannel, - J = new Array(1e3), - $ = void 0; - $ = Z ? function() { - return function() { - return r.nextTick(l) - } - }() : Y ? function() { - var e = 0, - t = new Y(l), - n = document.createTextNode(""); - return t.observe(n, { - characterData: !0 - }), - function() { - n.data = e = ++e % 2 - } - }() : Q ? function() { - var e = new MessageChannel; - return e.port1.onmessage = l, - function() { - return e.port2.postMessage(0) - } - }() : void 0 === W && "function" == typeof t ? function() { - try { - var e = t, - n = e("vertx"); - return H = n.runOnLoop || n.runOnContext, o() - } catch (e) { - return u() - } - }() : u(); - var ee = Math.random().toString(36).substring(16), - te = void 0, - ne = 1, - ie = 2, - re = new R, - se = new R, - ae = 0; - return x.prototype._enumerate = function() { - for (var e = this.length, t = this._input, n = 0; this._state === te && n < e; n++) this._eachEntry(t[n], n) - }, x.prototype._eachEntry = function(e, t) { - var n = this._instanceConstructor, - i = n.resolve; - if (i === h) { - var r = m(e); - if (r === d && e._state !== te) this._settledAt(e._state, t, e._result); - else if ("function" != typeof r) this._remaining--, this._result[t] = e; - else if (n === N) { - var s = new n(f); - y(s, e, r), this._willSettleAt(s, t) - } else this._willSettleAt(new n(function(t) { - return t(e) - }), t) - } else this._willSettleAt(i(e), t) - }, x.prototype._settledAt = function(e, t, n) { - var i = this.promise; - i._state === te && (this._remaining--, e === ie ? k(i, n) : this._result[t] = n), 0 === this._remaining && S(i, this._result) - }, x.prototype._willSettleAt = function(e, t) { - var n = this; - L(e, void 0, function(e) { - return n._settledAt(ne, t, e) - }, function(e) { - return n._settledAt(ie, t, e) - }) - }, N.all = D, N.race = B, N.resolve = h, N.reject = j, N._setScheduler = i, N._setAsap = a, N._asap = q, N.prototype = { - constructor: N, - then: d, - catch: function(e) { - return this.then(null, e) - } - }, N.polyfill = F, N.Promise = N, N - }) - }).call(this, t("_process"), "undefined" != typeof global ? global : "undefined" != typeof self ? self : "undefined" != typeof window ? window : {}) - }, { - _process: 3 - }], - 2: [function(e, t, n) { - function i() { - this._events = this._events || {}, this._maxListeners = this._maxListeners || void 0 - } - - function r(e) { - return "function" == typeof e - } - - function s(e) { - return "number" == typeof e - } - - function a(e) { - return "object" == typeof e && null !== e - } - - function o(e) { - return void 0 === e - } - t.exports = i, i.EventEmitter = i, i.prototype._events = void 0, i.prototype._maxListeners = void 0, i.defaultMaxListeners = 10, i.prototype.setMaxListeners = function(e) { - if (!s(e) || e < 0 || isNaN(e)) throw TypeError("n must be a positive number"); - return this._maxListeners = e, this - }, i.prototype.emit = function(e) { - var t, n, i, s, u, l; - if (this._events || (this._events = {}), "error" === e && (!this._events.error || a(this._events.error) && !this._events.error.length)) { - if ((t = arguments[1]) instanceof Error) throw t; - var d = new Error('Uncaught, unspecified "error" event. (' + t + ")"); - throw d.context = t, d - } - if (n = this._events[e], o(n)) return !1; - if (r(n)) switch (arguments.length) { - case 1: - n.call(this); - break; - case 2: - n.call(this, arguments[1]); - break; - case 3: - n.call(this, arguments[1], arguments[2]); - break; - default: - s = Array.prototype.slice.call(arguments, 1), n.apply(this, s) - } else if (a(n)) - for (s = Array.prototype.slice.call(arguments, 1), l = n.slice(), i = l.length, u = 0; u < i; u++) l[u].apply(this, s); - return !0 - }, i.prototype.addListener = function(e, t) { - var n; - if (!r(t)) throw TypeError("listener must be a function"); - return this._events || (this._events = {}), this._events.newListener && this.emit("newListener", e, r(t.listener) ? t.listener : t), this._events[e] ? a(this._events[e]) ? this._events[e].push(t) : this._events[e] = [this._events[e], t] : this._events[e] = t, a(this._events[e]) && !this._events[e].warned && (n = o(this._maxListeners) ? i.defaultMaxListeners : this._maxListeners) && n > 0 && this._events[e].length > n && (this._events[e].warned = !0, console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.", this._events[e].length), "function" == typeof console.trace && console.trace()), this - }, i.prototype.on = i.prototype.addListener, i.prototype.once = function(e, t) { - function n() { - this.removeListener(e, n), i || (i = !0, t.apply(this, arguments)) - } - if (!r(t)) throw TypeError("listener must be a function"); - var i = !1; - return n.listener = t, this.on(e, n), this - }, i.prototype.removeListener = function(e, t) { - var n, i, s, o; - if (!r(t)) throw TypeError("listener must be a function"); - if (!this._events || !this._events[e]) return this; - if (n = this._events[e], s = n.length, i = -1, n === t || r(n.listener) && n.listener === t) delete this._events[e], this._events.removeListener && this.emit("removeListener", e, t); - else if (a(n)) { - for (o = s; o-- > 0;) - if (n[o] === t || n[o].listener && n[o].listener === t) { - i = o; - break - } if (i < 0) return this; - 1 === n.length ? (n.length = 0, delete this._events[e]) : n.splice(i, 1), this._events.removeListener && this.emit("removeListener", e, t) - } - return this - }, i.prototype.removeAllListeners = function(e) { - var t, n; - if (!this._events) return this; - if (!this._events.removeListener) return 0 === arguments.length ? this._events = {} : this._events[e] && delete this._events[e], this; - if (0 === arguments.length) { - for (t in this._events) "removeListener" !== t && this.removeAllListeners(t); - return this.removeAllListeners("removeListener"), this._events = {}, this - } - if (n = this._events[e], r(n)) this.removeListener(e, n); - else if (n) - for (; n.length;) this.removeListener(e, n[n.length - 1]); - return delete this._events[e], this - }, i.prototype.listeners = function(e) { - return this._events && this._events[e] ? r(this._events[e]) ? [this._events[e]] : this._events[e].slice() : [] - }, i.prototype.listenerCount = function(e) { - if (this._events) { - var t = this._events[e]; - if (r(t)) return 1; - if (t) return t.length - } - return 0 - }, i.listenerCount = function(e, t) { - return e.listenerCount(t) - } - }, {}], - 3: [function(e, t, n) { - function i() { - throw new Error("setTimeout has not been defined") - } - - function r() { - throw new Error("clearTimeout has not been defined") - } - - function s(e) { - if (h === setTimeout) return setTimeout(e, 0); - if ((h === i || !h) && setTimeout) return h = setTimeout, setTimeout(e, 0); - try { - return h(e, 0) - } catch (t) { - try { - return h.call(null, e, 0) - } catch (t) { - return h.call(this, e, 0) - } - } - } - - function a(e) { - if (f === clearTimeout) return clearTimeout(e); - if ((f === r || !f) && clearTimeout) return f = clearTimeout, clearTimeout(e); - try { - return f(e) - } catch (t) { - try { - return f.call(null, e) - } catch (t) { - return f.call(this, e) - } - } - } - - function o() { - p && _ && (p = !1, _.length ? m = _.concat(m) : v = -1, m.length && u()) - } - - function u() { - if (!p) { - var e = s(o); - p = !0; - for (var t = m.length; t;) { - for (_ = m, m = []; ++v < t;) _ && _[v].run(); - v = -1, t = m.length - } - _ = null, p = !1, a(e) - } - } - - function l(e, t) { - this.fun = e, this.array = t - } - - function d() {} - var h, f, c = t.exports = {}; - ! function() { - try { - h = "function" == typeof setTimeout ? setTimeout : i - } catch (e) { - h = i - } - try { - f = "function" == typeof clearTimeout ? clearTimeout : r - } catch (e) { - f = r - } - }(); - var _, m = [], - p = !1, - v = -1; - c.nextTick = function(e) { - var t = new Array(arguments.length - 1); - if (arguments.length > 1) - for (var n = 1; n < arguments.length; n++) t[n - 1] = arguments[n]; - m.push(new l(e, t)), 1 !== m.length || p || s(u) - }, l.prototype.run = function() { - this.fun.apply(null, this.array) - }, c.title = "browser", c.browser = !0, c.env = {}, c.argv = [], c.version = "", c.versions = {}, c.on = d, c.addListener = d, c.once = d, c.off = d, c.removeListener = d, c.removeAllListeners = d, c.emit = d, c.prependListener = d, c.prependOnceListener = d, c.listeners = function(e) { - return [] - }, c.binding = function(e) { - throw new Error("process.binding is not supported") - }, c.cwd = function() { - return "/" - }, c.chdir = function(e) { - throw new Error("process.chdir is not supported") - }, c.umask = function() { - return 0 - } - }, {}], - 4: [function(e, t, n) { - var i = arguments[3], - r = arguments[4], - s = arguments[5], - a = JSON.stringify; - t.exports = function(e, t) { - function n(e) { - p[e] = !0; - for (var t in r[e][1]) { - var i = r[e][1][t]; - p[i] || n(i) - } - } - for (var o, u = Object.keys(s), l = 0, d = u.length; l < d; l++) { - var h = u[l], - f = s[h].exports; - if (f === e || f && f.default === e) { - o = h; - break - } - } - if (!o) { - o = Math.floor(Math.pow(16, 8) * Math.random()).toString(16); - for (var c = {}, l = 0, d = u.length; l < d; l++) { - var h = u[l]; - c[h] = h - } - r[o] = [Function(["require", "module", "exports"], "(" + e + ")(self)"), c] - } - var _ = Math.floor(Math.pow(16, 8) * Math.random()).toString(16), - m = {}; - m[o] = o, r[_] = [Function(["require"], "var f = require(" + a(o) + ");(f.default ? f.default : f)(self);"), m]; - var p = {}; - n(_); - var v = "(" + i + ")({" + Object.keys(p).map(function(e) { - return a(e) + ":[" + r[e][0] + "," + a(r[e][1]) + "]" - }).join(",") + "},{},[" + a(_) + "])", - g = window.URL || window.webkitURL || window.mozURL || window.msURL, - y = new Blob([v], { - type: "text/javascript" - }); - if (t && t.bare) return y; - var E = g.createObjectURL(y), - b = new Worker(E); - return b.objectURL = E, b - } - }, {}], - 5: [function(e, t, n) { - "use strict"; - - function i() { - return Object.assign({}, r) - } - Object.defineProperty(n, "__esModule", { - value: !0 - }), n.createDefaultConfig = i; - var r = n.defaultConfig = { - enableWorker: !1, - enableStashBuffer: !0, - stashInitialSize: void 0, - isLive: !1, - lazyLoad: !0, - lazyLoadMaxDuration: 180, - lazyLoadRecoverDuration: 30, - deferLoadAfterSourceOpen: !0, - autoCleanupMaxBackwardDuration: 180, - autoCleanupMinBackwardDuration: 120, - statisticsInfoReportInterval: 600, - fixAudioTimestampGap: !0, - accurateSeek: !1, - seekType: "range", - seekParamStart: "bstart", - seekParamEnd: "bend", - rangeLoadZeroStart: !1, - customSeekHandler: void 0, - reuseRedirectedURL: !1 - } - }, {}], - 6: [function(e, t, n) { - "use strict"; - - function i(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var r = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - s = e("../io/io-controller.js"), - a = function(e) { - return e && e.__esModule ? e : { - default: e - } - }(s), - o = e("../config.js"), - u = function() { - function e() { - i(this, e) - } - return r(e, null, [{ - key: "supportMSEH264Playback", - value: function() { - return window.MediaSource && window.MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"') - } - }, { - key: "supportNetworkStreamIO", - value: function() { - var e = new a.default({}, (0, o.createDefaultConfig)()), - t = e.loaderType; - return e.destroy(), "fetch-stream-loader" == t || "xhr-moz-chunked-loader" == t - } - }, { - key: "getNetworkLoaderTypeName", - value: function() { - var e = new a.default({}, (0, o.createDefaultConfig)()), - t = e.loaderType; - return e.destroy(), t - } - }, { - key: "supportNativeMediaPlayback", - value: function(t) { - void 0 == e.videoElement && (e.videoElement = window.document.createElement("video")); - var n = e.videoElement.canPlayType(t); - return "probably" === n || "maybe" == n - } - }, { - key: "getFeatureList", - value: function() { - var t = { - mseFlvPlayback: !1, - mseLiveFlvPlayback: !1, - networkStreamIO: !1, - networkLoaderName: "", - nativeMP4H264Playback: !1, - nativeWebmVP8Playback: !1, - nativeWebmVP9Playback: !1 - }; - return t.mseFlvPlayback = e.supportMSEH264Playback(), t.networkStreamIO = e.supportNetworkStreamIO(), t.networkLoaderName = e.getNetworkLoaderTypeName(), t.mseLiveFlvPlayback = t.mseFlvPlayback && t.networkStreamIO, t.nativeMP4H264Playback = e.supportNativeMediaPlayback('video/mp4; codecs="avc1.42001E, mp4a.40.2"'), t.nativeWebmVP8Playback = e.supportNativeMediaPlayback('video/webm; codecs="vp8.0, vorbis"'), t.nativeWebmVP9Playback = e.supportNativeMediaPlayback('video/webm; codecs="vp9"'), t - } - }]), e - }(); - n.default = u - }, { - "../config.js": 5, - "../io/io-controller.js": 23 - }], - 7: [function(e, t, n) { - "use strict"; - - function i(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var r = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - s = function() { - function e() { - i(this, e), this.mimeType = null, this.duration = null, this.hasAudio = null, this.hasVideo = null, this.audioCodec = null, this.videoCodec = null, this.audioDataRate = null, this.videoDataRate = null, this.audioSampleRate = null, this.audioChannelCount = null, this.width = null, this.height = null, this.fps = null, this.profile = null, this.level = null, this.refFrames = null, this.chromaFormat = null, this.sarNum = null, this.sarDen = null, this.metadata = null, this.segments = null, this.segmentCount = null, this.hasKeyframesIndex = null, this.keyframesIndex = null - } - return r(e, [{ - key: "isComplete", - value: function() { - var e = !1 === this.hasAudio || !0 === this.hasAudio && null != this.audioCodec && null != this.audioSampleRate && null != this.audioChannelCount, - t = !1 === this.hasVideo || !0 === this.hasVideo && null != this.videoCodec && null != this.width && null != this.height && null != this.fps && null != this.profile && null != this.level && null != this.refFrames && null != this.chromaFormat && null != this.sarNum && null != this.sarDen; - return null != this.mimeType && null != this.duration && null != this.metadata && null != this.hasKeyframesIndex && e && t - } - }, { - key: "isSeekable", - value: function() { - return !0 === this.hasKeyframesIndex - } - }, { - key: "getNearestKeyframe", - value: function(e) { - if (null == this.keyframesIndex) return null; - var t = this.keyframesIndex, - n = this._search(t.times, e); - return { - index: n, - milliseconds: t.times[n], - fileposition: t.filepositions[n] - } - } - }, { - key: "_search", - value: function(e, t) { - var n = 0, - i = e.length - 1, - r = 0, - s = 0, - a = i; - for (t < e[0] && (n = 0, s = a + 1); s <= a;) { - if ((r = s + Math.floor((a - s) / 2)) === i || t >= e[r] && t < e[r + 1]) { - n = r; - break - } - e[r] < t ? s = r + 1 : a = r - 1 - } - return n - } - }]), e - }(); - n.default = s - }, {}], - 8: [function(e, t, n) { - "use strict"; - - function i(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var r = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(); - n.SampleInfo = function e(t, n, r, s, a) { - i(this, e), this.dts = t, this.pts = n, this.duration = r, this.originalDts = s, this.isSyncPoint = a, this.fileposition = null - }, n.MediaSegmentInfo = function() { - function e() { - i(this, e), this.beginDts = 0, this.endDts = 0, this.beginPts = 0, this.endPts = 0, this.originalBeginDts = 0, this.originalEndDts = 0, this.syncPoints = [], this.firstSample = null, this.lastSample = null - } - return r(e, [{ - key: "appendSyncPoint", - value: function(e) { - e.isSyncPoint = !0, this.syncPoints.push(e) - } - }]), e - }(), n.IDRSampleList = function() { - function e() { - i(this, e), this._list = [] - } - return r(e, [{ - key: "clear", - value: function() { - this._list = [] - } - }, { - key: "appendArray", - value: function(e) { - var t = this._list; - 0 !== e.length && (t.length > 0 && e[0].originalDts < t[t.length - 1].originalDts && this.clear(), Array.prototype.push.apply(t, e)) - } - }, { - key: "getLastSyncPointBeforeDts", - value: function(e) { - if (0 == this._list.length) return null; - var t = this._list, - n = 0, - i = t.length - 1, - r = 0, - s = 0, - a = i; - for (e < t[0].dts && (n = 0, s = a + 1); s <= a;) { - if ((r = s + Math.floor((a - s) / 2)) === i || e >= t[r].dts && e < t[r + 1].dts) { - n = r; - break - } - t[r].dts < e ? s = r + 1 : a = r - 1 - } - return this._list[n] - } - }]), e - }(), n.MediaSegmentInfoList = function() { - function e(t) { - i(this, e), this._type = t, this._list = [], this._lastAppendLocation = -1 - } - return r(e, [{ - key: "isEmpty", - value: function() { - return 0 === this._list.length - } - }, { - key: "clear", - value: function() { - this._list = [], this._lastAppendLocation = -1 - } - }, { - key: "_searchNearestSegmentBefore", - value: function(e) { - var t = this._list; - if (0 === t.length) return -2; - var n = t.length - 1, - i = 0, - r = 0, - s = n, - a = 0; - if (e < t[0].originalBeginDts) return a = -1; - for (; r <= s;) { - if ((i = r + Math.floor((s - r) / 2)) === n || e > t[i].lastSample.originalDts && e < t[i + 1].originalBeginDts) { - a = i; - break - } - t[i].originalBeginDts < e ? r = i + 1 : s = i - 1 - } - return a - } - }, { - key: "_searchNearestSegmentAfter", - value: function(e) { - return this._searchNearestSegmentBefore(e) + 1 - } - }, { - key: "append", - value: function(e) { - var t = this._list, - n = e, - i = this._lastAppendLocation, - r = 0; - 1 !== i && i < t.length && n.originalBeginDts >= t[i].lastSample.originalDts && (i === t.length - 1 || i < t.length - 1 && n.originalBeginDts < t[i + 1].originalBeginDts) ? r = i + 1 : t.length > 0 && (r = this._searchNearestSegmentBefore(n.originalBeginDts) + 1), this._lastAppendLocation = r, this._list.splice(r, 0, n) - } - }, { - key: "getLastSegmentBefore", - value: function(e) { - var t = this._searchNearestSegmentBefore(e); - return t >= 0 ? this._list[t] : null - } - }, { - key: "getLastSampleBefore", - value: function(e) { - var t = this.getLastSegmentBefore(e); - return null != t ? t.lastSample : null - } - }, { - key: "getLastSyncPointBefore", - value: function(e) { - for (var t = this._searchNearestSegmentBefore(e), n = this._list[t].syncPoints; 0 === n.length && t > 0;) t--, n = this._list[t].syncPoints; - return n.length > 0 ? n[n.length - 1] : null - } - }, { - key: "type", - get: function() { - return this._type - } - }, { - key: "length", - get: function() { - return this._list.length - } - }]), e - }() - }, {}], - 9: [function(e, t, n) { - "use strict"; - - function i(e) { - return e && e.__esModule ? e : { - default: e - } - } - - function r(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var s = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - a = e("events"), - o = i(a), - u = e("../utils/logger.js"), - l = i(u), - d = e("../utils/browser.js"), - h = i(d), - f = e("./mse-events.js"), - c = i(f), - _ = e("./media-segment-info.js"), - m = e("../utils/exception.js"), - p = function() { - function e(t) { - r(this, e), this.TAG = "MSEController", this._config = t, this._emitter = new o.default, this._config.isLive && void 0 == this._config.autoCleanupSourceBuffer && (this._config.autoCleanupSourceBuffer = !0), this.e = { - onSourceOpen: this._onSourceOpen.bind(this), - onSourceEnded: this._onSourceEnded.bind(this), - onSourceClose: this._onSourceClose.bind(this), - onSourceBufferError: this._onSourceBufferError.bind(this), - onSourceBufferUpdateEnd: this._onSourceBufferUpdateEnd.bind(this) - }, this._mediaSource = null, this._mediaSourceObjectURL = null, this._mediaElement = null, this._isBufferFull = !1, this._hasPendingEos = !1, this._requireSetMediaDuration = !1, this._pendingMediaDuration = 0, this._pendingSourceBufferInit = [], this._mimeTypes = { - video: null, - audio: null - }, this._sourceBuffers = { - video: null, - audio: null - }, this._lastInitSegments = { - video: null, - audio: null - }, this._pendingSegments = { - video: [], - audio: [] - }, this._pendingRemoveRanges = { - video: [], - audio: [] - }, this._idrList = new _.IDRSampleList - } - return s(e, [{ - key: "destroy", - value: function() { - (this._mediaElement || this._mediaSource) && this.detachMediaElement(), this.e = null, this._emitter.removeAllListeners(), this._emitter = null - } - }, { - key: "on", - value: function(e, t) { - this._emitter.addListener(e, t) - } - }, { - key: "off", - value: function(e, t) { - this._emitter.removeListener(e, t) - } - }, { - key: "attachMediaElement", - value: function(e) { - if (this._mediaSource) throw new m.IllegalStateException("MediaSource has been attached to an HTMLMediaElement!"); - var t = this._mediaSource = new window.MediaSource; - t.addEventListener("sourceopen", this.e.onSourceOpen), t.addEventListener("sourceended", this.e.onSourceEnded), t.addEventListener("sourceclose", this.e.onSourceClose), this._mediaElement = e, this._mediaSourceObjectURL = window.URL.createObjectURL(this._mediaSource), e.src = this._mediaSourceObjectURL - } - }, { - key: "detachMediaElement", - value: function() { - if (this._mediaSource) { - var e = this._mediaSource; - for (var t in this._sourceBuffers) { - var n = this._pendingSegments[t]; - n.splice(0, n.length), this._pendingSegments[t] = null, this._pendingRemoveRanges[t] = null, this._lastInitSegments[t] = null; - var i = this._sourceBuffers[t]; - i && ("closed" !== e.readyState && (e.removeSourceBuffer(i), i.removeEventListener("error", this.e.onSourceBufferError), i.removeEventListener("updateend", this.e.onSourceBufferUpdateEnd)), this._mimeTypes[t] = null, this._sourceBuffers[t] = null) - } - if ("open" === e.readyState) try { - e.endOfStream() - } catch (e) { - l.default.e(this.TAG, e.message) - } - e.removeEventListener("sourceopen", this.e.onSourceOpen), e.removeEventListener("sourceended", this.e.onSourceEnded), e.removeEventListener("sourceclose", this.e.onSourceClose), this._pendingSourceBufferInit = [], this._isBufferFull = !1, this._idrList.clear(), this._mediaSource = null - } - this._mediaElement && (this._mediaElement.src = "", this._mediaElement.removeAttribute("src"), this._mediaElement = null), this._mediaSourceObjectURL && (window.URL.revokeObjectURL(this._mediaSourceObjectURL), this._mediaSourceObjectURL = null) - } - }, { - key: "appendInitSegment", - value: function(e, t) { - if (!this._mediaSource || "open" !== this._mediaSource.readyState) return this._pendingSourceBufferInit.push(e), void this._pendingSegments[e.type].push(e); - var n = e, - i = "" + n.container; - n.codec && n.codec.length > 0 && (i += ";codecs=" + n.codec); - var r = !1; - if (l.default.v(this.TAG, "Received Initialization Segment, mimeType: " + i), this._lastInitSegments[n.type] = n, i !== this._mimeTypes[n.type]) { - if (this._mimeTypes[n.type]) l.default.v(this.TAG, "Notice: " + n.type + " mimeType changed, origin: " + this._mimeTypes[n.type] + ", target: " + i); - else { - r = !0; - try { - var s = this._sourceBuffers[n.type] = this._mediaSource.addSourceBuffer(i); - s.addEventListener("error", this.e.onSourceBufferError), s.addEventListener("updateend", this.e.onSourceBufferUpdateEnd) - } catch (e) { - return l.default.e(this.TAG, e.message), void this._emitter.emit(c.default.ERROR, { - code: e.code, - msg: e.message - }) - } - } - this._mimeTypes[n.type] = i - } - t || this._pendingSegments[n.type].push(n), r || this._sourceBuffers[n.type] && !this._sourceBuffers[n.type].updating && this._doAppendSegments(), h.default.safari && "audio/mpeg" === n.container && n.mediaDuration > 0 && (this._requireSetMediaDuration = !0, this._pendingMediaDuration = n.mediaDuration / 1e3, this._updateMediaSourceDuration()) - } - }, { - key: "appendMediaSegment", - value: function(e) { - var t = e; - this._pendingSegments[t.type].push(t), this._config.autoCleanupSourceBuffer && this._needCleanupSourceBuffer() && this._doCleanupSourceBuffer(); - var n = this._sourceBuffers[t.type]; - !n || n.updating || this._hasPendingRemoveRanges() || this._doAppendSegments() - } - }, { - key: "seek", - value: function(e) { - for (var t in this._sourceBuffers) - if (this._sourceBuffers[t]) { - var n = this._sourceBuffers[t]; - if ("open" === this._mediaSource.readyState) try { - n.abort() - } catch (e) { - l.default.e(this.TAG, e.message) - } - this._idrList.clear(); - var i = this._pendingSegments[t]; - if (i.splice(0, i.length), "closed" !== this._mediaSource.readyState) { - for (var r = 0; r < n.buffered.length; r++) { - var s = n.buffered.start(r), - a = n.buffered.end(r); - this._pendingRemoveRanges[t].push({ - start: s, - end: a - }) - } - if (n.updating || this._doRemoveRanges(), h.default.safari) { - var o = this._lastInitSegments[t]; - o && (this._pendingSegments[t].push(o), n.updating || this._doAppendSegments()) - } - } - } - } - }, { - key: "endOfStream", - value: function() { - var e = this._mediaSource, - t = this._sourceBuffers; - if (!e || "open" !== e.readyState) return void(e && "closed" === e.readyState && this._hasPendingSegments() && (this._hasPendingEos = !0)); - t.video && t.video.updating || t.audio && t.audio.updating ? this._hasPendingEos = !0 : (this._hasPendingEos = !1, e.endOfStream()) - } - }, { - key: "getNearestKeyframe", - value: function(e) { - return this._idrList.getLastSyncPointBeforeDts(e) - } - }, { - key: "_needCleanupSourceBuffer", - value: function() { - if (!this._config.autoCleanupSourceBuffer) return !1; - var e = this._mediaElement.currentTime; - for (var t in this._sourceBuffers) { - var n = this._sourceBuffers[t]; - if (n) { - var i = n.buffered; - if (i.length >= 1 && e - i.start(0) >= this._config.autoCleanupMaxBackwardDuration) return !0 - } - } - return !1 - } - }, { - key: "_doCleanupSourceBuffer", - value: function() { - var e = this._mediaElement.currentTime; - for (var t in this._sourceBuffers) { - var n = this._sourceBuffers[t]; - if (n) { - for (var i = n.buffered, r = !1, s = 0; s < i.length; s++) { - var a = i.start(s), - o = i.end(s); - if (a <= e && e < o + 3) { - if (e - a >= this._config.autoCleanupMaxBackwardDuration) { - r = !0; - var u = e - this._config.autoCleanupMinBackwardDuration; - this._pendingRemoveRanges[t].push({ - start: a, - end: u - }) - } - } else o < e && (r = !0, this._pendingRemoveRanges[t].push({ - start: a, - end: o - })) - } - r && !n.updating && this._doRemoveRanges() - } - } - } - }, { - key: "_updateMediaSourceDuration", - value: function() { - var e = this._sourceBuffers; - if (0 !== this._mediaElement.readyState && "open" === this._mediaSource.readyState && !(e.video && e.video.updating || e.audio && e.audio.updating)) { - var t = this._mediaSource.duration, - n = this._pendingMediaDuration; - n > 0 && (isNaN(t) || n > t) && (l.default.v(this.TAG, "Update MediaSource duration from " + t + " to " + n), this._mediaSource.duration = n), this._requireSetMediaDuration = !1, this._pendingMediaDuration = 0 - } - } - }, { - key: "_doRemoveRanges", - value: function() { - for (var e in this._pendingRemoveRanges) - if (this._sourceBuffers[e] && !this._sourceBuffers[e].updating) - for (var t = this._sourceBuffers[e], n = this._pendingRemoveRanges[e]; n.length && !t.updating;) { - var i = n.shift(); - t.remove(i.start, i.end) - } - } - }, { - key: "_doAppendSegments", - value: function() { - var e = this._pendingSegments; - for (var t in e) - if (this._sourceBuffers[t] && !this._sourceBuffers[t].updating && e[t].length > 0) { - var n = e[t].shift(); - if (n.timestampOffset) { - var i = this._sourceBuffers[t].timestampOffset, - r = n.timestampOffset / 1e3, - s = Math.abs(i - r); - s > .1 && (l.default.v(this.TAG, "Update MPEG audio timestampOffset from " + i + " to " + r), this._sourceBuffers[t].timestampOffset = r), delete n.timestampOffset - } - if (!n.data || 0 === n.data.byteLength) continue; - try { - this._sourceBuffers[t].appendBuffer(n.data), this._isBufferFull = !1, "video" === t && n.hasOwnProperty("info") && this._idrList.appendArray(n.info.syncPoints) - } catch (e) { - this._pendingSegments[t].unshift(n), 22 === e.code ? (this._isBufferFull || this._emitter.emit(c.default.BUFFER_FULL), this._isBufferFull = !0) : (l.default.e(this.TAG, e.message), this._emitter.emit(c.default.ERROR, { - code: e.code, - msg: e.message - })) - } - } - } - }, { - key: "_onSourceOpen", - value: function() { - if (l.default.v(this.TAG, "MediaSource onSourceOpen"), this._mediaSource.removeEventListener("sourceopen", this.e.onSourceOpen), this._pendingSourceBufferInit.length > 0) - for (var e = this._pendingSourceBufferInit; e.length;) { - var t = e.shift(); - this.appendInitSegment(t, !0) - } - this._hasPendingSegments() && this._doAppendSegments(), this._emitter.emit(c.default.SOURCE_OPEN) - } - }, { - key: "_onSourceEnded", - value: function() { - l.default.v(this.TAG, "MediaSource onSourceEnded") - } - }, { - key: "_onSourceClose", - value: function() { - l.default.v(this.TAG, "MediaSource onSourceClose"), this._mediaSource && null != this.e && (this._mediaSource.removeEventListener("sourceopen", this.e.onSourceOpen), this._mediaSource.removeEventListener("sourceended", this.e.onSourceEnded), this._mediaSource.removeEventListener("sourceclose", this.e.onSourceClose)) - } - }, { - key: "_hasPendingSegments", - value: function() { - var e = this._pendingSegments; - return e.video.length > 0 || e.audio.length > 0 - } - }, { - key: "_hasPendingRemoveRanges", - value: function() { - var e = this._pendingRemoveRanges; - return e.video.length > 0 || e.audio.length > 0 - } - }, { - key: "_onSourceBufferUpdateEnd", - value: function() { - this._requireSetMediaDuration ? this._updateMediaSourceDuration() : this._hasPendingRemoveRanges() ? this._doRemoveRanges() : this._hasPendingSegments() ? this._doAppendSegments() : this._hasPendingEos && this.endOfStream(), this._emitter.emit(c.default.UPDATE_END) - } - }, { - key: "_onSourceBufferError", - value: function(e) { - l.default.e(this.TAG, "SourceBuffer Error: " + e) - } - }]), e - }(); - n.default = p - }, { - "../utils/browser.js": 39, - "../utils/exception.js": 40, - "../utils/logger.js": 41, - "./media-segment-info.js": 8, - "./mse-events.js": 10, - events: 2 - }], - 10: [function(e, t, n) { - "use strict"; - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var i = { - ERROR: "error", - SOURCE_OPEN: "source_open", - UPDATE_END: "update_end", - BUFFER_FULL: "buffer_full" - }; - n.default = i - }, {}], - 11: [function(e, t, n) { - "use strict"; - - function i(e) { - return e && e.__esModule ? e : { - default: e - } - } - - function r(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var s = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - a = e("events"), - o = i(a), - u = e("../utils/logger.js"), - l = i(u), - d = e("../utils/logging-control.js"), - h = i(d), - f = e("./transmuxing-controller.js"), - c = i(f), - _ = e("./transmuxing-events.js"), - m = i(_), - p = e("./transmuxing-worker.js"), - v = i(p), - g = e("./media-info.js"), - y = i(g), - E = function() { - function t(n, i) { - if (r(this, t), this.TAG = "Transmuxer", this._emitter = new o.default, i.enableWorker && "undefined" != typeof Worker) try { - var s = e("webworkify"); - this._worker = s(v.default), this._workerDestroying = !1, this._worker.addEventListener("message", this._onWorkerMessage.bind(this)), this._worker.postMessage({ - cmd: "init", - param: [n, i] - }), this.e = { - onLoggingConfigChanged: this._onLoggingConfigChanged.bind(this) - }, h.default.registerListener(this.e.onLoggingConfigChanged), this._worker.postMessage({ - cmd: "logging_config", - param: h.default.getConfig() - }) - } catch (e) { - l.default.e(this.TAG, "Error while initialize transmuxing worker, fallback to inline transmuxing"), this._worker = null, this._controller = new c.default(n, i) - } else this._controller = new c.default(n, i); - if (this._controller) { - var a = this._controller; - a.on(m.default.IO_ERROR, this._onIOError.bind(this)), a.on(m.default.DEMUX_ERROR, this._onDemuxError.bind(this)), a.on(m.default.INIT_SEGMENT, this._onInitSegment.bind(this)), a.on(m.default.MEDIA_SEGMENT, this._onMediaSegment.bind(this)), a.on(m.default.LOADING_COMPLETE, this._onLoadingComplete.bind(this)), a.on(m.default.RECOVERED_EARLY_EOF, this._onRecoveredEarlyEof.bind(this)), a.on(m.default.MEDIA_INFO, this._onMediaInfo.bind(this)), a.on(m.default.STATISTICS_INFO, this._onStatisticsInfo.bind(this)), a.on(m.default.RECOMMEND_SEEKPOINT, this._onRecommendSeekpoint.bind(this)) - } - } - return s(t, [{ - key: "destroy", - value: function() { - this._worker ? this._workerDestroying || (this._workerDestroying = !0, this._worker.postMessage({ - cmd: "destroy" - }), h.default.removeListener(this.e.onLoggingConfigChanged), this.e = null) : (this._controller.destroy(), this._controller = null), this._emitter.removeAllListeners(), this._emitter = null - } - }, { - key: "on", - value: function(e, t) { - this._emitter.addListener(e, t) - } - }, { - key: "off", - value: function(e, t) { - this._emitter.removeListener(e, t) - } - }, { - key: "hasWorker", - value: function() { - return null != this._worker - } - }, { - key: "open", - value: function() { - this._worker ? this._worker.postMessage({ - cmd: "start" - }) : this._controller.start() - } - }, { - key: "close", - value: function() { - this._worker ? this._worker.postMessage({ - cmd: "stop" - }) : this._controller.stop() - } - }, { - key: "seek", - value: function(e) { - this._worker ? this._worker.postMessage({ - cmd: "seek", - param: e - }) : this._controller.seek(e) - } - }, { - key: "pause", - value: function() { - this._worker ? this._worker.postMessage({ - cmd: "pause" - }) : this._controller.pause() - } - }, { - key: "resume", - value: function() { - this._worker ? this._worker.postMessage({ - cmd: "resume" - }) : this._controller.resume() - } - }, { - key: "_onInitSegment", - value: function(e, t) { - var n = this; - Promise.resolve().then(function() { - n._emitter.emit(m.default.INIT_SEGMENT, e, t) - }) - } - }, { - key: "_onMediaSegment", - value: function(e, t) { - var n = this; - Promise.resolve().then(function() { - n._emitter.emit(m.default.MEDIA_SEGMENT, e, t) - }) - } - }, { - key: "_onLoadingComplete", - value: function() { - var e = this; - Promise.resolve().then(function() { - e._emitter.emit(m.default.LOADING_COMPLETE) - }) - } - }, { - key: "_onRecoveredEarlyEof", - value: function() { - var e = this; - Promise.resolve().then(function() { - e._emitter.emit(m.default.RECOVERED_EARLY_EOF) - }) - } - }, { - key: "_onMediaInfo", - value: function(e) { - var t = this; - Promise.resolve().then(function() { - t._emitter.emit(m.default.MEDIA_INFO, e) - }) - } - }, { - key: "_onStatisticsInfo", - value: function(e) { - var t = this; - Promise.resolve().then(function() { - t._emitter.emit(m.default.STATISTICS_INFO, e) - }) - } - }, { - key: "_onIOError", - value: function(e, t) { - var n = this; - Promise.resolve().then(function() { - n._emitter.emit(m.default.IO_ERROR, e, t) - }) - } - }, { - key: "_onDemuxError", - value: function(e, t) { - var n = this; - Promise.resolve().then(function() { - n._emitter.emit(m.default.DEMUX_ERROR, e, t) - }) - } - }, { - key: "_onRecommendSeekpoint", - value: function(e) { - var t = this; - Promise.resolve().then(function() { - t._emitter.emit(m.default.RECOMMEND_SEEKPOINT, e) - }) - } - }, { - key: "_onLoggingConfigChanged", - value: function(e) { - this._worker && this._worker.postMessage({ - cmd: "logging_config", - param: e - }) - } - }, { - key: "_onWorkerMessage", - value: function(e) { - var t = e.data, - n = t.data; - if ("destroyed" === t.msg || this._workerDestroying) return this._workerDestroying = !1, this._worker.terminate(), void(this._worker = null); - switch (t.msg) { - case m.default.INIT_SEGMENT: - case m.default.MEDIA_SEGMENT: - this._emitter.emit(t.msg, n.type, n.data); - break; - case m.default.LOADING_COMPLETE: - case m.default.RECOVERED_EARLY_EOF: - this._emitter.emit(t.msg); - break; - case m.default.MEDIA_INFO: - Object.setPrototypeOf(n, y.default.prototype), this._emitter.emit(t.msg, n); - break; - case m.default.STATISTICS_INFO: - this._emitter.emit(t.msg, n); - break; - case m.default.IO_ERROR: - case m.default.DEMUX_ERROR: - this._emitter.emit(t.msg, n.type, n.info); - break; - case m.default.RECOMMEND_SEEKPOINT: - this._emitter.emit(t.msg, n); - break; - case "logcat_callback": - l.default.emitter.emit("log", n.type, n.logcat) - } - } - }]), t - }(); - n.default = E - }, { - "../utils/logger.js": 41, - "../utils/logging-control.js": 42, - "./media-info.js": 7, - "./transmuxing-controller.js": 12, - "./transmuxing-events.js": 13, - "./transmuxing-worker.js": 14, - events: 2, - webworkify: 4 - }], - 12: [function(e, t, n) { - "use strict"; - - function i(e) { - return e && e.__esModule ? e : { - default: e - } - } - - function r(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var s = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - a = e("events"), - o = i(a), - u = e("../utils/logger.js"), - l = i(u), - d = e("../utils/browser.js"), - h = i(d), - f = e("./media-info.js"), - c = i(f), - _ = e("../demux/flv-demuxer.js"), - m = i(_), - p = e("../remux/mp4-remuxer.js"), - v = i(p), - g = e("../demux/demux-errors.js"), - y = i(g), - E = e("../io/io-controller.js"), - b = i(E), - S = e("./transmuxing-events.js"), - k = i(S), - L = (e("../io/loader.js"), function() { - function e(t, n) { - r(this, e), this.TAG = "TransmuxingController", this._emitter = new o.default, this._config = n, t.segments || (t.segments = [{ - duration: t.duration, - filesize: t.filesize, - url: t.url - }]), "boolean" != typeof t.cors && (t.cors = !0), "boolean" != typeof t.withCredentials && (t.withCredentials = !1), this._mediaDataSource = t, this._currentSegmentIndex = 0; - var i = 0; - this._mediaDataSource.segments.forEach(function(e) { - e.timestampBase = i, i += e.duration, e.cors = t.cors, e.withCredentials = t.withCredentials, n.referrerPolicy && (e.referrerPolicy = n.referrerPolicy) - }), isNaN(i) || this._mediaDataSource.duration === i || (this._mediaDataSource.duration = i), this._mediaInfo = null, this._demuxer = null, this._remuxer = null, this._ioctl = null, this._pendingSeekTime = null, this._pendingResolveSeekPoint = null, this._statisticsReporter = null - } - return s(e, [{ - key: "destroy", - value: function() { - this._mediaInfo = null, this._mediaDataSource = null, this._statisticsReporter && this._disableStatisticsReporter(), this._ioctl && (this._ioctl.destroy(), this._ioctl = null), this._demuxer && (this._demuxer.destroy(), this._demuxer = null), this._remuxer && (this._remuxer.destroy(), this._remuxer = null), this._emitter.removeAllListeners(), this._emitter = null - } - }, { - key: "on", - value: function(e, t) { - this._emitter.addListener(e, t) - } - }, { - key: "off", - value: function(e, t) { - this._emitter.removeListener(e, t) - } - }, { - key: "start", - value: function() { - this._loadSegment(0), this._enableStatisticsReporter() - } - }, { - key: "_loadSegment", - value: function(e, t) { - this._currentSegmentIndex = e; - var n = this._mediaDataSource.segments[e], - i = this._ioctl = new b.default(n, this._config, e); - i.onError = this._onIOException.bind(this), i.onSeeked = this._onIOSeeked.bind(this), i.onComplete = this._onIOComplete.bind(this), i.onRedirect = this._onIORedirect.bind(this), i.onRecoveredEarlyEof = this._onIORecoveredEarlyEof.bind(this), t ? this._demuxer.bindDataSource(this._ioctl) : i.onDataArrival = this._onInitChunkArrival.bind(this), i.open(t) - } - }, { - key: "stop", - value: function() { - this._internalAbort(), this._disableStatisticsReporter() - } - }, { - key: "_internalAbort", - value: function() { - this._ioctl && (this._ioctl.destroy(), this._ioctl = null) - } - }, { - key: "pause", - value: function() { - this._ioctl && this._ioctl.isWorking() && (this._ioctl.pause(), this._disableStatisticsReporter()) - } - }, { - key: "resume", - value: function() { - this._ioctl && this._ioctl.isPaused() && (this._ioctl.resume(), this._enableStatisticsReporter()) - } - }, { - key: "seek", - value: function(e) { - if (null != this._mediaInfo && this._mediaInfo.isSeekable()) { - var t = this._searchSegmentIndexContains(e); - if (t === this._currentSegmentIndex) { - var n = this._mediaInfo.segments[t]; - if (void 0 == n) this._pendingSeekTime = e; - else { - var i = n.getNearestKeyframe(e); - this._remuxer.seek(i.milliseconds), this._ioctl.seek(i.fileposition), this._pendingResolveSeekPoint = i.milliseconds - } - } else { - var r = this._mediaInfo.segments[t]; - if (void 0 == r) this._pendingSeekTime = e, this._internalAbort(), this._remuxer.seek(), this._remuxer.insertDiscontinuity(), this._loadSegment(t); - else { - var s = r.getNearestKeyframe(e); - this._internalAbort(), this._remuxer.seek(e), this._remuxer.insertDiscontinuity(), this._demuxer.resetMediaInfo(), this._demuxer.timestampBase = this._mediaDataSource.segments[t].timestampBase, this._loadSegment(t, s.fileposition), this._pendingResolveSeekPoint = s.milliseconds, this._reportSegmentMediaInfo(t) - } - } - this._enableStatisticsReporter() - } - } - }, { - key: "_searchSegmentIndexContains", - value: function(e) { - for (var t = this._mediaDataSource.segments, n = t.length - 1, i = 0; i < t.length; i++) - if (e < t[i].timestampBase) { - n = i - 1; - break - } return n - } - }, { - key: "_onInitChunkArrival", - value: function(e, t) { - var n = this, - i = null, - r = 0; - if (t > 0) this._demuxer.bindDataSource(this._ioctl), this._demuxer.timestampBase = this._mediaDataSource.segments[this._currentSegmentIndex].timestampBase, r = this._demuxer.parseChunks(e, t); - else if ((i = m.default.probe(e)).match) { - this._demuxer = new m.default(i, this._config), this._remuxer || (this._remuxer = new v.default(this._config)); - var s = this._mediaDataSource; - void 0 == s.duration || isNaN(s.duration) || (this._demuxer.overridedDuration = s.duration), "boolean" == typeof s.hasAudio && (this._demuxer.overridedHasAudio = s.hasAudio), "boolean" == typeof s.hasVideo && (this._demuxer.overridedHasVideo = s.hasVideo), this._demuxer.timestampBase = s.segments[this._currentSegmentIndex].timestampBase, this._demuxer.onError = this._onDemuxException.bind(this), this._demuxer.onMediaInfo = this._onMediaInfo.bind(this), this._remuxer.bindDataSource(this._demuxer.bindDataSource(this._ioctl)), this._remuxer.onInitSegment = this._onRemuxerInitSegmentArrival.bind(this), this._remuxer.onMediaSegment = this._onRemuxerMediaSegmentArrival.bind(this), r = this._demuxer.parseChunks(e, t) - } else i = null, l.default.e(this.TAG, "Non-FLV, Unsupported media type!"), Promise.resolve().then(function() { - n._internalAbort() - }), this._emitter.emit(k.default.DEMUX_ERROR, y.default.FORMAT_UNSUPPORTED, "Non-FLV, Unsupported media type"), r = 0; - return r - } - }, { - key: "_onMediaInfo", - value: function(e) { - var t = this; - null == this._mediaInfo && (this._mediaInfo = Object.assign({}, e), this._mediaInfo.keyframesIndex = null, this._mediaInfo.segments = [], this._mediaInfo.segmentCount = this._mediaDataSource.segments.length, Object.setPrototypeOf(this._mediaInfo, c.default.prototype)); - var n = Object.assign({}, e); - Object.setPrototypeOf(n, c.default.prototype), this._mediaInfo.segments[this._currentSegmentIndex] = n, this._reportSegmentMediaInfo(this._currentSegmentIndex), null != this._pendingSeekTime && Promise.resolve().then(function() { - var e = t._pendingSeekTime; - t._pendingSeekTime = null, t.seek(e) - }) - } - }, { - key: "_onIOSeeked", - value: function() { - this._remuxer.insertDiscontinuity() - } - }, { - key: "_onIOComplete", - value: function(e) { - var t = e, - n = t + 1; - n < this._mediaDataSource.segments.length ? (this._internalAbort(), this._remuxer.flushStashedSamples(), this._loadSegment(n)) : (this._remuxer.flushStashedSamples(), this._emitter.emit(k.default.LOADING_COMPLETE), this._disableStatisticsReporter()) - } - }, { - key: "_onIORedirect", - value: function(e) { - var t = this._ioctl.extraData; - this._mediaDataSource.segments[t].redirectedURL = e - } - }, { - key: "_onIORecoveredEarlyEof", - value: function() { - this._emitter.emit(k.default.RECOVERED_EARLY_EOF) - } - }, { - key: "_onIOException", - value: function(e, t) { - l.default.e(this.TAG, "IOException: type = " + e + ", code = " + t.code + ", msg = " + t.msg), this._emitter.emit(k.default.IO_ERROR, e, t), this._disableStatisticsReporter() - } - }, { - key: "_onDemuxException", - value: function(e, t) { - l.default.e(this.TAG, "DemuxException: type = " + e + ", info = " + t), this._emitter.emit(k.default.DEMUX_ERROR, e, t) - } - }, { - key: "_onRemuxerInitSegmentArrival", - value: function(e, t) { - this._emitter.emit(k.default.INIT_SEGMENT, e, t) - } - }, { - key: "_onRemuxerMediaSegmentArrival", - value: function(e, t) { - if (null == this._pendingSeekTime && (this._emitter.emit(k.default.MEDIA_SEGMENT, e, t), null != this._pendingResolveSeekPoint && "video" === e)) { - var n = t.info.syncPoints, - i = this._pendingResolveSeekPoint; - this._pendingResolveSeekPoint = null, h.default.safari && n.length > 0 && n[0].originalDts === i && (i = n[0].pts), this._emitter.emit(k.default.RECOMMEND_SEEKPOINT, i) - } - } - }, { - key: "_enableStatisticsReporter", - value: function() { - null == this._statisticsReporter && (this._statisticsReporter = self.setInterval(this._reportStatisticsInfo.bind(this), this._config.statisticsInfoReportInterval)) - } - }, { - key: "_disableStatisticsReporter", - value: function() { - this._statisticsReporter && (self.clearInterval(this._statisticsReporter), this._statisticsReporter = null) - } - }, { - key: "_reportSegmentMediaInfo", - value: function(e) { - var t = this._mediaInfo.segments[e], - n = Object.assign({}, t); - n.duration = this._mediaInfo.duration, n.segmentCount = this._mediaInfo.segmentCount, delete n.segments, delete n.keyframesIndex, this._emitter.emit(k.default.MEDIA_INFO, n) - } - }, { - key: "_reportStatisticsInfo", - value: function() { - var e = {}; - e.url = this._ioctl.currentURL, e.hasRedirect = this._ioctl.hasRedirect, e.hasRedirect && (e.redirectedURL = this._ioctl.currentRedirectedURL), e.speed = this._ioctl.currentSpeed, e.loaderType = this._ioctl.loaderType, e.currentSegmentIndex = this._currentSegmentIndex, e.totalSegmentCount = this._mediaDataSource.segments.length, this._emitter.emit(k.default.STATISTICS_INFO, e) - } - }]), e - }()); - n.default = L - }, { - "../demux/demux-errors.js": 16, - "../demux/flv-demuxer.js": 18, - "../io/io-controller.js": 23, - "../io/loader.js": 24, - "../remux/mp4-remuxer.js": 38, - "../utils/browser.js": 39, - "../utils/logger.js": 41, - "./media-info.js": 7, - "./transmuxing-events.js": 13, - events: 2 - }], - 13: [function(e, t, n) { - "use strict"; - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var i = { - IO_ERROR: "io_error", - DEMUX_ERROR: "demux_error", - INIT_SEGMENT: "init_segment", - MEDIA_SEGMENT: "media_segment", - LOADING_COMPLETE: "loading_complete", - RECOVERED_EARLY_EOF: "recovered_early_eof", - MEDIA_INFO: "media_info", - STATISTICS_INFO: "statistics_info", - RECOMMEND_SEEKPOINT: "recommend_seekpoint" - }; - n.default = i - }, {}], - 14: [function(e, t, n) { - "use strict"; - - function i(e) { - return e && e.__esModule ? e : { - default: e - } - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var r = e("../utils/logger.js"), - s = (i(r), e("../utils/logging-control.js")), - a = i(s), - o = e("../utils/polyfill.js"), - u = i(o), - l = e("./transmuxing-controller.js"), - d = i(l), - h = e("./transmuxing-events.js"), - f = i(h), - c = function(e) { - function t(t, n) { - var i = { - msg: f.default.INIT_SEGMENT, - data: { - type: t, - data: n - } - }; - e.postMessage(i, [n.data]) - } - - function n(t, n) { - var i = { - msg: f.default.MEDIA_SEGMENT, - data: { - type: t, - data: n - } - }; - e.postMessage(i, [n.data]) - } - - function i() { - var t = { - msg: f.default.LOADING_COMPLETE - }; - e.postMessage(t) - } - - function r() { - var t = { - msg: f.default.RECOVERED_EARLY_EOF - }; - e.postMessage(t) - } - - function s(t) { - var n = { - msg: f.default.MEDIA_INFO, - data: t - }; - e.postMessage(n) - } - - function o(t) { - var n = { - msg: f.default.STATISTICS_INFO, - data: t - }; - e.postMessage(n) - } - - function l(t, n) { - e.postMessage({ - msg: f.default.IO_ERROR, - data: { - type: t, - info: n - } - }) - } - - function h(t, n) { - e.postMessage({ - msg: f.default.DEMUX_ERROR, - data: { - type: t, - info: n - } - }) - } - - function c(t) { - e.postMessage({ - msg: f.default.RECOMMEND_SEEKPOINT, - data: t - }) - } - - function _(t, n) { - e.postMessage({ - msg: "logcat_callback", - data: { - type: t, - logcat: n - } - }) - } - var m = null, - p = _.bind(this); - u.default.install(), e.addEventListener("message", function(u) { - switch (u.data.cmd) { - case "init": - m = new d.default(u.data.param[0], u.data.param[1]), m.on(f.default.IO_ERROR, l.bind(this)), m.on(f.default.DEMUX_ERROR, h.bind(this)), m.on(f.default.INIT_SEGMENT, t.bind(this)), m.on(f.default.MEDIA_SEGMENT, n.bind(this)), m.on(f.default.LOADING_COMPLETE, i.bind(this)), m.on(f.default.RECOVERED_EARLY_EOF, r.bind(this)), m.on(f.default.MEDIA_INFO, s.bind(this)), m.on(f.default.STATISTICS_INFO, o.bind(this)), m.on(f.default.RECOMMEND_SEEKPOINT, c.bind(this)); - break; - case "destroy": - m && (m.destroy(), m = null), e.postMessage({ - msg: "destroyed" - }); - break; - case "start": - m.start(); - break; - case "stop": - m.stop(); - break; - case "seek": - m.seek(u.data.param); - break; - case "pause": - m.pause(); - break; - case "resume": - m.resume(); - break; - case "logging_config": - var _ = u.data.param; - a.default.applyConfig(_), !0 === _.enableCallback ? a.default.addLogListener(p) : a.default.removeLogListener(p) - } - }) - }; - n.default = c - }, { - "../utils/logger.js": 41, - "../utils/logging-control.js": 42, - "../utils/polyfill.js": 43, - "./transmuxing-controller.js": 12, - "./transmuxing-events.js": 13 - }], - 15: [function(e, t, n) { - "use strict"; - - function i(e) { - return e && e.__esModule ? e : { - default: e - } - } - - function r(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var s = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - a = e("../utils/logger.js"), - o = i(a), - u = e("../utils/utf8-conv.js"), - l = i(u), - d = e("../utils/exception.js"), - h = function() { - var e = new ArrayBuffer(2); - return new DataView(e).setInt16(0, 256, !0), 256 === new Int16Array(e)[0] - }(), - f = function() { - function e() { - r(this, e) - } - return s(e, null, [{ - key: "parseScriptData", - value: function(t, n, i) { - var r = {}; - try { - var s = e.parseValue(t, n, i), - a = e.parseValue(t, n + s.size, i - s.size); - r[s.data] = a.data - } catch (e) { - o.default.e("AMF", e.toString()) - } - return r - } - }, { - key: "parseObject", - value: function(t, n, i) { - if (i < 3) throw new d.IllegalStateException("Data not enough when parse ScriptDataObject"); - var r = e.parseString(t, n, i), - s = e.parseValue(t, n + r.size, i - r.size), - a = s.objectEnd; - return { - data: { - name: r.data, - value: s.data - }, - size: r.size + s.size, - objectEnd: a - } - } - }, { - key: "parseVariable", - value: function(t, n, i) { - return e.parseObject(t, n, i) - } - }, { - key: "parseString", - value: function(e, t, n) { - if (n < 2) throw new d.IllegalStateException("Data not enough when parse String"); - var i = new DataView(e, t, n), - r = i.getUint16(0, !h), - s = void 0; - return s = r > 0 ? (0, l.default)(new Uint8Array(e, t + 2, r)) : "", { - data: s, - size: 2 + r - } - } - }, { - key: "parseLongString", - value: function(e, t, n) { - if (n < 4) throw new d.IllegalStateException("Data not enough when parse LongString"); - var i = new DataView(e, t, n), - r = i.getUint32(0, !h), - s = void 0; - return s = r > 0 ? (0, l.default)(new Uint8Array(e, t + 4, r)) : "", { - data: s, - size: 4 + r - } - } - }, { - key: "parseDate", - value: function(e, t, n) { - if (n < 10) throw new d.IllegalStateException("Data size invalid when parse Date"); - var i = new DataView(e, t, n), - r = i.getFloat64(0, !h); - return r += 60 * i.getInt16(8, !h) * 1e3, { - data: new Date(r), - size: 10 - } - } - }, { - key: "parseValue", - value: function(t, n, i) { - if (i < 1) throw new d.IllegalStateException("Data not enough when parse Value"); - var r = new DataView(t, n, i), - s = 1, - a = r.getUint8(0), - u = void 0, - l = !1; - try { - switch (a) { - case 0: - u = r.getFloat64(1, !h), s += 8; - break; - case 1: - u = !!r.getUint8(1), s += 1; - break; - case 2: - var f = e.parseString(t, n + 1, i - 1); - u = f.data, s += f.size; - break; - case 3: - u = {}; - var c = 0; - for (9 == (16777215 & r.getUint32(i - 4, !h)) && (c = 3); s < i - 4;) { - var _ = e.parseObject(t, n + s, i - s - c); - if (_.objectEnd) break; - u[_.data.name] = _.data.value, s += _.size - } - if (s <= i - 3) { - 9 === (16777215 & r.getUint32(s - 1, !h)) && (s += 3) - } - break; - case 8: - u = {}, s += 4; - var m = 0; - for (9 == (16777215 & r.getUint32(i - 4, !h)) && (m = 3); s < i - 8;) { - var p = e.parseVariable(t, n + s, i - s - m); - if (p.objectEnd) break; - u[p.data.name] = p.data.value, s += p.size - } - if (s <= i - 3) { - 9 === (16777215 & r.getUint32(s - 1, !h)) && (s += 3) - } - break; - case 9: - u = void 0, s = 1, l = !0; - break; - case 10: - u = []; - var v = r.getUint32(1, !h); - s += 4; - for (var g = 0; g < v; g++) { - var y = e.parseValue(t, n + s, i - s); - u.push(y.data), s += y.size - } - break; - case 11: - var E = e.parseDate(t, n + 1, i - 1); - u = E.data, s += E.size; - break; - case 12: - var b = e.parseString(t, n + 1, i - 1); - u = b.data, s += b.size; - break; - default: - s = i, o.default.w("AMF", "Unsupported AMF value type " + a) - } - } catch (e) { - o.default.e("AMF", e.toString()) - } - return { - data: u, - size: s, - objectEnd: l - } - } - }]), e - }(); - n.default = f - }, { - "../utils/exception.js": 40, - "../utils/logger.js": 41, - "../utils/utf8-conv.js": 44 - }], - 16: [function(e, t, n) { - "use strict"; - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var i = { - OK: "OK", - FORMAT_ERROR: "FormatError", - FORMAT_UNSUPPORTED: "FormatUnsupported", - CODEC_UNSUPPORTED: "CodecUnsupported" - }; - n.default = i - }, {}], - 17: [function(e, t, n) { - "use strict"; - - function i(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var r = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - s = e("../utils/exception.js"), - a = function() { - function e(t) { - i(this, e), this.TAG = "ExpGolomb", this._buffer = t, this._buffer_index = 0, this._total_bytes = t.byteLength, this._total_bits = 8 * t.byteLength, this._current_word = 0, this._current_word_bits_left = 0 - } - return r(e, [{ - key: "destroy", - value: function() { - this._buffer = null - } - }, { - key: "_fillCurrentWord", - value: function() { - var e = this._total_bytes - this._buffer_index; - if (e <= 0) throw new s.IllegalStateException("ExpGolomb: _fillCurrentWord() but no bytes available"); - var t = Math.min(4, e), - n = new Uint8Array(4); - n.set(this._buffer.subarray(this._buffer_index, this._buffer_index + t)), this._current_word = new DataView(n.buffer).getUint32(0, !1), this._buffer_index += t, this._current_word_bits_left = 8 * t - } - }, { - key: "readBits", - value: function(e) { - if (e > 32) throw new s.InvalidArgumentException("ExpGolomb: readBits() bits exceeded max 32bits!"); - if (e <= this._current_word_bits_left) { - var t = this._current_word >>> 32 - e; - return this._current_word <<= e, this._current_word_bits_left -= e, t - } - var n = this._current_word_bits_left ? this._current_word : 0; - n >>>= 32 - this._current_word_bits_left; - var i = e - this._current_word_bits_left; - this._fillCurrentWord(); - var r = Math.min(i, this._current_word_bits_left), - a = this._current_word >>> 32 - r; - return this._current_word <<= r, this._current_word_bits_left -= r, n = n << r | a - } - }, { - key: "readBool", - value: function() { - return 1 === this.readBits(1) - } - }, { - key: "readByte", - value: function() { - return this.readBits(8) - } - }, { - key: "_skipLeadingZero", - value: function() { - var e = void 0; - for (e = 0; e < this._current_word_bits_left; e++) - if (0 != (this._current_word & 2147483648 >>> e)) return this._current_word <<= e, this._current_word_bits_left -= e, e; - return this._fillCurrentWord(), e + this._skipLeadingZero() - } - }, { - key: "readUEG", - value: function() { - var e = this._skipLeadingZero(); - return this.readBits(e + 1) - 1 - } - }, { - key: "readSEG", - value: function() { - var e = this.readUEG(); - return 1 & e ? e + 1 >>> 1 : -1 * (e >>> 1) - } - }]), e - }(); - n.default = a - }, { - "../utils/exception.js": 40 - }], - 18: [function(e, t, n) { - "use strict"; - - function i(e) { - return e && e.__esModule ? e : { - default: e - } - } - - function r(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - - function s(e, t) { - return e[t] << 24 | e[t + 1] << 16 | e[t + 2] << 8 | e[t + 3] - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var a = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(e) { - return typeof e - } : function(e) { - return e && "function" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : typeof e - }, - o = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - u = e("../utils/logger.js"), - l = i(u), - d = e("./amf-parser.js"), - h = i(d), - f = e("./sps-parser.js"), - c = i(f), - _ = e("./demux-errors.js"), - m = i(_), - p = e("../core/media-info.js"), - v = i(p), - g = e("../utils/exception.js"), - y = function() { - function e(t, n) { - r(this, e), this.TAG = "FLVDemuxer", this._config = n, this._onError = null, this._onMediaInfo = null, this._onTrackMetadata = null, this._onDataAvailable = null, this._dataOffset = t.dataOffset, this._firstParse = !0, this._dispatch = !1, this._hasAudio = t.hasAudioTrack, this._hasVideo = t.hasVideoTrack, this._hasAudioFlagOverrided = !1, this._hasVideoFlagOverrided = !1, this._audioInitialMetadataDispatched = !1, this._videoInitialMetadataDispatched = !1, this._mediaInfo = new v.default, this._mediaInfo.hasAudio = this._hasAudio, this._mediaInfo.hasVideo = this._hasVideo, this._metadata = null, this._audioMetadata = null, this._videoMetadata = null, this._naluLengthSize = 4, this._timestampBase = 0, this._timescale = 1e3, this._duration = 0, this._durationOverrided = !1, this._referenceFrameRate = { - fixed: !0, - fps: 23.976, - fps_num: 23976, - fps_den: 1e3 - }, this._flvSoundRateTable = [5500, 11025, 22050, 44100, 48e3], this._mpegSamplingRates = [96e3, 88200, 64e3, 48e3, 44100, 32e3, 24e3, 22050, 16e3, 12e3, 11025, 8e3, 7350], this._mpegAudioV10SampleRateTable = [44100, 48e3, 32e3, 0], this._mpegAudioV20SampleRateTable = [22050, 24e3, 16e3, 0], this._mpegAudioV25SampleRateTable = [11025, 12e3, 8e3, 0], this._mpegAudioL1BitRateTable = [0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1], this._mpegAudioL2BitRateTable = [0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1], this._mpegAudioL3BitRateTable = [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1], this._videoTrack = { - type: "video", - id: 1, - sequenceNumber: 0, - samples: [], - length: 0 - }, this._audioTrack = { - type: "audio", - id: 2, - sequenceNumber: 0, - samples: [], - length: 0 - }, this._littleEndian = function() { - var e = new ArrayBuffer(2); - return new DataView(e).setInt16(0, 256, !0), 256 === new Int16Array(e)[0] - }() - } - return o(e, [{ - key: "destroy", - value: function() { - this._mediaInfo = null, this._metadata = null, this._audioMetadata = null, this._videoMetadata = null, this._videoTrack = null, this._audioTrack = null, this._onError = null, this._onMediaInfo = null, this._onTrackMetadata = null, this._onDataAvailable = null - } - }, { - key: "bindDataSource", - value: function(e) { - return e.onDataArrival = this.parseChunks.bind(this), this - } - }, { - key: "resetMediaInfo", - value: function() { - this._mediaInfo = new v.default - } - }, { - key: "_isInitialMetadataDispatched", - value: function() { - return this._hasAudio && this._hasVideo ? this._audioInitialMetadataDispatched && this._videoInitialMetadataDispatched : this._hasAudio && !this._hasVideo ? this._audioInitialMetadataDispatched : !(this._hasAudio || !this._hasVideo) && this._videoInitialMetadataDispatched - } - }, { - key: "parseChunks", - value: function(t, n) { - if (!(this._onError && this._onMediaInfo && this._onTrackMetadata && this._onDataAvailable)) throw new g.IllegalStateException("Flv: onError & onMediaInfo & onTrackMetadata & onDataAvailable callback must be specified"); - var i = 0, - r = this._littleEndian; - if (0 === n) { - if (!(t.byteLength > 13)) return 0; - i = e.probe(t).dataOffset - } - if (this._firstParse) { - this._firstParse = !1, n + i !== this._dataOffset && l.default.w(this.TAG, "First time parsing but chunk byteStart invalid!"); - 0 !== new DataView(t, i).getUint32(0, !r) && l.default.w(this.TAG, "PrevTagSize0 !== 0 !!!"), i += 4 - } - for (; i < t.byteLength;) { - this._dispatch = !0; - var s = new DataView(t, i); - if (i + 11 + 4 > t.byteLength) break; - var a = s.getUint8(0), - o = 16777215 & s.getUint32(0, !r); - if (i + 11 + o + 4 > t.byteLength) break; - if (8 === a || 9 === a || 18 === a) { - var u = s.getUint8(4), - d = s.getUint8(5), - h = s.getUint8(6), - f = s.getUint8(7), - c = h | d << 8 | u << 16 | f << 24; - 0 !== (16777215 & s.getUint32(7, !r)) && l.default.w(this.TAG, "Meet tag which has StreamID != 0!"); - var _ = i + 11; - switch (a) { - case 8: - this._parseAudioData(t, _, o, c); - break; - case 9: - this._parseVideoData(t, _, o, c, n + i); - break; - case 18: - this._parseScriptData(t, _, o) - } - var m = s.getUint32(11 + o, !r); - m !== 11 + o && l.default.w(this.TAG, "Invalid PrevTagSize " + m), i += 11 + o + 4 - } else l.default.w(this.TAG, "Unsupported tag type " + a + ", skipped"), i += 11 + o + 4 - } - return this._isInitialMetadataDispatched() && this._dispatch && (this._audioTrack.length || this._videoTrack.length) && this._onDataAvailable(this._audioTrack, this._videoTrack), i - } - }, { - key: "_parseScriptData", - value: function(e, t, n) { - var i = h.default.parseScriptData(e, t, n); - if (i.hasOwnProperty("onMetaData")) { - if (null == i.onMetaData || "object" !== a(i.onMetaData)) return void l.default.w(this.TAG, "Invalid onMetaData structure!"); - this._metadata && l.default.w(this.TAG, "Found another onMetaData tag!"), this._metadata = i; - var r = this._metadata.onMetaData; - if ("boolean" == typeof r.hasAudio && !1 === this._hasAudioFlagOverrided && (this._hasAudio = r.hasAudio, this._mediaInfo.hasAudio = this._hasAudio), "boolean" == typeof r.hasVideo && !1 === this._hasVideoFlagOverrided && (this._hasVideo = r.hasVideo, this._mediaInfo.hasVideo = this._hasVideo), "number" == typeof r.audiodatarate && (this._mediaInfo.audioDataRate = r.audiodatarate), "number" == typeof r.videodatarate && (this._mediaInfo.videoDataRate = r.videodatarate), "number" == typeof r.width && (this._mediaInfo.width = r.width), "number" == typeof r.height && (this._mediaInfo.height = r.height), "number" == typeof r.duration) { - if (!this._durationOverrided) { - var s = Math.floor(r.duration * this._timescale); - this._duration = s, this._mediaInfo.duration = s - } - } else this._mediaInfo.duration = 0; - if ("number" == typeof r.framerate) { - var o = Math.floor(1e3 * r.framerate); - if (o > 0) { - var u = o / 1e3; - this._referenceFrameRate.fixed = !0, this._referenceFrameRate.fps = u, this._referenceFrameRate.fps_num = o, this._referenceFrameRate.fps_den = 1e3, this._mediaInfo.fps = u - } - } - if ("object" === a(r.keyframes)) { - this._mediaInfo.hasKeyframesIndex = !0; - var d = r.keyframes; - this._mediaInfo.keyframesIndex = this._parseKeyframesIndex(d), r.keyframes = null - } else this._mediaInfo.hasKeyframesIndex = !1; - this._dispatch = !1, this._mediaInfo.metadata = r, l.default.v(this.TAG, "Parsed onMetaData"), this._mediaInfo.isComplete() && this._onMediaInfo(this._mediaInfo) - } - } - }, { - key: "_parseKeyframesIndex", - value: function(e) { - for (var t = [], n = [], i = 1; i < e.times.length; i++) { - var r = this._timestampBase + Math.floor(1e3 * e.times[i]); - t.push(r), n.push(e.filepositions[i]) - } - return { - times: t, - filepositions: n - } - } - }, { - key: "_parseAudioData", - value: function(e, t, n, i) { - if (n <= 1) return void l.default.w(this.TAG, "Flv: Invalid audio packet, missing SoundData payload!"); - if (!0 !== this._hasAudioFlagOverrided || !1 !== this._hasAudio) { - var r = (this._littleEndian, new DataView(e, t, n)), - s = r.getUint8(0), - a = s >>> 4; - if (2 !== a && 10 !== a) return void this._onError(m.default.CODEC_UNSUPPORTED, "Flv: Unsupported audio codec idx: " + a); - var o = 0, - u = (12 & s) >>> 2; - if (!(u >= 0 && u <= 4)) return void this._onError(m.default.FORMAT_ERROR, "Flv: Invalid audio sample rate idx: " + u); - o = this._flvSoundRateTable[u]; - var d = 1 & s, - h = this._audioMetadata, - f = this._audioTrack; - if (h || (!1 === this._hasAudio && !1 === this._hasAudioFlagOverrided && (this._hasAudio = !0, this._mediaInfo.hasAudio = !0), h = this._audioMetadata = {}, h.type = "audio", h.id = f.id, h.timescale = this._timescale, h.duration = this._duration, h.audioSampleRate = o, h.channelCount = 0 === d ? 1 : 2), 10 === a) { - var c = this._parseAACAudioData(e, t + 1, n - 1); - if (void 0 == c) return; - if (0 === c.packetType) { - h.config && l.default.w(this.TAG, "Found another AudioSpecificConfig!"); - var _ = c.data; - h.audioSampleRate = _.samplingRate, h.channelCount = _.channelCount, h.codec = _.codec, h.originalCodec = _.originalCodec, h.config = _.config, h.refSampleDuration = 1024 / h.audioSampleRate * h.timescale, l.default.v(this.TAG, "Parsed AudioSpecificConfig"), this._isInitialMetadataDispatched() ? this._dispatch && (this._audioTrack.length || this._videoTrack.length) && this._onDataAvailable(this._audioTrack, this._videoTrack) : this._audioInitialMetadataDispatched = !0, this._dispatch = !1, this._onTrackMetadata("audio", h); - var p = this._mediaInfo; - p.audioCodec = h.originalCodec, p.audioSampleRate = h.audioSampleRate, p.audioChannelCount = h.channelCount, p.hasVideo ? null != p.videoCodec && (p.mimeType = 'video/x-flv; codecs="' + p.videoCodec + "," + p.audioCodec + '"') : p.mimeType = 'video/x-flv; codecs="' + p.audioCodec + '"', p.isComplete() && this._onMediaInfo(p) - } else if (1 === c.packetType) { - var v = this._timestampBase + i, - g = { - unit: c.data, - length: c.data.byteLength, - dts: v, - pts: v - }; - f.samples.push(g), f.length += c.data.length - } else l.default.e(this.TAG, "Flv: Unsupported AAC data type " + c.packetType) - } else if (2 === a) { - if (!h.codec) { - var y = this._parseMP3AudioData(e, t + 1, n - 1, !0); - if (void 0 == y) return; - h.audioSampleRate = y.samplingRate, h.channelCount = y.channelCount, h.codec = y.codec, h.originalCodec = y.originalCodec, h.refSampleDuration = 1152 / h.audioSampleRate * h.timescale, l.default.v(this.TAG, "Parsed MPEG Audio Frame Header"), this._audioInitialMetadataDispatched = !0, this._onTrackMetadata("audio", h); - var E = this._mediaInfo; - E.audioCodec = h.codec, E.audioSampleRate = h.audioSampleRate, E.audioChannelCount = h.channelCount, E.audioDataRate = y.bitRate, E.hasVideo ? null != E.videoCodec && (E.mimeType = 'video/x-flv; codecs="' + E.videoCodec + "," + E.audioCodec + '"') : E.mimeType = 'video/x-flv; codecs="' + E.audioCodec + '"', E.isComplete() && this._onMediaInfo(E) - } - var b = this._parseMP3AudioData(e, t + 1, n - 1, !1); - if (void 0 == b) return; - var S = this._timestampBase + i, - k = { - unit: b, - length: b.byteLength, - dts: S, - pts: S - }; - f.samples.push(k), f.length += b.length - } - } - } - }, { - key: "_parseAACAudioData", - value: function(e, t, n) { - if (n <= 1) return void l.default.w(this.TAG, "Flv: Invalid AAC packet, missing AACPacketType or/and Data!"); - var i = {}, - r = new Uint8Array(e, t, n); - return i.packetType = r[0], 0 === r[0] ? i.data = this._parseAACAudioSpecificConfig(e, t + 1, n - 1) : i.data = r.subarray(1), i - } - }, { - key: "_parseAACAudioSpecificConfig", - value: function(e, t, n) { - var i = new Uint8Array(e, t, n), - r = null, - s = 0, - a = 0, - o = 0, - u = null; - if (s = a = i[0] >>> 3, (o = (7 & i[0]) << 1 | i[1] >>> 7) < 0 || o >= this._mpegSamplingRates.length) return void this._onError(m.default.FORMAT_ERROR, "Flv: AAC invalid sampling frequency index!"); - var l = this._mpegSamplingRates[o], - d = (120 & i[1]) >>> 3; - if (d < 0 || d >= 8) return void this._onError(m.default.FORMAT_ERROR, "Flv: AAC invalid channel configuration"); - 5 === s && (u = (7 & i[1]) << 1 | i[2] >>> 7, i[2]); - var h = self.navigator.userAgent.toLowerCase(); - return -1 !== h.indexOf("firefox") ? o >= 6 ? (s = 5, r = new Array(4), u = o - 3) : (s = 2, r = new Array(2), u = o) : -1 !== h.indexOf("android") ? (s = 2, r = new Array(2), u = o) : (s = 5, u = o, r = new Array(4), o >= 6 ? u = o - 3 : 1 === d && (s = 2, r = new Array(2), u = o)), r[0] = s << 3, r[0] |= (15 & o) >>> 1, r[1] = (15 & o) << 7, r[1] |= (15 & d) << 3, 5 === s && (r[1] |= (15 & u) >>> 1, r[2] = (1 & u) << 7, r[2] |= 8, r[3] = 0), { - config: r, - samplingRate: l, - channelCount: d, - codec: "mp4a.40." + s, - originalCodec: "mp4a.40." + a - } - } - }, { - key: "_parseMP3AudioData", - value: function(e, t, n, i) { - if (n < 4) return void l.default.w(this.TAG, "Flv: Invalid MP3 packet, header missing!"); - var r = (this._littleEndian, new Uint8Array(e, t, n)), - s = null; - if (i) { - if (255 !== r[0]) return; - var a = r[1] >>> 3 & 3, - o = (6 & r[1]) >> 1, - u = (240 & r[2]) >>> 4, - d = (12 & r[2]) >>> 2, - h = r[3] >>> 6 & 3, - f = 3 !== h ? 2 : 1, - c = 0, - _ = 0; - switch (a) { - case 0: - c = this._mpegAudioV25SampleRateTable[d]; - break; - case 2: - c = this._mpegAudioV20SampleRateTable[d]; - break; - case 3: - c = this._mpegAudioV10SampleRateTable[d] - } - switch (o) { - case 1: - 34, u < this._mpegAudioL3BitRateTable.length && (_ = this._mpegAudioL3BitRateTable[u]); - break; - case 2: - 33, u < this._mpegAudioL2BitRateTable.length && (_ = this._mpegAudioL2BitRateTable[u]); - break; - case 3: - 32, u < this._mpegAudioL1BitRateTable.length && (_ = this._mpegAudioL1BitRateTable[u]) - } - s = { - bitRate: _, - samplingRate: c, - channelCount: f, - codec: "mp3", - originalCodec: "mp3" - } - } else s = r; - return s - } - }, { - key: "_parseVideoData", - value: function(e, t, n, i, r) { - if (n <= 1) return void l.default.w(this.TAG, "Flv: Invalid video packet, missing VideoData payload!"); - if (!0 !== this._hasVideoFlagOverrided || !1 !== this._hasVideo) { - var s = new Uint8Array(e, t, n)[0], - a = (240 & s) >>> 4, - o = 15 & s; - if (7 !== o) return void this._onError(m.default.CODEC_UNSUPPORTED, "Flv: Unsupported codec in video frame: " + o); - this._parseAVCVideoPacket(e, t + 1, n - 1, i, r, a) - } - } - }, { - key: "_parseAVCVideoPacket", - value: function(e, t, n, i, r, s) { - if (n < 4) return void l.default.w(this.TAG, "Flv: Invalid AVC packet, missing AVCPacketType or/and CompositionTime"); - var a = this._littleEndian, - o = new DataView(e, t, n), - u = o.getUint8(0), - d = 16777215 & o.getUint32(0, !a), - h = d << 8 >> 8; - if (0 === u) this._parseAVCDecoderConfigurationRecord(e, t + 4, n - 4); - else if (1 === u) this._parseAVCVideoData(e, t + 4, n - 4, i, r, s, h); - else if (2 !== u) return void this._onError(m.default.FORMAT_ERROR, "Flv: Invalid video packet type " + u) - } - }, { - key: "_parseAVCDecoderConfigurationRecord", - value: function(e, t, n) { - if (n < 7) return void l.default.w(this.TAG, "Flv: Invalid AVCDecoderConfigurationRecord, lack of data!"); - var i = this._videoMetadata, - r = this._videoTrack, - s = this._littleEndian, - a = new DataView(e, t, n); - i ? void 0 !== i.avcc && l.default.w(this.TAG, "Found another AVCDecoderConfigurationRecord!") : (!1 === this._hasVideo && !1 === this._hasVideoFlagOverrided && (this._hasVideo = !0, this._mediaInfo.hasVideo = !0), i = this._videoMetadata = {}, i.type = "video", i.id = r.id, i.timescale = this._timescale, i.duration = this._duration); - var o = a.getUint8(0), - u = a.getUint8(1); - a.getUint8(2), a.getUint8(3); - if (1 !== o || 0 === u) return void this._onError(m.default.FORMAT_ERROR, "Flv: Invalid AVCDecoderConfigurationRecord"); - if (this._naluLengthSize = 1 + (3 & a.getUint8(4)), 3 !== this._naluLengthSize && 4 !== this._naluLengthSize) return void this._onError(m.default.FORMAT_ERROR, "Flv: Strange NaluLengthSizeMinusOne: " + (this._naluLengthSize - 1)); - var d = 31 & a.getUint8(5); - if (0 === d) return void this._onError(m.default.FORMAT_ERROR, "Flv: Invalid AVCDecoderConfigurationRecord: No SPS"); - d > 1 && l.default.w(this.TAG, "Flv: Strange AVCDecoderConfigurationRecord: SPS Count = " + d); - for (var h = 6, f = 0; f < d; f++) { - var _ = a.getUint16(h, !s); - if (h += 2, 0 !== _) { - var p = new Uint8Array(e, t + h, _); - h += _; - var v = c.default.parseSPS(p); - if (0 === f) { - i.codecWidth = v.codec_size.width, i.codecHeight = v.codec_size.height, i.presentWidth = v.present_size.width, i.presentHeight = v.present_size.height, i.profile = v.profile_string, i.level = v.level_string, i.bitDepth = v.bit_depth, i.chromaFormat = v.chroma_format, i.sarRatio = v.sar_ratio, i.frameRate = v.frame_rate, !1 !== v.frame_rate.fixed && 0 !== v.frame_rate.fps_num && 0 !== v.frame_rate.fps_den || (i.frameRate = this._referenceFrameRate); - var g = i.frameRate.fps_den, - y = i.frameRate.fps_num; - i.refSampleDuration = i.timescale * (g / y); - for (var E = p.subarray(1, 4), b = "avc1.", S = 0; S < 3; S++) { - var k = E[S].toString(16); - k.length < 2 && (k = "0" + k), b += k - } - i.codec = b; - var L = this._mediaInfo; - L.width = i.codecWidth, L.height = i.codecHeight, L.fps = i.frameRate.fps, L.profile = i.profile, L.level = i.level, L.refFrames = v.ref_frames, L.chromaFormat = v.chroma_format_string, L.sarNum = i.sarRatio.width, L.sarDen = i.sarRatio.height, L.videoCodec = b, L.hasAudio ? null != L.audioCodec && (L.mimeType = 'video/x-flv; codecs="' + L.videoCodec + "," + L.audioCodec + '"') : L.mimeType = 'video/x-flv; codecs="' + L.videoCodec + '"', L.isComplete() && this._onMediaInfo(L) - } - } - } - var w = a.getUint8(h); - if (0 === w) return void this._onError(m.default.FORMAT_ERROR, "Flv: Invalid AVCDecoderConfigurationRecord: No PPS"); - w > 1 && l.default.w(this.TAG, "Flv: Strange AVCDecoderConfigurationRecord: PPS Count = " + w), h++; - for (var R = 0; R < w; R++) { - var A = a.getUint16(h, !s); - h += 2, 0 !== A && (h += A) - } - i.avcc = new Uint8Array(n), i.avcc.set(new Uint8Array(e, t, n), 0), l.default.v(this.TAG, "Parsed AVCDecoderConfigurationRecord"), this._isInitialMetadataDispatched() ? this._dispatch && (this._audioTrack.length || this._videoTrack.length) && this._onDataAvailable(this._audioTrack, this._videoTrack) : this._videoInitialMetadataDispatched = !0, this._dispatch = !1, this._onTrackMetadata("video", i) - } - }, { - key: "_parseAVCVideoData", - value: function(e, t, n, i, r, s, a) { - for (var o = this._littleEndian, u = new DataView(e, t, n), d = [], h = 0, f = 0, c = this._naluLengthSize, _ = this._timestampBase + i, m = 1 === s; f < n;) { - if (f + 4 >= n) { - l.default.w(this.TAG, "Malformed Nalu near timestamp " + _ + ", offset = " + f + ", dataSize = " + n); - break - } - var p = u.getUint32(f, !o); - if (3 === c && (p >>>= 8), p > n - c) return void l.default.w(this.TAG, "Malformed Nalus near timestamp " + _ + ", NaluSize > DataSize!"); - var v = 31 & u.getUint8(f + c); - 5 === v && (m = !0); - var g = new Uint8Array(e, t + f, c + p), - y = { - type: v, - data: g - }; - d.push(y), h += g.byteLength, f += c + p - } - if (d.length) { - var E = this._videoTrack, - b = { - units: d, - length: h, - isKeyframe: m, - dts: _, - cts: a, - pts: _ + a - }; - m && (b.fileposition = r), E.samples.push(b), E.length += h - } - } - }, { - key: "onTrackMetadata", - get: function() { - return this._onTrackMetadata - }, - set: function(e) { - this._onTrackMetadata = e - } - }, { - key: "onMediaInfo", - get: function() { - return this._onMediaInfo - }, - set: function(e) { - this._onMediaInfo = e - } - }, { - key: "onError", - get: function() { - return this._onError - }, - set: function(e) { - this._onError = e - } - }, { - key: "onDataAvailable", - get: function() { - return this._onDataAvailable - }, - set: function(e) { - this._onDataAvailable = e - } - }, { - key: "timestampBase", - get: function() { - return this._timestampBase - }, - set: function(e) { - this._timestampBase = e - } - }, { - key: "overridedDuration", - get: function() { - return this._duration - }, - set: function(e) { - this._durationOverrided = !0, this._duration = e, this._mediaInfo.duration = e - } - }, { - key: "overridedHasAudio", - set: function(e) { - this._hasAudioFlagOverrided = !0, this._hasAudio = e, this._mediaInfo.hasAudio = e - } - }, { - key: "overridedHasVideo", - set: function(e) { - this._hasVideoFlagOverrided = !0, this._hasVideo = e, this._mediaInfo.hasVideo = e - } - }], [{ - key: "probe", - value: function(e) { - var t = new Uint8Array(e), - n = { - match: !1 - }; - if (70 !== t[0] || 76 !== t[1] || 86 !== t[2] || 1 !== t[3]) return n; - var i = (4 & t[4]) >>> 2 != 0, - r = 0 != (1 & t[4]), - a = s(t, 5); - return a < 9 ? n : { - match: !0, - consumed: a, - dataOffset: a, - hasAudioTrack: i, - hasVideoTrack: r - } - } - }]), e - }(); - n.default = y - }, { - "../core/media-info.js": 7, - "../utils/exception.js": 40, - "../utils/logger.js": 41, - "./amf-parser.js": 15, - "./demux-errors.js": 16, - "./sps-parser.js": 19 - }], - 19: [function(e, t, n) { - "use strict"; - - function i(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var r = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - s = e("./exp-golomb.js"), - a = function(e) { - return e && e.__esModule ? e : { - default: e - } - }(s), - o = function() { - function e() { - i(this, e) - } - return r(e, null, [{ - key: "_ebsp2rbsp", - value: function(e) { - for (var t = e, n = t.byteLength, i = new Uint8Array(n), r = 0, s = 0; s < n; s++) s >= 2 && 3 === t[s] && 0 === t[s - 1] && 0 === t[s - 2] || (i[r] = t[s], r++); - return new Uint8Array(i.buffer, 0, r) - } - }, { - key: "parseSPS", - value: function(t) { - var n = e._ebsp2rbsp(t), - i = new a.default(n); - i.readByte(); - var r = i.readByte(); - i.readByte(); - var s = i.readByte(); - i.readUEG(); - var o = e.getProfileString(r), - u = e.getLevelString(s), - l = 1, - d = 420, - h = [0, 420, 422, 444], - f = 8; - if ((100 === r || 110 === r || 122 === r || 244 === r || 44 === r || 83 === r || 86 === r || 118 === r || 128 === r || 138 === r || 144 === r) && (l = i.readUEG(), 3 === l && i.readBits(1), l <= 3 && (d = h[l]), f = i.readUEG() + 8, i.readUEG(), i.readBits(1), i.readBool())) - for (var c = 3 !== l ? 8 : 12, _ = 0; _ < c; _++) i.readBool() && (_ < 6 ? e._skipScalingList(i, 16) : e._skipScalingList(i, 64)); - i.readUEG(); - var m = i.readUEG(); - if (0 === m) i.readUEG(); - else if (1 === m) { - i.readBits(1), i.readSEG(), i.readSEG(); - for (var p = i.readUEG(), v = 0; v < p; v++) i.readSEG() - } - var g = i.readUEG(); - i.readBits(1); - var y = i.readUEG(), - E = i.readUEG(), - b = i.readBits(1); - 0 === b && i.readBits(1), i.readBits(1); - var S = 0, - k = 0, - L = 0, - w = 0; - i.readBool() && (S = i.readUEG(), k = i.readUEG(), L = i.readUEG(), w = i.readUEG()); - var R = 1, - A = 1, - O = 0, - T = !0, - C = 0, - I = 0; - if (i.readBool()) { - if (i.readBool()) { - var x = i.readByte(), - M = [1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160, 4, 3, 2], - D = [1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1]; - x > 0 && x < 16 ? (R = M[x - 1], A = D[x - 1]) : 255 === x && (R = i.readByte() << 8 | i.readByte(), A = i.readByte() << 8 | i.readByte()) - } - if (i.readBool() && i.readBool(), i.readBool() && (i.readBits(4), i.readBool() && i.readBits(24)), i.readBool() && (i.readUEG(), i.readUEG()), i.readBool()) { - var B = i.readBits(32), - j = i.readBits(32); - T = i.readBool(), C = j, I = 2 * B, O = C / I - } - } - var P = 1; - 1 === R && 1 === A || (P = R / A); - var U = 0, - N = 0; - if (0 === l) U = 1, N = 2 - b; - else { - var F = 3 === l ? 1 : 2, - G = 1 === l ? 2 : 1; - U = F, N = G * (2 - b) - } - var V = 16 * (y + 1), - z = 16 * (E + 1) * (2 - b); - V -= (S + k) * U, z -= (L + w) * N; - var H = Math.ceil(V * P); - return i.destroy(), i = null, { - profile_string: o, - level_string: u, - bit_depth: f, - ref_frames: g, - chroma_format: d, - chroma_format_string: e.getChromaFormatString(d), - frame_rate: { - fixed: T, - fps: O, - fps_den: I, - fps_num: C - }, - sar_ratio: { - width: R, - height: A - }, - codec_size: { - width: V, - height: z - }, - present_size: { - width: H, - height: z - } - } - } - }, { - key: "_skipScalingList", - value: function(e, t) { - for (var n = 8, i = 8, r = 0, s = 0; s < t; s++) 0 !== i && (r = e.readSEG(), i = (n + r + 256) % 256), n = 0 === i ? n : i - } - }, { - key: "getProfileString", - value: function(e) { - switch (e) { - case 66: - return "Baseline"; - case 77: - return "Main"; - case 88: - return "Extended"; - case 100: - return "High"; - case 110: - return "High10"; - case 122: - return "High422"; - case 244: - return "High444"; - default: - return "Unknown" - } - } - }, { - key: "getLevelString", - value: function(e) { - return (e / 10).toFixed(1) - } - }, { - key: "getChromaFormatString", - value: function(e) { - switch (e) { - case 420: - return "4:2:0"; - case 422: - return "4:2:2"; - case 444: - return "4:4:4"; - default: - return "Unknown" - } - } - }]), e - }(); - n.default = o - }, { - "./exp-golomb.js": 17 - }], - 20: [function(e, t, n) { - "use strict"; - - function i(e) { - return e && e.__esModule ? e : { - default: e - } - } - - function r(e, t) { - var n = e; - if (null == n || "object" !== (void 0 === n ? "undefined" : o(n))) throw new b.InvalidArgumentException("MediaDataSource must be an javascript object!"); - if (!n.hasOwnProperty("type")) throw new b.InvalidArgumentException("MediaDataSource must has type field to indicate video file type!"); - switch (n.type) { - case "flv": - return new c.default(n, t); - default: - return new m.default(n, t) - } - } - - function s() { - return h.default.supportMSEH264Playback() - } - - function a() { - return h.default.getFeatureList() - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var o = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(e) { - return typeof e - } : function(e) { - return e && "function" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : typeof e - }, - u = e("./utils/polyfill.js"), - l = i(u), - d = e("./core/features.js"), - h = i(d), - f = e("./player/flv-player.js"), - c = i(f), - _ = e("./player/native-player.js"), - m = i(_), - p = e("./player/player-events.js"), - v = i(p), - g = e("./player/player-errors.js"), - y = e("./utils/logging-control.js"), - E = i(y), - b = e("./utils/exception.js"); - l.default.install(); - var S = {}; - S.createPlayer = r, S.isSupported = s, S.getFeatureList = a, S.Events = v.default, S.ErrorTypes = g.ErrorTypes, S.ErrorDetails = g.ErrorDetails, S.FlvPlayer = c.default, S.NativePlayer = m.default, S.LoggingControl = E.default, Object.defineProperty(S, "version", { - enumerable: !0, - get: function() { - return "1.4.2" - } - }), n.default = S - }, { - "./core/features.js": 6, - "./player/flv-player.js": 32, - "./player/native-player.js": 33, - "./player/player-errors.js": 34, - "./player/player-events.js": 35, - "./utils/exception.js": 40, - "./utils/logging-control.js": 42, - "./utils/polyfill.js": 43 - }], - 21: [function(e, t, n) { - "use strict"; - t.exports = e("./flv.js").default - }, { - "./flv.js": 20 - }], - 22: [function(e, t, n) { - "use strict"; - - function i(e) { - return e && e.__esModule ? e : { - default: e - } - } - - function r(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - - function s(e, t) { - if (!e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); - return !t || "object" != typeof t && "function" != typeof t ? e : t - } - - function a(e, t) { - if ("function" != typeof t && null !== t) throw new TypeError("Super expression must either be null or a function, not " + typeof t); - e.prototype = Object.create(t && t.prototype, { - constructor: { - value: e, - enumerable: !1, - writable: !0, - configurable: !0 - } - }), t && (Object.setPrototypeOf ? Object.setPrototypeOf(e, t) : e.__proto__ = t) - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var o = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(e) { - return typeof e - } : function(e) { - return e && "function" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : typeof e - }, - u = function e(t, n, i) { - null === t && (t = Function.prototype); - var r = Object.getOwnPropertyDescriptor(t, n); - if (void 0 === r) { - var s = Object.getPrototypeOf(t); - return null === s ? void 0 : e(s, n, i) - } - if ("value" in r) return r.value; - var a = r.get; - if (void 0 !== a) return a.call(i) - }, - l = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - d = e("../utils/logger.js"), - h = (i(d), e("../utils/browser.js")), - f = i(h), - c = e("./loader.js"), - _ = e("../utils/exception.js"), - m = function(e) { - function t(e, n) { - r(this, t); - var i = s(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, "fetch-stream-loader")); - return i.TAG = "FetchStreamLoader", i._seekHandler = e, i._config = n, i._needStash = !0, i._requestAbort = !1, i._contentLength = null, i._receivedLength = 0, i - } - return a(t, e), l(t, null, [{ - key: "isSupported", - value: function() { - try { - var e = f.default.msedge && f.default.version.minor >= 15048, - t = !f.default.msedge || e; - return self.fetch && self.ReadableStream && t - } catch (e) { - return !1 - } - } - }]), l(t, [{ - key: "destroy", - value: function() { - this.isWorking() && this.abort(), u(t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), "destroy", this).call(this) - } - }, { - key: "open", - value: function(e, t) { - var n = this; - this._dataSource = e, this._range = t; - var i = e.url; - this._config.reuseRedirectedURL && void 0 != e.redirectedURL && (i = e.redirectedURL); - var r = this._seekHandler.getConfig(i, t), - s = new self.Headers; - if ("object" === o(r.headers)) { - var a = r.headers; - for (var u in a) a.hasOwnProperty(u) && s.append(u, a[u]) - } - var l = { - method: "GET", - headers: s, - mode: "cors", - cache: "default", - referrerPolicy: "no-referrer-when-downgrade" - }; - !1 === e.cors && (l.mode = "same-origin"), e.withCredentials && (l.credentials = "include"), e.referrerPolicy && (l.referrerPolicy = e.referrerPolicy), this._status = c.LoaderStatus.kConnecting, self.fetch(r.url, l).then(function(e) { - if (n._requestAbort) return n._requestAbort = !1, void(n._status = c.LoaderStatus.kIdle); - if (e.ok && e.status >= 200 && e.status <= 299) { - if (e.url !== r.url && n._onURLRedirect) { - var t = n._seekHandler.removeURLParameters(e.url); - n._onURLRedirect(t) - } - var i = e.headers.get("Content-Length"); - return null != i && (n._contentLength = parseInt(i), 0 !== n._contentLength && n._onContentLengthKnown && n._onContentLengthKnown(n._contentLength)), n._pump.call(n, e.body.getReader()) - } - if (n._status = c.LoaderStatus.kError, !n._onError) throw new _.RuntimeException("FetchStreamLoader: Http code invalid, " + e.status + " " + e.statusText); - n._onError(c.LoaderErrors.HTTP_STATUS_CODE_INVALID, { - code: e.status, - msg: e.statusText - }) - }).catch(function(e) { - if (n._status = c.LoaderStatus.kError, !n._onError) throw e; - n._onError(c.LoaderErrors.EXCEPTION, { - code: -1, - msg: e.message - }) - }) - } - }, { - key: "abort", - value: function() { - this._requestAbort = !0 - } - }, { - key: "_pump", - value: function(e) { - var t = this; - return e.read().then(function(n) { - if (n.done) - if (null !== t._contentLength && t._receivedLength < t._contentLength) { - t._status = c.LoaderStatus.kError; - var i = c.LoaderErrors.EARLY_EOF, - r = { - code: -1, - msg: "Fetch stream meet Early-EOF" - }; - if (!t._onError) throw new _.RuntimeException(r.msg); - t._onError(i, r) - } else t._status = c.LoaderStatus.kComplete, t._onComplete && t._onComplete(t._range.from, t._range.from + t._receivedLength - 1); - else { - if (!0 === t._requestAbort) return t._requestAbort = !1, t._status = c.LoaderStatus.kComplete, e.cancel(); - t._status = c.LoaderStatus.kBuffering; - var s = n.value.buffer, - a = t._range.from + t._receivedLength; - t._receivedLength += s.byteLength, t._onDataArrival && t._onDataArrival(s, a, t._receivedLength), t._pump(e) - } - }).catch(function(e) { - if (11 !== e.code || !f.default.msedge) { - t._status = c.LoaderStatus.kError; - var n = 0, - i = null; - if (19 !== e.code && "network error" !== e.message || !(null === t._contentLength || null !== t._contentLength && t._receivedLength < t._contentLength) ? (n = c.LoaderErrors.EXCEPTION, i = { - code: e.code, - msg: e.message - }) : (n = c.LoaderErrors.EARLY_EOF, i = { - code: e.code, - msg: "Fetch stream meet Early-EOF" - }), !t._onError) throw new _.RuntimeException(i.msg); - t._onError(n, i) - } - }) - } - }]), t - }(c.BaseLoader); - n.default = m - }, { - "../utils/browser.js": 39, - "../utils/exception.js": 40, - "../utils/logger.js": 41, - "./loader.js": 24 - }], - 23: [function(e, t, n) { - "use strict"; - - function i(e) { - return e && e.__esModule ? e : { - default: e - } - } - - function r(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var s = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - a = e("../utils/logger.js"), - o = i(a), - u = e("./speed-sampler.js"), - l = i(u), - d = e("./loader.js"), - h = e("./fetch-stream-loader.js"), - f = i(h), - c = e("./xhr-moz-chunked-loader.js"), - _ = i(c), - m = e("./xhr-msstream-loader.js"), - p = (i(m), e("./xhr-range-loader.js")), - v = i(p), - g = e("./websocket-loader.js"), - y = i(g), - E = e("./range-seek-handler.js"), - b = i(E), - S = e("./param-seek-handler.js"), - k = i(S), - L = e("../utils/exception.js"), - w = function() { - function e(t, n, i) { - r(this, e), this.TAG = "IOController", this._config = n, this._extraData = i, this._stashInitialSize = 393216, void 0 != n.stashInitialSize && n.stashInitialSize > 0 && (this._stashInitialSize = n.stashInitialSize), this._stashUsed = 0, this._stashSize = this._stashInitialSize, this._bufferSize = 3145728, this._stashBuffer = new ArrayBuffer(this._bufferSize), this._stashByteStart = 0, this._enableStash = !0, !1 === n.enableStashBuffer && (this._enableStash = !1), this._loader = null, this._loaderClass = null, this._seekHandler = null, this._dataSource = t, this._isWebSocketURL = /wss?:\/\/(.+?)/.test(t.url), this._refTotalLength = t.filesize ? t.filesize : null, this._totalLength = this._refTotalLength, this._fullRequestFlag = !1, this._currentRange = null, this._redirectedURL = null, this._speedNormalized = 0, this._speedSampler = new l.default, this._speedNormalizeList = [64, 128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096], this._isEarlyEofReconnecting = !1, this._paused = !1, this._resumeFrom = 0, this._onDataArrival = null, this._onSeeked = null, this._onError = null, this._onComplete = null, this._onRedirect = null, this._onRecoveredEarlyEof = null, this._selectSeekHandler(), this._selectLoader(), this._createLoader() - } - return s(e, [{ - key: "destroy", - value: function() { - this._loader.isWorking() && this._loader.abort(), this._loader.destroy(), this._loader = null, this._loaderClass = null, this._dataSource = null, this._stashBuffer = null, this._stashUsed = this._stashSize = this._bufferSize = this._stashByteStart = 0, this._currentRange = null, this._speedSampler = null, this._isEarlyEofReconnecting = !1, this._onDataArrival = null, this._onSeeked = null, this._onError = null, this._onComplete = null, this._onRedirect = null, this._onRecoveredEarlyEof = null, this._extraData = null - } - }, { - key: "isWorking", - value: function() { - return this._loader && this._loader.isWorking() && !this._paused - } - }, { - key: "isPaused", - value: function() { - return this._paused - } - }, { - key: "_selectSeekHandler", - value: function() { - var e = this._config; - if ("range" === e.seekType) this._seekHandler = new b.default(this._config.rangeLoadZeroStart); - else if ("param" === e.seekType) { - var t = e.seekParamStart || "bstart", - n = e.seekParamEnd || "bend"; - this._seekHandler = new k.default(t, n) - } else { - if ("custom" !== e.seekType) throw new L.InvalidArgumentException("Invalid seekType in config: " + e.seekType); - if ("function" != typeof e.customSeekHandler) throw new L.InvalidArgumentException("Custom seekType specified in config but invalid customSeekHandler!"); - this._seekHandler = new e.customSeekHandler - } - } - }, { - key: "_selectLoader", - value: function() { - if (this._isWebSocketURL) this._loaderClass = y.default; - else if (f.default.isSupported()) this._loaderClass = f.default; - else if (_.default.isSupported()) this._loaderClass = _.default; - else { - if (!v.default.isSupported()) throw new L.RuntimeException("Your browser doesn't support xhr with arraybuffer responseType!"); - this._loaderClass = v.default - } - } - }, { - key: "_createLoader", - value: function() { - this._loader = new this._loaderClass(this._seekHandler, this._config), !1 === this._loader.needStashBuffer && (this._enableStash = !1), this._loader.onContentLengthKnown = this._onContentLengthKnown.bind(this), this._loader.onURLRedirect = this._onURLRedirect.bind(this), this._loader.onDataArrival = this._onLoaderChunkArrival.bind(this), this._loader.onComplete = this._onLoaderComplete.bind(this), this._loader.onError = this._onLoaderError.bind(this) - } - }, { - key: "open", - value: function(e) { - this._currentRange = { - from: 0, - to: -1 - }, e && (this._currentRange.from = e), this._speedSampler.reset(), e || (this._fullRequestFlag = !0), this._loader.open(this._dataSource, Object.assign({}, this._currentRange)) - } - }, { - key: "abort", - value: function() { - this._loader.abort(), this._paused && (this._paused = !1, this._resumeFrom = 0) - } - }, { - key: "pause", - value: function() { - this.isWorking() && (this._loader.abort(), 0 !== this._stashUsed ? (this._resumeFrom = this._stashByteStart, this._currentRange.to = this._stashByteStart - 1) : this._resumeFrom = this._currentRange.to + 1, this._stashUsed = 0, this._stashByteStart = 0, this._paused = !0) - } - }, { - key: "resume", - value: function() { - if (this._paused) { - this._paused = !1; - var e = this._resumeFrom; - this._resumeFrom = 0, this._internalSeek(e, !0) - } - } - }, { - key: "seek", - value: function(e) { - this._paused = !1, this._stashUsed = 0, this._stashByteStart = 0, this._internalSeek(e, !0) - } - }, { - key: "_internalSeek", - value: function(e, t) { - this._loader.isWorking() && this._loader.abort(), this._flushStashBuffer(t), this._loader.destroy(), this._loader = null; - var n = { - from: e, - to: -1 - }; - this._currentRange = { - from: n.from, - to: -1 - }, this._speedSampler.reset(), this._stashSize = this._stashInitialSize, this._createLoader(), this._loader.open(this._dataSource, n), this._onSeeked && this._onSeeked() - } - }, { - key: "updateUrl", - value: function(e) { - if (!e || "string" != typeof e || 0 === e.length) throw new L.InvalidArgumentException("Url must be a non-empty string!"); - this._dataSource.url = e - } - }, { - key: "_expandBuffer", - value: function(e) { - for (var t = this._stashSize; t + 1048576 < e;) t *= 2; - if ((t += 1048576) !== this._bufferSize) { - var n = new ArrayBuffer(t); - if (this._stashUsed > 0) { - var i = new Uint8Array(this._stashBuffer, 0, this._stashUsed); - new Uint8Array(n, 0, t).set(i, 0) - } - this._stashBuffer = n, this._bufferSize = t - } - } - }, { - key: "_normalizeSpeed", - value: function(e) { - var t = this._speedNormalizeList, - n = t.length - 1, - i = 0, - r = 0, - s = n; - if (e < t[0]) return t[0]; - for (; r <= s;) { - if ((i = r + Math.floor((s - r) / 2)) === n || e >= t[i] && e < t[i + 1]) return t[i]; - t[i] < e ? r = i + 1 : s = i - 1 - } - } - }, { - key: "_adjustStashSize", - value: function(e) { - var t = 0; - (t = this._config.isLive ? e : e < 512 ? e : e >= 512 && e <= 1024 ? Math.floor(1.5 * e) : 2 * e) > 8192 && (t = 8192); - var n = 1024 * t + 1048576; - this._bufferSize < n && this._expandBuffer(n), this._stashSize = 1024 * t - } - }, { - key: "_dispatchChunks", - value: function(e, t) { - return this._currentRange.to = t + e.byteLength - 1, this._onDataArrival(e, t) - } - }, { - key: "_onURLRedirect", - value: function(e) { - this._redirectedURL = e, this._onRedirect && this._onRedirect(e) - } - }, { - key: "_onContentLengthKnown", - value: function(e) { - e && this._fullRequestFlag && (this._totalLength = e, this._fullRequestFlag = !1) - } - }, { - key: "_onLoaderChunkArrival", - value: function(e, t, n) { - if (!this._onDataArrival) throw new L.IllegalStateException("IOController: No existing consumer (onDataArrival) callback!"); - if (!this._paused) { - this._isEarlyEofReconnecting && (this._isEarlyEofReconnecting = !1, this._onRecoveredEarlyEof && this._onRecoveredEarlyEof()), this._speedSampler.addBytes(e.byteLength); - var i = this._speedSampler.lastSecondKBps; - if (0 !== i) { - var r = this._normalizeSpeed(i); - this._speedNormalized !== r && (this._speedNormalized = r, this._adjustStashSize(r)) - } - if (this._enableStash) - if (0 === this._stashUsed && 0 === this._stashByteStart && (this._stashByteStart = t), this._stashUsed + e.byteLength <= this._stashSize) { - var s = new Uint8Array(this._stashBuffer, 0, this._stashSize); - s.set(new Uint8Array(e), this._stashUsed), this._stashUsed += e.byteLength - } else { - var a = new Uint8Array(this._stashBuffer, 0, this._bufferSize); - if (this._stashUsed > 0) { - var o = this._stashBuffer.slice(0, this._stashUsed), - u = this._dispatchChunks(o, this._stashByteStart); - if (u < o.byteLength) { - if (u > 0) { - var l = new Uint8Array(o, u); - a.set(l, 0), this._stashUsed = l.byteLength, this._stashByteStart += u - } - } else this._stashUsed = 0, this._stashByteStart += u; - this._stashUsed + e.byteLength > this._bufferSize && (this._expandBuffer(this._stashUsed + e.byteLength), a = new Uint8Array(this._stashBuffer, 0, this._bufferSize)), a.set(new Uint8Array(e), this._stashUsed), this._stashUsed += e.byteLength - } else { - var d = this._dispatchChunks(e, t); - if (d < e.byteLength) { - var h = e.byteLength - d; - h > this._bufferSize && (this._expandBuffer(h), a = new Uint8Array(this._stashBuffer, 0, this._bufferSize)), a.set(new Uint8Array(e, d), 0), this._stashUsed += h, this._stashByteStart = t + d - } - } - } - else if (0 === this._stashUsed) { - var f = this._dispatchChunks(e, t); - if (f < e.byteLength) { - var c = e.byteLength - f; - c > this._bufferSize && this._expandBuffer(c); - var _ = new Uint8Array(this._stashBuffer, 0, this._bufferSize); - _.set(new Uint8Array(e, f), 0), this._stashUsed += c, this._stashByteStart = t + f - } - } else { - this._stashUsed + e.byteLength > this._bufferSize && this._expandBuffer(this._stashUsed + e.byteLength); - var m = new Uint8Array(this._stashBuffer, 0, this._bufferSize); - m.set(new Uint8Array(e), this._stashUsed), this._stashUsed += e.byteLength; - var p = this._dispatchChunks(this._stashBuffer.slice(0, this._stashUsed), this._stashByteStart); - if (p < this._stashUsed && p > 0) { - var v = new Uint8Array(this._stashBuffer, p); - m.set(v, 0) - } - this._stashUsed -= p, this._stashByteStart += p - } - } - } - }, { - key: "_flushStashBuffer", - value: function(e) { - if (this._stashUsed > 0) { - var t = this._stashBuffer.slice(0, this._stashUsed), - n = this._dispatchChunks(t, this._stashByteStart), - i = t.byteLength - n; - if (n < t.byteLength) { - if (!e) { - if (n > 0) { - var r = new Uint8Array(this._stashBuffer, 0, this._bufferSize), - s = new Uint8Array(t, n); - r.set(s, 0), this._stashUsed = s.byteLength, this._stashByteStart += n - } - return 0 - } - o.default.w(this.TAG, i + " bytes unconsumed data remain when flush buffer, dropped") - } - return this._stashUsed = 0, this._stashByteStart = 0, i - } - return 0 - } - }, { - key: "_onLoaderComplete", - value: function(e, t) { - this._flushStashBuffer(!0), this._onComplete && this._onComplete(this._extraData) - } - }, { - key: "_onLoaderError", - value: function(e, t) { - switch (o.default.e(this.TAG, "Loader error, code = " + t.code + ", msg = " + t.msg), this._flushStashBuffer(!1), this._isEarlyEofReconnecting && (this._isEarlyEofReconnecting = !1, e = d.LoaderErrors.UNRECOVERABLE_EARLY_EOF), e) { - case d.LoaderErrors.EARLY_EOF: - if (!this._config.isLive && this._totalLength) { - var n = this._currentRange.to + 1; - return void(n < this._totalLength && (o.default.w(this.TAG, "Connection lost, trying reconnect..."), this._isEarlyEofReconnecting = !0, this._internalSeek(n, !1))) - } - e = d.LoaderErrors.UNRECOVERABLE_EARLY_EOF; - break; - case d.LoaderErrors.UNRECOVERABLE_EARLY_EOF: - case d.LoaderErrors.CONNECTING_TIMEOUT: - case d.LoaderErrors.HTTP_STATUS_CODE_INVALID: - case d.LoaderErrors.EXCEPTION: - } - if (!this._onError) throw new L.RuntimeException("IOException: " + t.msg); - this._onError(e, t) - } - }, { - key: "status", - get: function() { - return this._loader.status - } - }, { - key: "extraData", - get: function() { - return this._extraData - }, - set: function(e) { - this._extraData = e - } - }, { - key: "onDataArrival", - get: function() { - return this._onDataArrival - }, - set: function(e) { - this._onDataArrival = e - } - }, { - key: "onSeeked", - get: function() { - return this._onSeeked - }, - set: function(e) { - this._onSeeked = e - } - }, { - key: "onError", - get: function() { - return this._onError - }, - set: function(e) { - this._onError = e - } - }, { - key: "onComplete", - get: function() { - return this._onComplete - }, - set: function(e) { - this._onComplete = e - } - }, { - key: "onRedirect", - get: function() { - return this._onRedirect - }, - set: function(e) { - this._onRedirect = e - } - }, { - key: "onRecoveredEarlyEof", - get: function() { - return this._onRecoveredEarlyEof - }, - set: function(e) { - this._onRecoveredEarlyEof = e - } - }, { - key: "currentURL", - get: function() { - return this._dataSource.url - } - }, { - key: "hasRedirect", - get: function() { - return null != this._redirectedURL || void 0 != this._dataSource.redirectedURL - } - }, { - key: "currentRedirectedURL", - get: function() { - return this._redirectedURL || this._dataSource.redirectedURL - } - }, { - key: "currentSpeed", - get: function() { - return this._loaderClass === v.default ? this._loader.currentSpeed : this._speedSampler.lastSecondKBps - } - }, { - key: "loaderType", - get: function() { - return this._loader.type - } - }]), e - }(); - n.default = w - }, { - "../utils/exception.js": 40, - "../utils/logger.js": 41, - "./fetch-stream-loader.js": 22, - "./loader.js": 24, - "./param-seek-handler.js": 25, - "./range-seek-handler.js": 26, - "./speed-sampler.js": 27, - "./websocket-loader.js": 28, - "./xhr-moz-chunked-loader.js": 29, - "./xhr-msstream-loader.js": 30, - "./xhr-range-loader.js": 31 - }], - 24: [function(e, t, n) { - "use strict"; - - function i(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }), n.BaseLoader = n.LoaderErrors = n.LoaderStatus = void 0; - var r = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - s = e("../utils/exception.js"), - a = n.LoaderStatus = { - kIdle: 0, - kConnecting: 1, - kBuffering: 2, - kError: 3, - kComplete: 4 - }; - n.LoaderErrors = { - OK: "OK", - EXCEPTION: "Exception", - HTTP_STATUS_CODE_INVALID: "HttpStatusCodeInvalid", - CONNECTING_TIMEOUT: "ConnectingTimeout", - EARLY_EOF: "EarlyEof", - UNRECOVERABLE_EARLY_EOF: "UnrecoverableEarlyEof" - }, n.BaseLoader = function() { - function e(t) { - i(this, e), this._type = t || "undefined", this._status = a.kIdle, this._needStash = !1, this._onContentLengthKnown = null, this._onURLRedirect = null, this._onDataArrival = null, this._onError = null, this._onComplete = null - } - return r(e, [{ - key: "destroy", - value: function() { - this._status = a.kIdle, this._onContentLengthKnown = null, this._onURLRedirect = null, this._onDataArrival = null, this._onError = null, this._onComplete = null - } - }, { - key: "isWorking", - value: function() { - return this._status === a.kConnecting || this._status === a.kBuffering - } - }, { - key: "open", - value: function(e, t) { - throw new s.NotImplementedException("Unimplemented abstract function!") - } - }, { - key: "abort", - value: function() { - throw new s.NotImplementedException("Unimplemented abstract function!") - } - }, { - key: "type", - get: function() { - return this._type - } - }, { - key: "status", - get: function() { - return this._status - } - }, { - key: "needStashBuffer", - get: function() { - return this._needStash - } - }, { - key: "onContentLengthKnown", - get: function() { - return this._onContentLengthKnown - }, - set: function(e) { - this._onContentLengthKnown = e - } - }, { - key: "onURLRedirect", - get: function() { - return this._onURLRedirect - }, - set: function(e) { - this._onURLRedirect = e - } - }, { - key: "onDataArrival", - get: function() { - return this._onDataArrival - }, - set: function(e) { - this._onDataArrival = e - } - }, { - key: "onError", - get: function() { - return this._onError - }, - set: function(e) { - this._onError = e - } - }, { - key: "onComplete", - get: function() { - return this._onComplete - }, - set: function(e) { - this._onComplete = e - } - }]), e - }() - }, { - "../utils/exception.js": 40 - }], - 25: [function(e, t, n) { - "use strict"; - - function i(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var r = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - s = function() { - function e(t, n) { - i(this, e), this._startName = t, this._endName = n - } - return r(e, [{ - key: "getConfig", - value: function(e, t) { - var n = e; - if (0 !== t.from || -1 !== t.to) { - var i = !0; - 1 === n.indexOf("?") && (n += "?", i = !1), i && (n += "&"), n += this._startName + "=" + t.from.toString(), -1 !== t.to && (n += "&" + this._endName + "=" + t.to.toString()) - } - return { - url: n, - headers: {} - } - } - }, { - key: "removeURLParameters", - value: function(e) { - var t = e.split("?")[0], - n = void 0, - i = e.indexOf("?"); - 1 !== i && (n = e.substring(i + 1)); - var r = ""; - if (void 0 != n && n.length > 0) - for (var s = n.split("&"), a = 0; a < s.length; a++) { - var o = s[a].split("="), - u = a > 0; - o[0] !== this._startName && o[0] !== this._endName && (u && (r += "&"), r += s[a]) - } - return 0 === r.length ? t : t + "?" + r - } - }]), e - }(); - n.default = s - }, {}], - 26: [function(e, t, n) { - "use strict"; - - function i(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var r = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - s = function() { - function e(t) { - i(this, e), this._zeroStart = t || !1 - } - return r(e, [{ - key: "getConfig", - value: function(e, t) { - var n = {}; - if (0 !== t.from || -1 !== t.to) { - var i = void 0; - i = -1 !== t.to ? "bytes=" + t.from.toString() + "-" + t.to.toString() : "bytes=" + t.from.toString() + "-", n.Range = i - } else this._zeroStart && (n.Range = "bytes=0-"); - return { - url: e, - headers: n - } - } - }, { - key: "removeURLParameters", - value: function(e) { - return e - } - }]), e - }(); - n.default = s - }, {}], - 27: [function(e, t, n) { - "use strict"; - - function i(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var r = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - s = function() { - function e() { - i(this, e), this._firstCheckpoint = 0, this._lastCheckpoint = 0, this._intervalBytes = 0, this._totalBytes = 0, this._lastSecondBytes = 0, self.performance && self.performance.now ? this._now = self.performance.now.bind(self.performance) : this._now = Date.now - } - return r(e, [{ - key: "reset", - value: function() { - this._firstCheckpoint = this._lastCheckpoint = 0, this._totalBytes = this._intervalBytes = 0, this._lastSecondBytes = 0 - } - }, { - key: "addBytes", - value: function(e) { - 0 === this._firstCheckpoint ? (this._firstCheckpoint = this._now(), this._lastCheckpoint = this._firstCheckpoint, this._intervalBytes += e, this._totalBytes += e) : this._now() - this._lastCheckpoint < 1e3 ? (this._intervalBytes += e, this._totalBytes += e) : (this._lastSecondBytes = this._intervalBytes, this._intervalBytes = e, this._totalBytes += e, this._lastCheckpoint = this._now()) - } - }, { - key: "currentKBps", - get: function() { - this.addBytes(0); - var e = (this._now() - this._lastCheckpoint) / 1e3; - return 0 == e && (e = 1), this._intervalBytes / e / 1024 - } - }, { - key: "lastSecondKBps", - get: function() { - return this.addBytes(0), 0 !== this._lastSecondBytes ? this._lastSecondBytes / 1024 : this._now() - this._lastCheckpoint >= 500 ? this.currentKBps : 0 - } - }, { - key: "averageKBps", - get: function() { - var e = (this._now() - this._firstCheckpoint) / 1e3; - return this._totalBytes / e / 1024 - } - }]), e - }(); - n.default = s - }, {}], - 28: [function(e, t, n) { - "use strict"; - - function i(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - - function r(e, t) { - if (!e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); - return !t || "object" != typeof t && "function" != typeof t ? e : t - } - - function s(e, t) { - if ("function" != typeof t && null !== t) throw new TypeError("Super expression must either be null or a function, not " + typeof t); - e.prototype = Object.create(t && t.prototype, { - constructor: { - value: e, - enumerable: !1, - writable: !0, - configurable: !0 - } - }), t && (Object.setPrototypeOf ? Object.setPrototypeOf(e, t) : e.__proto__ = t) - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var a = function e(t, n, i) { - null === t && (t = Function.prototype); - var r = Object.getOwnPropertyDescriptor(t, n); - if (void 0 === r) { - var s = Object.getPrototypeOf(t); - return null === s ? void 0 : e(s, n, i) - } - if ("value" in r) return r.value; - var a = r.get; - if (void 0 !== a) return a.call(i) - }, - o = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - u = e("../utils/logger.js"), - l = (function(e) { - e && e.__esModule - }(u), e("./loader.js")), - d = e("../utils/exception.js"), - h = function(e) { - function t() { - i(this, t); - var e = r(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, "websocket-loader")); - return e.TAG = "WebSocketLoader", e._needStash = !0, e._ws = null, e._requestAbort = !1, e._receivedLength = 0, e - } - return s(t, e), o(t, null, [{ - key: "isSupported", - value: function() { - try { - return void 0 !== self.WebSocket - } catch (e) { - return !1 - } - } - }]), o(t, [{ - key: "destroy", - value: function() { - this._ws && this.abort(), a(t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), "destroy", this).call(this) - } - }, { - key: "open", - value: function(e) { - try { - var t = this._ws = new self.WebSocket(e.url); - t.binaryType = "arraybuffer", t.onopen = this._onWebSocketOpen.bind(this), t.onclose = this._onWebSocketClose.bind(this), t.onmessage = this._onWebSocketMessage.bind(this), t.onerror = this._onWebSocketError.bind(this), this._status = l.LoaderStatus.kConnecting - } catch (e) { - this._status = l.LoaderStatus.kError; - var n = { - code: e.code, - msg: e.message - }; - if (!this._onError) throw new d.RuntimeException(n.msg); - this._onError(l.LoaderErrors.EXCEPTION, n) - } - } - }, { - key: "abort", - value: function() { - var e = this._ws; - !e || 0 !== e.readyState && 1 !== e.readyState || (this._requestAbort = !0, e.close()), this._ws = null, this._status = l.LoaderStatus.kComplete - } - }, { - key: "_onWebSocketOpen", - value: function(e) { - this._status = l.LoaderStatus.kBuffering - } - }, { - key: "_onWebSocketClose", - value: function(e) { - if (!0 === this._requestAbort) return void(this._requestAbort = !1); - this._status = l.LoaderStatus.kComplete, this._onComplete && this._onComplete(0, this._receivedLength - 1) - } - }, { - key: "_onWebSocketMessage", - value: function(e) { - var t = this; - if (e.data instanceof ArrayBuffer) this._dispatchArrayBuffer(e.data); - else if (e.data instanceof Blob) { - var n = new FileReader; - n.onload = function() { - t._dispatchArrayBuffer(n.result) - }, n.readAsArrayBuffer(e.data) - } else { - this._status = l.LoaderStatus.kError; - var i = { - code: -1, - msg: "Unsupported WebSocket message type: " + e.data.constructor.name - }; - if (!this._onError) throw new d.RuntimeException(i.msg); - this._onError(l.LoaderErrors.EXCEPTION, i) - } - } - }, { - key: "_dispatchArrayBuffer", - value: function(e) { - var t = e, - n = this._receivedLength; - this._receivedLength += t.byteLength, this._onDataArrival && this._onDataArrival(t, n, this._receivedLength) - } - }, { - key: "_onWebSocketError", - value: function(e) { - this._status = l.LoaderStatus.kError; - var t = { - code: e.code, - msg: e.message - }; - if (!this._onError) throw new d.RuntimeException(t.msg); - this._onError(l.LoaderErrors.EXCEPTION, t) - } - }]), t - }(l.BaseLoader); - n.default = h - }, { - "../utils/exception.js": 40, - "../utils/logger.js": 41, - "./loader.js": 24 - }], - 29: [function(e, t, n) { - "use strict"; - - function i(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - - function r(e, t) { - if (!e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); - return !t || "object" != typeof t && "function" != typeof t ? e : t - } - - function s(e, t) { - if ("function" != typeof t && null !== t) throw new TypeError("Super expression must either be null or a function, not " + typeof t); - e.prototype = Object.create(t && t.prototype, { - constructor: { - value: e, - enumerable: !1, - writable: !0, - configurable: !0 - } - }), t && (Object.setPrototypeOf ? Object.setPrototypeOf(e, t) : e.__proto__ = t) - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var a = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(e) { - return typeof e - } : function(e) { - return e && "function" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : typeof e - }, - o = function e(t, n, i) { - null === t && (t = Function.prototype); - var r = Object.getOwnPropertyDescriptor(t, n); - if (void 0 === r) { - var s = Object.getPrototypeOf(t); - return null === s ? void 0 : e(s, n, i) - } - if ("value" in r) return r.value; - var a = r.get; - if (void 0 !== a) return a.call(i) - }, - u = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - l = e("../utils/logger.js"), - d = function(e) { - return e && e.__esModule ? e : { - default: e - } - }(l), - h = e("./loader.js"), - f = e("../utils/exception.js"), - c = function(e) { - function t(e, n) { - i(this, t); - var s = r(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, "xhr-moz-chunked-loader")); - return s.TAG = "MozChunkedLoader", s._seekHandler = e, s._config = n, s._needStash = !0, s._xhr = null, s._requestAbort = !1, s._contentLength = null, s._receivedLength = 0, s - } - return s(t, e), u(t, null, [{ - key: "isSupported", - value: function() { - try { - var e = new XMLHttpRequest; - return e.open("GET", "https://example.com", !0), e.responseType = "moz-chunked-arraybuffer", "moz-chunked-arraybuffer" === e.responseType - } catch (e) { - return d.default.w("MozChunkedLoader", e.message), !1 - } - } - }]), u(t, [{ - key: "destroy", - value: function() { - this.isWorking() && this.abort(), this._xhr && (this._xhr.onreadystatechange = null, this._xhr.onprogress = null, this._xhr.onloadend = null, this._xhr.onerror = null, this._xhr = null), o(t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), "destroy", this).call(this) - } - }, { - key: "open", - value: function(e, t) { - this._dataSource = e, this._range = t; - var n = e.url; - this._config.reuseRedirectedURL && void 0 != e.redirectedURL && (n = e.redirectedURL); - var i = this._seekHandler.getConfig(n, t); - this._requestURL = i.url; - var r = this._xhr = new XMLHttpRequest; - if (r.open("GET", i.url, !0), r.responseType = "moz-chunked-arraybuffer", r.onreadystatechange = this._onReadyStateChange.bind(this), r.onprogress = this._onProgress.bind(this), r.onloadend = this._onLoadEnd.bind(this), r.onerror = this._onXhrError.bind(this), e.withCredentials && (r.withCredentials = !0), "object" === a(i.headers)) { - var s = i.headers; - for (var o in s) s.hasOwnProperty(o) && r.setRequestHeader(o, s[o]) - } - this._status = h.LoaderStatus.kConnecting, r.send() - } - }, { - key: "abort", - value: function() { - this._requestAbort = !0, this._xhr && this._xhr.abort(), this._status = h.LoaderStatus.kComplete - } - }, { - key: "_onReadyStateChange", - value: function(e) { - var t = e.target; - if (2 === t.readyState) { - if (void 0 != t.responseURL && t.responseURL !== this._requestURL && this._onURLRedirect) { - var n = this._seekHandler.removeURLParameters(t.responseURL); - this._onURLRedirect(n) - } - if (0 !== t.status && (t.status < 200 || t.status > 299)) { - if (this._status = h.LoaderStatus.kError, !this._onError) throw new f.RuntimeException("MozChunkedLoader: Http code invalid, " + t.status + " " + t.statusText); - this._onError(h.LoaderErrors.HTTP_STATUS_CODE_INVALID, { - code: t.status, - msg: t.statusText - }) - } else this._status = h.LoaderStatus.kBuffering - } - } - }, { - key: "_onProgress", - value: function(e) { - if (this._status !== h.LoaderStatus.kError) { - null === this._contentLength && null !== e.total && 0 !== e.total && (this._contentLength = e.total, this._onContentLengthKnown && this._onContentLengthKnown(this._contentLength)); - var t = e.target.response, - n = this._range.from + this._receivedLength; - this._receivedLength += t.byteLength, this._onDataArrival && this._onDataArrival(t, n, this._receivedLength) - } - } - }, { - key: "_onLoadEnd", - value: function(e) { - if (!0 === this._requestAbort) return void(this._requestAbort = !1); - this._status !== h.LoaderStatus.kError && (this._status = h.LoaderStatus.kComplete, this._onComplete && this._onComplete(this._range.from, this._range.from + this._receivedLength - 1)) - } - }, { - key: "_onXhrError", - value: function(e) { - this._status = h.LoaderStatus.kError; - var t = 0, - n = null; - if (this._contentLength && e.loaded < this._contentLength ? (t = h.LoaderErrors.EARLY_EOF, n = { - code: -1, - msg: "Moz-Chunked stream meet Early-Eof" - }) : (t = h.LoaderErrors.EXCEPTION, n = { - code: -1, - msg: e.constructor.name + " " + e.type - }), !this._onError) throw new f.RuntimeException(n.msg); - this._onError(t, n) - } - }]), t - }(h.BaseLoader); - n.default = c - }, { - "../utils/exception.js": 40, - "../utils/logger.js": 41, - "./loader.js": 24 - }], - 30: [function(e, t, n) { - "use strict"; - - function i(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - - function r(e, t) { - if (!e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); - return !t || "object" != typeof t && "function" != typeof t ? e : t - } - - function s(e, t) { - if ("function" != typeof t && null !== t) throw new TypeError("Super expression must either be null or a function, not " + typeof t); - e.prototype = Object.create(t && t.prototype, { - constructor: { - value: e, - enumerable: !1, - writable: !0, - configurable: !0 - } - }), t && (Object.setPrototypeOf ? Object.setPrototypeOf(e, t) : e.__proto__ = t) - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var a = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(e) { - return typeof e - } : function(e) { - return e && "function" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : typeof e - }, - o = function e(t, n, i) { - null === t && (t = Function.prototype); - var r = Object.getOwnPropertyDescriptor(t, n); - if (void 0 === r) { - var s = Object.getPrototypeOf(t); - return null === s ? void 0 : e(s, n, i) - } - if ("value" in r) return r.value; - var a = r.get; - if (void 0 !== a) return a.call(i) - }, - u = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - l = e("../utils/logger.js"), - d = function(e) { - return e && e.__esModule ? e : { - default: e - } - }(l), - h = e("./loader.js"), - f = e("../utils/exception.js"), - c = function(e) { - function t(e, n) { - i(this, t); - var s = r(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, "xhr-msstream-loader")); - return s.TAG = "MSStreamLoader", s._seekHandler = e, s._config = n, s._needStash = !0, s._xhr = null, s._reader = null, s._totalRange = null, s._currentRange = null, s._currentRequestURL = null, s._currentRedirectedURL = null, s._contentLength = null, s._receivedLength = 0, s._bufferLimit = 16777216, s._lastTimeBufferSize = 0, s._isReconnecting = !1, s - } - return s(t, e), u(t, null, [{ - key: "isSupported", - value: function() { - try { - if (void 0 === self.MSStream || void 0 === self.MSStreamReader) return !1; - var e = new XMLHttpRequest; - return e.open("GET", "https://example.com", !0), e.responseType = "ms-stream", "ms-stream" === e.responseType - } catch (e) { - return d.default.w("MSStreamLoader", e.message), !1 - } - } - }]), u(t, [{ - key: "destroy", - value: function() { - this.isWorking() && this.abort(), this._reader && (this._reader.onprogress = null, this._reader.onload = null, this._reader.onerror = null, this._reader = null), this._xhr && (this._xhr.onreadystatechange = null, this._xhr = null), o(t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), "destroy", this).call(this) - } - }, { - key: "open", - value: function(e, t) { - this._internalOpen(e, t, !1) - } - }, { - key: "_internalOpen", - value: function(e, t, n) { - this._dataSource = e, n ? this._currentRange = t : this._totalRange = t; - var i = e.url; - this._config.reuseRedirectedURL && (void 0 != this._currentRedirectedURL ? i = this._currentRedirectedURL : void 0 != e.redirectedURL && (i = e.redirectedURL)); - var r = this._seekHandler.getConfig(i, t); - this._currentRequestURL = r.url; - var s = this._reader = new self.MSStreamReader; - s.onprogress = this._msrOnProgress.bind(this), s.onload = this._msrOnLoad.bind(this), s.onerror = this._msrOnError.bind(this); - var o = this._xhr = new XMLHttpRequest; - if (o.open("GET", r.url, !0), o.responseType = "ms-stream", o.onreadystatechange = this._xhrOnReadyStateChange.bind(this), o.onerror = this._xhrOnError.bind(this), e.withCredentials && (o.withCredentials = !0), "object" === a(r.headers)) { - var u = r.headers; - for (var l in u) u.hasOwnProperty(l) && o.setRequestHeader(l, u[l]) - } - this._isReconnecting ? this._isReconnecting = !1 : this._status = h.LoaderStatus.kConnecting, o.send() - } - }, { - key: "abort", - value: function() { - this._internalAbort(), this._status = h.LoaderStatus.kComplete - } - }, { - key: "_internalAbort", - value: function() { - this._reader && (1 === this._reader.readyState && this._reader.abort(), this._reader.onprogress = null, this._reader.onload = null, this._reader.onerror = null, this._reader = null), this._xhr && (this._xhr.abort(), this._xhr.onreadystatechange = null, this._xhr = null) - } - }, { - key: "_xhrOnReadyStateChange", - value: function(e) { - var t = e.target; - if (2 === t.readyState) - if (t.status >= 200 && t.status <= 299) { - if (this._status = h.LoaderStatus.kBuffering, void 0 != t.responseURL) { - var n = this._seekHandler.removeURLParameters(t.responseURL); - t.responseURL !== this._currentRequestURL && n !== this._currentRedirectedURL && (this._currentRedirectedURL = n, this._onURLRedirect && this._onURLRedirect(n)) - } - var i = t.getResponseHeader("Content-Length"); - if (null != i && null == this._contentLength) { - var r = parseInt(i); - r > 0 && (this._contentLength = r, this._onContentLengthKnown && this._onContentLengthKnown(this._contentLength)) - } - } else { - if (this._status = h.LoaderStatus.kError, !this._onError) throw new f.RuntimeException("MSStreamLoader: Http code invalid, " + t.status + " " + t.statusText); - this._onError(h.LoaderErrors.HTTP_STATUS_CODE_INVALID, { - code: t.status, - msg: t.statusText - }) - } - else if (3 === t.readyState && t.status >= 200 && t.status <= 299) { - this._status = h.LoaderStatus.kBuffering; - var s = t.response; - this._reader.readAsArrayBuffer(s) - } - } - }, { - key: "_xhrOnError", - value: function(e) { - this._status = h.LoaderStatus.kError; - var t = h.LoaderErrors.EXCEPTION, - n = { - code: -1, - msg: e.constructor.name + " " + e.type - }; - if (!this._onError) throw new f.RuntimeException(n.msg); - this._onError(t, n) - } - }, { - key: "_msrOnProgress", - value: function(e) { - var t = e.target, - n = t.result; - if (null == n) return void this._doReconnectIfNeeded(); - var i = n.slice(this._lastTimeBufferSize); - this._lastTimeBufferSize = n.byteLength; - var r = this._totalRange.from + this._receivedLength; - this._receivedLength += i.byteLength, this._onDataArrival && this._onDataArrival(i, r, this._receivedLength), n.byteLength >= this._bufferLimit && (d.default.v(this.TAG, "MSStream buffer exceeded max size near " + (r + i.byteLength) + ", reconnecting..."), this._doReconnectIfNeeded()) - } - }, { - key: "_doReconnectIfNeeded", - value: function() { - if (null == this._contentLength || this._receivedLength < this._contentLength) { - this._isReconnecting = !0, this._lastTimeBufferSize = 0, this._internalAbort(); - var e = { - from: this._totalRange.from + this._receivedLength, - to: -1 - }; - this._internalOpen(this._dataSource, e, !0) - } - } - }, { - key: "_msrOnLoad", - value: function(e) { - this._status = h.LoaderStatus.kComplete, this._onComplete && this._onComplete(this._totalRange.from, this._totalRange.from + this._receivedLength - 1) - } - }, { - key: "_msrOnError", - value: function(e) { - this._status = h.LoaderStatus.kError; - var t = 0, - n = null; - if (this._contentLength && this._receivedLength < this._contentLength ? (t = h.LoaderErrors.EARLY_EOF, n = { - code: -1, - msg: "MSStream meet Early-Eof" - }) : (t = h.LoaderErrors.EARLY_EOF, n = { - code: -1, - msg: e.constructor.name + " " + e.type - }), !this._onError) throw new f.RuntimeException(n.msg); - this._onError(t, n) - } - }]), t - }(h.BaseLoader); - n.default = c - }, { - "../utils/exception.js": 40, - "../utils/logger.js": 41, - "./loader.js": 24 - }], - 31: [function(e, t, n) { - "use strict"; - - function i(e) { - return e && e.__esModule ? e : { - default: e - } - } - - function r(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - - function s(e, t) { - if (!e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); - return !t || "object" != typeof t && "function" != typeof t ? e : t - } - - function a(e, t) { - if ("function" != typeof t && null !== t) throw new TypeError("Super expression must either be null or a function, not " + typeof t); - e.prototype = Object.create(t && t.prototype, { - constructor: { - value: e, - enumerable: !1, - writable: !0, - configurable: !0 - } - }), t && (Object.setPrototypeOf ? Object.setPrototypeOf(e, t) : e.__proto__ = t) - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var o = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(e) { - return typeof e - } : function(e) { - return e && "function" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : typeof e - }, - u = function e(t, n, i) { - null === t && (t = Function.prototype); - var r = Object.getOwnPropertyDescriptor(t, n); - if (void 0 === r) { - var s = Object.getPrototypeOf(t); - return null === s ? void 0 : e(s, n, i) - } - if ("value" in r) return r.value; - var a = r.get; - if (void 0 !== a) return a.call(i) - }, - l = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - d = e("../utils/logger.js"), - h = i(d), - f = e("./speed-sampler.js"), - c = i(f), - _ = e("./loader.js"), - m = e("../utils/exception.js"), - p = function(e) { - function t(e, n) { - r(this, t); - var i = s(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, "xhr-range-loader")); - return i.TAG = "RangeLoader", i._seekHandler = e, i._config = n, i._needStash = !1, i._chunkSizeKBList = [128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 5120, 6144, 7168, 8192], i._currentChunkSizeKB = 384, i._currentSpeedNormalized = 0, i._zeroSpeedChunkCount = 0, i._xhr = null, i._speedSampler = new c.default, i._requestAbort = !1, i._waitForTotalLength = !1, i._totalLengthReceived = !1, i._currentRequestURL = null, i._currentRedirectedURL = null, i._currentRequestRange = null, i._totalLength = null, i._contentLength = null, i._receivedLength = 0, i._lastTimeLoaded = 0, i - } - return a(t, e), l(t, null, [{ - key: "isSupported", - value: function() { - try { - var e = new XMLHttpRequest; - return e.open("GET", "https://example.com", !0), e.responseType = "arraybuffer", "arraybuffer" === e.responseType - } catch (e) { - return h.default.w("RangeLoader", e.message), !1 - } - } - }]), l(t, [{ - key: "destroy", - value: function() { - this.isWorking() && this.abort(), this._xhr && (this._xhr.onreadystatechange = null, this._xhr.onprogress = null, this._xhr.onload = null, this._xhr.onerror = null, this._xhr = null), u(t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), "destroy", this).call(this) - } - }, { - key: "open", - value: function(e, t) { - this._dataSource = e, this._range = t, this._status = _.LoaderStatus.kConnecting; - var n = !1; - void 0 != this._dataSource.filesize && 0 !== this._dataSource.filesize && (n = !0, this._totalLength = this._dataSource.filesize), this._totalLengthReceived || n ? this._openSubRange() : (this._waitForTotalLength = !0, this._internalOpen(this._dataSource, { - from: 0, - to: -1 - })) - } - }, { - key: "_openSubRange", - value: function() { - var e = 1024 * this._currentChunkSizeKB, - t = this._range.from + this._receivedLength, - n = t + e; - null != this._contentLength && n - this._range.from >= this._contentLength && (n = this._range.from + this._contentLength - 1), this._currentRequestRange = { - from: t, - to: n - }, this._internalOpen(this._dataSource, this._currentRequestRange) - } - }, { - key: "_internalOpen", - value: function(e, t) { - this._lastTimeLoaded = 0; - var n = e.url; - this._config.reuseRedirectedURL && (void 0 != this._currentRedirectedURL ? n = this._currentRedirectedURL : void 0 != e.redirectedURL && (n = e.redirectedURL)); - var i = this._seekHandler.getConfig(n, t); - this._currentRequestURL = i.url; - var r = this._xhr = new XMLHttpRequest; - if (r.open("GET", i.url, !0), r.responseType = "arraybuffer", r.onreadystatechange = this._onReadyStateChange.bind(this), r.onprogress = this._onProgress.bind(this), r.onload = this._onLoad.bind(this), r.onerror = this._onXhrError.bind(this), e.withCredentials && (r.withCredentials = !0), "object" === o(i.headers)) { - var s = i.headers; - for (var a in s) s.hasOwnProperty(a) && r.setRequestHeader(a, s[a]) - } - r.send() - } - }, { - key: "abort", - value: function() { - this._requestAbort = !0, this._internalAbort(), this._status = _.LoaderStatus.kComplete - } - }, { - key: "_internalAbort", - value: function() { - this._xhr && (this._xhr.onreadystatechange = null, this._xhr.onprogress = null, this._xhr.onload = null, this._xhr.onerror = null, this._xhr.abort(), this._xhr = null) - } - }, { - key: "_onReadyStateChange", - value: function(e) { - var t = e.target; - if (2 === t.readyState) { - if (void 0 != t.responseURL) { - var n = this._seekHandler.removeURLParameters(t.responseURL); - t.responseURL !== this._currentRequestURL && n !== this._currentRedirectedURL && (this._currentRedirectedURL = n, this._onURLRedirect && this._onURLRedirect(n)) - } - if (t.status >= 200 && t.status <= 299) { - if (this._waitForTotalLength) return; - this._status = _.LoaderStatus.kBuffering - } else { - if (this._status = _.LoaderStatus.kError, !this._onError) throw new m.RuntimeException("RangeLoader: Http code invalid, " + t.status + " " + t.statusText); - this._onError(_.LoaderErrors.HTTP_STATUS_CODE_INVALID, { - code: t.status, - msg: t.statusText - }) - } - } - } - }, { - key: "_onProgress", - value: function(e) { - if (this._status !== _.LoaderStatus.kError) { - if (null === this._contentLength) { - var t = !1; - if (this._waitForTotalLength) { - this._waitForTotalLength = !1, this._totalLengthReceived = !0, t = !0; - var n = e.total; - this._internalAbort(), null != n & 0 !== n && (this._totalLength = n) - } - if (-1 === this._range.to ? this._contentLength = this._totalLength - this._range.from : this._contentLength = this._range.to - this._range.from + 1, t) return void this._openSubRange(); - this._onContentLengthKnown && this._onContentLengthKnown(this._contentLength) - } - var i = e.loaded - this._lastTimeLoaded; - this._lastTimeLoaded = e.loaded, this._speedSampler.addBytes(i) - } - } - }, { - key: "_normalizeSpeed", - value: function(e) { - var t = this._chunkSizeKBList, - n = t.length - 1, - i = 0, - r = 0, - s = n; - if (e < t[0]) return t[0]; - for (; r <= s;) { - if ((i = r + Math.floor((s - r) / 2)) === n || e >= t[i] && e < t[i + 1]) return t[i]; - t[i] < e ? r = i + 1 : s = i - 1 - } - } - }, { - key: "_onLoad", - value: function(e) { - if (this._status !== _.LoaderStatus.kError) { - if (this._waitForTotalLength) return void(this._waitForTotalLength = !1); - this._lastTimeLoaded = 0; - var t = this._speedSampler.lastSecondKBps; - if (0 === t && ++this._zeroSpeedChunkCount >= 3 && (t = this._speedSampler.currentKBps), 0 !== t) { - var n = this._normalizeSpeed(t); - this._currentSpeedNormalized !== n && (this._currentSpeedNormalized = n, this._currentChunkSizeKB = n) - } - var i = e.target.response, - r = this._range.from + this._receivedLength; - this._receivedLength += i.byteLength; - var s = !1; - null != this._contentLength && this._receivedLength < this._contentLength ? this._openSubRange() : s = !0, this._onDataArrival && this._onDataArrival(i, r, this._receivedLength), s && (this._status = _.LoaderStatus.kComplete, this._onComplete && this._onComplete(this._range.from, this._range.from + this._receivedLength - 1)) - } - } - }, { - key: "_onXhrError", - value: function(e) { - this._status = _.LoaderStatus.kError; - var t = 0, - n = null; - if (this._contentLength && this._receivedLength > 0 && this._receivedLength < this._contentLength ? (t = _.LoaderErrors.EARLY_EOF, n = { - code: -1, - msg: "RangeLoader meet Early-Eof" - }) : (t = _.LoaderErrors.EXCEPTION, n = { - code: -1, - msg: e.constructor.name + " " + e.type - }), !this._onError) throw new m.RuntimeException(n.msg); - this._onError(t, n) - } - }, { - key: "currentSpeed", - get: function() { - return this._speedSampler.lastSecondKBps - } - }]), t - }(_.BaseLoader); - n.default = p - }, { - "../utils/exception.js": 40, - "../utils/logger.js": 41, - "./loader.js": 24, - "./speed-sampler.js": 27 - }], - 32: [function(e, t, n) { - "use strict"; - - function i(e) { - return e && e.__esModule ? e : { - default: e - } - } - - function r(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var s = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(e) { - return typeof e - } : function(e) { - return e && "function" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : typeof e - }, - a = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - o = e("events"), - u = i(o), - l = e("../utils/logger.js"), - d = i(l), - h = e("../utils/browser.js"), - f = i(h), - c = e("./player-events.js"), - _ = i(c), - m = e("../core/transmuxer.js"), - p = i(m), - v = e("../core/transmuxing-events.js"), - g = i(v), - y = e("../core/mse-controller.js"), - E = i(y), - b = e("../core/mse-events.js"), - S = i(b), - k = e("./player-errors.js"), - L = e("../config.js"), - w = e("../utils/exception.js"), - R = function() { - function e(t, n) { - if (r(this, e), this.TAG = "FlvPlayer", this._type = "FlvPlayer", this._emitter = new u.default, this._config = (0, L.createDefaultConfig)(), "object" === (void 0 === n ? "undefined" : s(n)) && Object.assign(this._config, n), "flv" !== t.type.toLowerCase()) throw new w.InvalidArgumentException("FlvPlayer requires an flv MediaDataSource input!"); - !0 === t.isLive && (this._config.isLive = !0), this.e = { - onvLoadedMetadata: this._onvLoadedMetadata.bind(this), - onvSeeking: this._onvSeeking.bind(this), - onvCanPlay: this._onvCanPlay.bind(this), - onvStalled: this._onvStalled.bind(this), - onvProgress: this._onvProgress.bind(this) - }, self.performance && self.performance.now ? this._now = self.performance.now.bind(self.performance) : this._now = Date.now, this._pendingSeekTime = null, this._requestSetTime = !1, this._seekpointRecord = null, this._progressChecker = null, this._mediaDataSource = t, this._mediaElement = null, this._msectl = null, this._transmuxer = null, this._mseSourceOpened = !1, this._hasPendingLoad = !1, this._receivedCanPlay = !1, this._mediaInfo = null, this._statisticsInfo = null; - var i = f.default.chrome && (f.default.version.major < 50 || 50 === f.default.version.major && f.default.version.build < 2661); - this._alwaysSeekKeyframe = !!(i || f.default.msedge || f.default.msie), this._alwaysSeekKeyframe && (this._config.accurateSeek = !1) - } - return a(e, [{ - key: "destroy", - value: function() { - null != this._progressChecker && (window.clearInterval(this._progressChecker), this._progressChecker = null), this._transmuxer && this.unload(), this._mediaElement && this.detachMediaElement(), this.e = null, this._mediaDataSource = null, this._emitter.removeAllListeners(), this._emitter = null - } - }, { - key: "on", - value: function(e, t) { - var n = this; - e === _.default.MEDIA_INFO ? null != this._mediaInfo && Promise.resolve().then(function() { - n._emitter.emit(_.default.MEDIA_INFO, n.mediaInfo) - }) : e === _.default.STATISTICS_INFO && null != this._statisticsInfo && Promise.resolve().then(function() { - n._emitter.emit(_.default.STATISTICS_INFO, n.statisticsInfo) - }), this._emitter.addListener(e, t) - } - }, { - key: "off", - value: function(e, t) { - this._emitter.removeListener(e, t) - } - }, { - key: "attachMediaElement", - value: function(e) { - var t = this; - if (this._mediaElement = e, e.addEventListener("loadedmetadata", this.e.onvLoadedMetadata), e.addEventListener("seeking", this.e.onvSeeking), e.addEventListener("canplay", this.e.onvCanPlay), e.addEventListener("stalled", this.e.onvStalled), e.addEventListener("progress", this.e.onvProgress), this._msectl = new E.default(this._config), this._msectl.on(S.default.UPDATE_END, this._onmseUpdateEnd.bind(this)), this._msectl.on(S.default.BUFFER_FULL, this._onmseBufferFull.bind(this)), this._msectl.on(S.default.SOURCE_OPEN, function() { - t._mseSourceOpened = !0, t._hasPendingLoad && (t._hasPendingLoad = !1, t.load()) - }), this._msectl.on(S.default.ERROR, function(e) { - t._emitter.emit(_.default.ERROR, k.ErrorTypes.MEDIA_ERROR, k.ErrorDetails.MEDIA_MSE_ERROR, e) - }), this._msectl.attachMediaElement(e), null != this._pendingSeekTime) try { - e.currentTime = this._pendingSeekTime, this._pendingSeekTime = null - } catch (e) {} - } - }, { - key: "detachMediaElement", - value: function() { - this._mediaElement && (this._msectl.detachMediaElement(), this._mediaElement.removeEventListener("loadedmetadata", this.e.onvLoadedMetadata), this._mediaElement.removeEventListener("seeking", this.e.onvSeeking), this._mediaElement.removeEventListener("canplay", this.e.onvCanPlay), this._mediaElement.removeEventListener("stalled", this.e.onvStalled), this._mediaElement.removeEventListener("progress", this.e.onvProgress), this._mediaElement = null), this._msectl && (this._msectl.destroy(), this._msectl = null) - } - }, { - key: "load", - value: function() { - var e = this; - if (!this._mediaElement) throw new w.IllegalStateException("HTMLMediaElement must be attached before load()!"); - if (this._transmuxer) throw new w.IllegalStateException("FlvPlayer.load() has been called, please call unload() first!"); - if (!this._hasPendingLoad) { - if (this._config.deferLoadAfterSourceOpen && !1 === this._mseSourceOpened) return void(this._hasPendingLoad = !0); - this._mediaElement.readyState > 0 && (this._requestSetTime = !0, this._mediaElement.currentTime = 0), this._transmuxer = new p.default(this._mediaDataSource, this._config), this._transmuxer.on(g.default.INIT_SEGMENT, function(t, n) { - e._msectl.appendInitSegment(n) - }), this._transmuxer.on(g.default.MEDIA_SEGMENT, function(t, n) { - if (e._msectl.appendMediaSegment(n), e._config.lazyLoad && !e._config.isLive) { - var i = e._mediaElement.currentTime; - n.info.endDts >= 1e3 * (i + e._config.lazyLoadMaxDuration) && null == e._progressChecker && (d.default.v(e.TAG, "Maximum buffering duration exceeded, suspend transmuxing task"), e._suspendTransmuxer()) - } - }), this._transmuxer.on(g.default.LOADING_COMPLETE, function() { - e._msectl.endOfStream(), e._emitter.emit(_.default.LOADING_COMPLETE) - }), - this._transmuxer.on(g.default.RECOVERED_EARLY_EOF, function() { - e._emitter.emit(_.default.RECOVERED_EARLY_EOF) - }), this._transmuxer.on(g.default.IO_ERROR, function(t, n) { - e._emitter.emit(_.default.ERROR, k.ErrorTypes.NETWORK_ERROR, t, n) - }), this._transmuxer.on(g.default.DEMUX_ERROR, function(t, n) { - e._emitter.emit(_.default.ERROR, k.ErrorTypes.MEDIA_ERROR, t, { - code: -1, - msg: n - }) - }), this._transmuxer.on(g.default.MEDIA_INFO, function(t) { - e._mediaInfo = t, e._emitter.emit(_.default.MEDIA_INFO, Object.assign({}, t)) - }), this._transmuxer.on(g.default.STATISTICS_INFO, function(t) { - e._statisticsInfo = e._fillStatisticsInfo(t), e._emitter.emit(_.default.STATISTICS_INFO, Object.assign({}, e._statisticsInfo)) - }), this._transmuxer.on(g.default.RECOMMEND_SEEKPOINT, function(t) { - e._mediaElement && !e._config.accurateSeek && (e._requestSetTime = !0, e._mediaElement.currentTime = t / 1e3) - }), this._transmuxer.open() - } - } - }, { - key: "unload", - value: function() { - this._mediaElement && this._mediaElement.pause(), this._msectl && this._msectl.seek(0), this._transmuxer && (this._transmuxer.close(), this._transmuxer.destroy(), this._transmuxer = null) - } - }, { - key: "play", - value: function() { - return this._mediaElement.play() - } - }, { - key: "pause", - value: function() { - this._mediaElement.pause() - } - }, { - key: "_fillStatisticsInfo", - value: function(e) { - if (e.playerType = this._type, !(this._mediaElement instanceof HTMLVideoElement)) return e; - var t = !0, - n = 0, - i = 0; - if (this._mediaElement.getVideoPlaybackQuality) { - var r = this._mediaElement.getVideoPlaybackQuality(); - n = r.totalVideoFrames, i = r.droppedVideoFrames - } else void 0 != this._mediaElement.webkitDecodedFrameCount ? (n = this._mediaElement.webkitDecodedFrameCount, i = this._mediaElement.webkitDroppedFrameCount) : t = !1; - return t && (e.decodedFrames = n, e.droppedFrames = i), e - } - }, { - key: "_onmseUpdateEnd", - value: function() { - if (this._config.lazyLoad && !this._config.isLive) { - for (var e = this._mediaElement.buffered, t = this._mediaElement.currentTime, n = 0, i = 0; i < e.length; i++) { - var r = e.start(i), - s = e.end(i); - if (r <= t && t < s) { - r, - n = s; - break - } - } - n >= t + this._config.lazyLoadMaxDuration && null == this._progressChecker && (d.default.v(this.TAG, "Maximum buffering duration exceeded, suspend transmuxing task"), this._suspendTransmuxer()) - } - } - }, { - key: "_onmseBufferFull", - value: function() { - d.default.v(this.TAG, "MSE SourceBuffer is full, suspend transmuxing task"), null == this._progressChecker && this._suspendTransmuxer() - } - }, { - key: "_suspendTransmuxer", - value: function() { - this._transmuxer && (this._transmuxer.pause(), null == this._progressChecker && (this._progressChecker = window.setInterval(this._checkProgressAndResume.bind(this), 1e3))) - } - }, { - key: "_checkProgressAndResume", - value: function() { - for (var e = this._mediaElement.currentTime, t = this._mediaElement.buffered, n = !1, i = 0; i < t.length; i++) { - var r = t.start(i), - s = t.end(i); - if (e >= r && e < s) { - e >= s - this._config.lazyLoadRecoverDuration && (n = !0); - break - } - } - n && (window.clearInterval(this._progressChecker), this._progressChecker = null, n && (d.default.v(this.TAG, "Continue loading from paused position"), this._transmuxer.resume())) - } - }, { - key: "_isTimepointBuffered", - value: function(e) { - for (var t = this._mediaElement.buffered, n = 0; n < t.length; n++) { - var i = t.start(n), - r = t.end(n); - if (e >= i && e < r) return !0 - } - return !1 - } - }, { - key: "_internalSeek", - value: function(e) { - var t = this._isTimepointBuffered(e), - n = !1, - i = 0; - if (e < 1 && this._mediaElement.buffered.length > 0) { - var r = this._mediaElement.buffered.start(0); - (r < 1 && e < r || f.default.safari) && (n = !0, i = f.default.safari ? .1 : r) - } - if (n) this._requestSetTime = !0, this._mediaElement.currentTime = i; - else if (t) { - if (this._alwaysSeekKeyframe) { - var s = this._msectl.getNearestKeyframe(Math.floor(1e3 * e)); - this._requestSetTime = !0, this._mediaElement.currentTime = null != s ? s.dts / 1e3 : e - } else this._requestSetTime = !0, this._mediaElement.currentTime = e; - null != this._progressChecker && this._checkProgressAndResume() - } else null != this._progressChecker && (window.clearInterval(this._progressChecker), this._progressChecker = null), this._msectl.seek(e), this._transmuxer.seek(Math.floor(1e3 * e)), this._config.accurateSeek && (this._requestSetTime = !0, this._mediaElement.currentTime = e) - } - }, { - key: "_checkAndApplyUnbufferedSeekpoint", - value: function() { - if (this._seekpointRecord) - if (this._seekpointRecord.recordTime <= this._now() - 100) { - var e = this._mediaElement.currentTime; - this._seekpointRecord = null, this._isTimepointBuffered(e) || (null != this._progressChecker && (window.clearTimeout(this._progressChecker), this._progressChecker = null), this._msectl.seek(e), this._transmuxer.seek(Math.floor(1e3 * e)), this._config.accurateSeek && (this._requestSetTime = !0, this._mediaElement.currentTime = e)) - } else window.setTimeout(this._checkAndApplyUnbufferedSeekpoint.bind(this), 50) - } - }, { - key: "_checkAndResumeStuckPlayback", - value: function(e) { - var t = this._mediaElement; - if (e || !this._receivedCanPlay || t.readyState < 2) { - var n = t.buffered; - n.length > 0 && t.currentTime < n.start(0) && (d.default.w(this.TAG, "Playback seems stuck at " + t.currentTime + ", seek to " + n.start(0)), this._requestSetTime = !0, this._mediaElement.currentTime = n.start(0), this._mediaElement.removeEventListener("progress", this.e.onvProgress)) - } else this._mediaElement.removeEventListener("progress", this.e.onvProgress) - } - }, { - key: "_onvLoadedMetadata", - value: function(e) { - null != this._pendingSeekTime && (this._mediaElement.currentTime = this._pendingSeekTime, this._pendingSeekTime = null) - } - }, { - key: "_onvSeeking", - value: function(e) { - var t = this._mediaElement.currentTime, - n = this._mediaElement.buffered; - if (this._requestSetTime) return void(this._requestSetTime = !1); - if (t < 1 && n.length > 0) { - var i = n.start(0); - if (i < 1 && t < i || f.default.safari) return this._requestSetTime = !0, void(this._mediaElement.currentTime = f.default.safari ? .1 : i) - } - if (this._isTimepointBuffered(t)) { - if (this._alwaysSeekKeyframe) { - var r = this._msectl.getNearestKeyframe(Math.floor(1e3 * t)); - null != r && (this._requestSetTime = !0, this._mediaElement.currentTime = r.dts / 1e3) - } - return void(null != this._progressChecker && this._checkProgressAndResume()) - } - this._seekpointRecord = { - seekPoint: t, - recordTime: this._now() - }, window.setTimeout(this._checkAndApplyUnbufferedSeekpoint.bind(this), 50) - } - }, { - key: "_onvCanPlay", - value: function(e) { - this._receivedCanPlay = !0, this._mediaElement.removeEventListener("canplay", this.e.onvCanPlay) - } - }, { - key: "_onvStalled", - value: function(e) { - this._checkAndResumeStuckPlayback(!0) - } - }, { - key: "_onvProgress", - value: function(e) { - this._checkAndResumeStuckPlayback() - } - }, { - key: "type", - get: function() { - return this._type - } - }, { - key: "buffered", - get: function() { - return this._mediaElement.buffered - } - }, { - key: "duration", - get: function() { - return this._mediaElement.duration - } - }, { - key: "volume", - get: function() { - return this._mediaElement.volume - }, - set: function(e) { - this._mediaElement.volume = e - } - }, { - key: "muted", - get: function() { - return this._mediaElement.muted - }, - set: function(e) { - this._mediaElement.muted = e - } - }, { - key: "currentTime", - get: function() { - return this._mediaElement ? this._mediaElement.currentTime : 0 - }, - set: function(e) { - this._mediaElement ? this._internalSeek(e) : this._pendingSeekTime = e - } - }, { - key: "mediaInfo", - get: function() { - return Object.assign({}, this._mediaInfo) - } - }, { - key: "statisticsInfo", - get: function() { - return null == this._statisticsInfo && (this._statisticsInfo = {}), this._statisticsInfo = this._fillStatisticsInfo(this._statisticsInfo), Object.assign({}, this._statisticsInfo) - } - }]), e - }(); - n.default = R - }, { - "../config.js": 5, - "../core/mse-controller.js": 9, - "../core/mse-events.js": 10, - "../core/transmuxer.js": 11, - "../core/transmuxing-events.js": 13, - "../utils/browser.js": 39, - "../utils/exception.js": 40, - "../utils/logger.js": 41, - "./player-errors.js": 34, - "./player-events.js": 35, - events: 2 - }], - 33: [function(e, t, n) { - "use strict"; - - function i(e) { - return e && e.__esModule ? e : { - default: e - } - } - - function r(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var s = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(e) { - return typeof e - } : function(e) { - return e && "function" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : typeof e - }, - a = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - o = e("events"), - u = i(o), - l = e("./player-events.js"), - d = i(l), - h = e("../config.js"), - f = e("../utils/exception.js"), - c = function() { - function e(t, n) { - if (r(this, e), this.TAG = "NativePlayer", this._type = "NativePlayer", this._emitter = new u.default, this._config = (0, h.createDefaultConfig)(), "object" === (void 0 === n ? "undefined" : s(n)) && Object.assign(this._config, n), "flv" === t.type.toLowerCase()) throw new f.InvalidArgumentException("NativePlayer does't support flv MediaDataSource input!"); - if (t.hasOwnProperty("segments")) throw new f.InvalidArgumentException("NativePlayer(" + t.type + ") doesn't support multipart playback!"); - this.e = { - onvLoadedMetadata: this._onvLoadedMetadata.bind(this) - }, this._pendingSeekTime = null, this._statisticsReporter = null, this._mediaDataSource = t, this._mediaElement = null - } - return a(e, [{ - key: "destroy", - value: function() { - this._mediaElement && (this.unload(), this.detachMediaElement()), this.e = null, this._mediaDataSource = null, this._emitter.removeAllListeners(), this._emitter = null - } - }, { - key: "on", - value: function(e, t) { - var n = this; - e === d.default.MEDIA_INFO ? null != this._mediaElement && 0 !== this._mediaElement.readyState && Promise.resolve().then(function() { - n._emitter.emit(d.default.MEDIA_INFO, n.mediaInfo) - }) : e === d.default.STATISTICS_INFO && null != this._mediaElement && 0 !== this._mediaElement.readyState && Promise.resolve().then(function() { - n._emitter.emit(d.default.STATISTICS_INFO, n.statisticsInfo) - }), this._emitter.addListener(e, t) - } - }, { - key: "off", - value: function(e, t) { - this._emitter.removeListener(e, t) - } - }, { - key: "attachMediaElement", - value: function(e) { - if (this._mediaElement = e, e.addEventListener("loadedmetadata", this.e.onvLoadedMetadata), null != this._pendingSeekTime) try { - e.currentTime = this._pendingSeekTime, this._pendingSeekTime = null - } catch (e) {} - } - }, { - key: "detachMediaElement", - value: function() { - this._mediaElement && (this._mediaElement.src = "", this._mediaElement.removeAttribute("src"), this._mediaElement.removeEventListener("loadedmetadata", this.e.onvLoadedMetadata), this._mediaElement = null), null != this._statisticsReporter && (window.clearInterval(this._statisticsReporter), this._statisticsReporter = null) - } - }, { - key: "load", - value: function() { - if (!this._mediaElement) throw new f.IllegalStateException("HTMLMediaElement must be attached before load()!"); - this._mediaElement.src = this._mediaDataSource.url, this._mediaElement.readyState > 0 && (this._mediaElement.currentTime = 0), this._mediaElement.preload = "auto", this._mediaElement.load(), this._statisticsReporter = window.setInterval(this._reportStatisticsInfo.bind(this), this._config.statisticsInfoReportInterval) - } - }, { - key: "unload", - value: function() { - this._mediaElement && (this._mediaElement.src = "", this._mediaElement.removeAttribute("src")), null != this._statisticsReporter && (window.clearInterval(this._statisticsReporter), this._statisticsReporter = null) - } - }, { - key: "play", - value: function() { - return this._mediaElement.play() - } - }, { - key: "pause", - value: function() { - this._mediaElement.pause() - } - }, { - key: "_onvLoadedMetadata", - value: function(e) { - null != this._pendingSeekTime && (this._mediaElement.currentTime = this._pendingSeekTime, this._pendingSeekTime = null), this._emitter.emit(d.default.MEDIA_INFO, this.mediaInfo) - } - }, { - key: "_reportStatisticsInfo", - value: function() { - this._emitter.emit(d.default.STATISTICS_INFO, this.statisticsInfo) - } - }, { - key: "type", - get: function() { - return this._type - } - }, { - key: "buffered", - get: function() { - return this._mediaElement.buffered - } - }, { - key: "duration", - get: function() { - return this._mediaElement.duration - } - }, { - key: "volume", - get: function() { - return this._mediaElement.volume - }, - set: function(e) { - this._mediaElement.volume = e - } - }, { - key: "muted", - get: function() { - return this._mediaElement.muted - }, - set: function(e) { - this._mediaElement.muted = e - } - }, { - key: "currentTime", - get: function() { - return this._mediaElement ? this._mediaElement.currentTime : 0 - }, - set: function(e) { - this._mediaElement ? this._mediaElement.currentTime = e : this._pendingSeekTime = e - } - }, { - key: "mediaInfo", - get: function() { - var e = this._mediaElement instanceof HTMLAudioElement ? "audio/" : "video/", - t = { - mimeType: e + this._mediaDataSource.type - }; - return this._mediaElement && (t.duration = Math.floor(1e3 * this._mediaElement.duration), this._mediaElement instanceof HTMLVideoElement && (t.width = this._mediaElement.videoWidth, t.height = this._mediaElement.videoHeight)), t - } - }, { - key: "statisticsInfo", - get: function() { - var e = { - playerType: this._type, - url: this._mediaDataSource.url - }; - if (!(this._mediaElement instanceof HTMLVideoElement)) return e; - var t = !0, - n = 0, - i = 0; - if (this._mediaElement.getVideoPlaybackQuality) { - var r = this._mediaElement.getVideoPlaybackQuality(); - n = r.totalVideoFrames, i = r.droppedVideoFrames - } else void 0 != this._mediaElement.webkitDecodedFrameCount ? (n = this._mediaElement.webkitDecodedFrameCount, i = this._mediaElement.webkitDroppedFrameCount) : t = !1; - return t && (e.decodedFrames = n, e.droppedFrames = i), e - } - }]), e - }(); - n.default = c - }, { - "../config.js": 5, - "../utils/exception.js": 40, - "./player-events.js": 35, - events: 2 - }], - 34: [function(e, t, n) { - "use strict"; - Object.defineProperty(n, "__esModule", { - value: !0 - }), n.ErrorDetails = n.ErrorTypes = void 0; - var i = e("../io/loader.js"), - r = e("../demux/demux-errors.js"), - s = function(e) { - return e && e.__esModule ? e : { - default: e - } - }(r); - n.ErrorTypes = { - NETWORK_ERROR: "NetworkError", - MEDIA_ERROR: "MediaError", - OTHER_ERROR: "OtherError" - }, n.ErrorDetails = { - NETWORK_EXCEPTION: i.LoaderErrors.EXCEPTION, - NETWORK_STATUS_CODE_INVALID: i.LoaderErrors.HTTP_STATUS_CODE_INVALID, - NETWORK_TIMEOUT: i.LoaderErrors.CONNECTING_TIMEOUT, - NETWORK_UNRECOVERABLE_EARLY_EOF: i.LoaderErrors.UNRECOVERABLE_EARLY_EOF, - MEDIA_MSE_ERROR: "MediaMSEError", - MEDIA_FORMAT_ERROR: s.default.FORMAT_ERROR, - MEDIA_FORMAT_UNSUPPORTED: s.default.FORMAT_UNSUPPORTED, - MEDIA_CODEC_UNSUPPORTED: s.default.CODEC_UNSUPPORTED - } - }, { - "../demux/demux-errors.js": 16, - "../io/loader.js": 24 - }], - 35: [function(e, t, n) { - "use strict"; - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var i = { - ERROR: "error", - LOADING_COMPLETE: "loading_complete", - RECOVERED_EARLY_EOF: "recovered_early_eof", - MEDIA_INFO: "media_info", - STATISTICS_INFO: "statistics_info" - }; - n.default = i - }, {}], - 36: [function(e, t, n) { - "use strict"; - - function i(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var r = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - s = function() { - function e() { - i(this, e) - } - return r(e, null, [{ - key: "getSilentFrame", - value: function(e, t) { - if ("mp4a.40.2" === e) { - if (1 === t) return new Uint8Array([0, 200, 0, 128, 35, 128]); - if (2 === t) return new Uint8Array([33, 0, 73, 144, 2, 25, 0, 35, 128]); - if (3 === t) return new Uint8Array([0, 200, 0, 128, 32, 132, 1, 38, 64, 8, 100, 0, 142]); - if (4 === t) return new Uint8Array([0, 200, 0, 128, 32, 132, 1, 38, 64, 8, 100, 0, 128, 44, 128, 8, 2, 56]); - if (5 === t) return new Uint8Array([0, 200, 0, 128, 32, 132, 1, 38, 64, 8, 100, 0, 130, 48, 4, 153, 0, 33, 144, 2, 56]); - if (6 === t) return new Uint8Array([0, 200, 0, 128, 32, 132, 1, 38, 64, 8, 100, 0, 130, 48, 4, 153, 0, 33, 144, 2, 0, 178, 0, 32, 8, 224]) - } else { - if (1 === t) return new Uint8Array([1, 64, 34, 128, 163, 78, 230, 128, 186, 8, 0, 0, 0, 28, 6, 241, 193, 10, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 94]); - if (2 === t) return new Uint8Array([1, 64, 34, 128, 163, 94, 230, 128, 186, 8, 0, 0, 0, 0, 149, 0, 6, 241, 161, 10, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 94]); - if (3 === t) return new Uint8Array([1, 64, 34, 128, 163, 94, 230, 128, 186, 8, 0, 0, 0, 0, 149, 0, 6, 241, 161, 10, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 94]) - } - return null - } - }]), e - }(); - n.default = s - }, {}], - 37: [function(e, t, n) { - "use strict"; - - function i(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var r = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - s = function() { - function e() { - i(this, e) - } - return r(e, null, [{ - key: "init", - value: function() { - e.types = { - avc1: [], - avcC: [], - btrt: [], - dinf: [], - dref: [], - esds: [], - ftyp: [], - hdlr: [], - mdat: [], - mdhd: [], - mdia: [], - mfhd: [], - minf: [], - moof: [], - moov: [], - mp4a: [], - mvex: [], - mvhd: [], - sdtp: [], - stbl: [], - stco: [], - stsc: [], - stsd: [], - stsz: [], - stts: [], - tfdt: [], - tfhd: [], - traf: [], - trak: [], - trun: [], - trex: [], - tkhd: [], - vmhd: [], - smhd: [], - ".mp3": [] - }; - for (var t in e.types) e.types.hasOwnProperty(t) && (e.types[t] = [t.charCodeAt(0), t.charCodeAt(1), t.charCodeAt(2), t.charCodeAt(3)]); - var n = e.constants = {}; - n.FTYP = new Uint8Array([105, 115, 111, 109, 0, 0, 0, 1, 105, 115, 111, 109, 97, 118, 99, 49]), n.STSD_PREFIX = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1]), n.STTS = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]), n.STSC = n.STCO = n.STTS, n.STSZ = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), n.HDLR_VIDEO = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 118, 105, 100, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 105, 100, 101, 111, 72, 97, 110, 100, 108, 101, 114, 0]), n.HDLR_AUDIO = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 115, 111, 117, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 111, 117, 110, 100, 72, 97, 110, 100, 108, 101, 114, 0]), n.DREF = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 12, 117, 114, 108, 32, 0, 0, 0, 1]), n.SMHD = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]), n.VMHD = new Uint8Array([0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]) - } - }, { - key: "box", - value: function(e) { - for (var t = 8, n = null, i = Array.prototype.slice.call(arguments, 1), r = i.length, s = 0; s < r; s++) t += i[s].byteLength; - n = new Uint8Array(t), n[0] = t >>> 24 & 255, n[1] = t >>> 16 & 255, n[2] = t >>> 8 & 255, n[3] = 255 & t, n.set(e, 4); - for (var a = 8, o = 0; o < r; o++) n.set(i[o], a), a += i[o].byteLength; - return n - } - }, { - key: "generateInitSegment", - value: function(t) { - var n = e.box(e.types.ftyp, e.constants.FTYP), - i = e.moov(t), - r = new Uint8Array(n.byteLength + i.byteLength); - return r.set(n, 0), r.set(i, n.byteLength), r - } - }, { - key: "moov", - value: function(t) { - var n = e.mvhd(t.timescale, t.duration), - i = e.trak(t), - r = e.mvex(t); - return e.box(e.types.moov, n, i, r) - } - }, { - key: "mvhd", - value: function(t, n) { - return e.box(e.types.mvhd, new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, t >>> 24 & 255, t >>> 16 & 255, t >>> 8 & 255, 255 & t, n >>> 24 & 255, n >>> 16 & 255, n >>> 8 & 255, 255 & n, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255])) - } - }, { - key: "trak", - value: function(t) { - return e.box(e.types.trak, e.tkhd(t), e.mdia(t)) - } - }, { - key: "tkhd", - value: function(t) { - var n = t.id, - i = t.duration, - r = t.presentWidth, - s = t.presentHeight; - return e.box(e.types.tkhd, new Uint8Array([0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, n >>> 24 & 255, n >>> 16 & 255, n >>> 8 & 255, 255 & n, 0, 0, 0, 0, i >>> 24 & 255, i >>> 16 & 255, i >>> 8 & 255, 255 & i, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, r >>> 8 & 255, 255 & r, 0, 0, s >>> 8 & 255, 255 & s, 0, 0])) - } - }, { - key: "mdia", - value: function(t) { - return e.box(e.types.mdia, e.mdhd(t), e.hdlr(t), e.minf(t)) - } - }, { - key: "mdhd", - value: function(t) { - var n = t.timescale, - i = t.duration; - return e.box(e.types.mdhd, new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, n >>> 24 & 255, n >>> 16 & 255, n >>> 8 & 255, 255 & n, i >>> 24 & 255, i >>> 16 & 255, i >>> 8 & 255, 255 & i, 85, 196, 0, 0])) - } - }, { - key: "hdlr", - value: function(t) { - var n = null; - return n = "audio" === t.type ? e.constants.HDLR_AUDIO : e.constants.HDLR_VIDEO, e.box(e.types.hdlr, n) - } - }, { - key: "minf", - value: function(t) { - var n = null; - return n = "audio" === t.type ? e.box(e.types.smhd, e.constants.SMHD) : e.box(e.types.vmhd, e.constants.VMHD), e.box(e.types.minf, n, e.dinf(), e.stbl(t)) - } - }, { - key: "dinf", - value: function() { - return e.box(e.types.dinf, e.box(e.types.dref, e.constants.DREF)) - } - }, { - key: "stbl", - value: function(t) { - return e.box(e.types.stbl, e.stsd(t), e.box(e.types.stts, e.constants.STTS), e.box(e.types.stsc, e.constants.STSC), e.box(e.types.stsz, e.constants.STSZ), e.box(e.types.stco, e.constants.STCO)) - } - }, { - key: "stsd", - value: function(t) { - return "audio" === t.type ? "mp3" === t.codec ? e.box(e.types.stsd, e.constants.STSD_PREFIX, e.mp3(t)) : e.box(e.types.stsd, e.constants.STSD_PREFIX, e.mp4a(t)) : e.box(e.types.stsd, e.constants.STSD_PREFIX, e.avc1(t)) - } - }, { - key: "mp3", - value: function(t) { - var n = t.channelCount, - i = t.audioSampleRate, - r = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, n, 0, 16, 0, 0, 0, 0, i >>> 8 & 255, 255 & i, 0, 0]); - return e.box(e.types[".mp3"], r) - } - }, { - key: "mp4a", - value: function(t) { - var n = t.channelCount, - i = t.audioSampleRate, - r = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, n, 0, 16, 0, 0, 0, 0, i >>> 8 & 255, 255 & i, 0, 0]); - return e.box(e.types.mp4a, r, e.esds(t)) - } - }, { - key: "esds", - value: function(t) { - var n = t.config || [], - i = n.length, - r = new Uint8Array([0, 0, 0, 0, 3, 23 + i, 0, 1, 0, 4, 15 + i, 64, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5].concat([i]).concat(n).concat([6, 1, 2])); - return e.box(e.types.esds, r) - } - }, { - key: "avc1", - value: function(t) { - var n = t.avcc, - i = t.codecWidth, - r = t.codecHeight, - s = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, i >>> 8 & 255, 255 & i, r >>> 8 & 255, 255 & r, 0, 72, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 1, 10, 120, 113, 113, 47, 102, 108, 118, 46, 106, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 255, 255]); - return e.box(e.types.avc1, s, e.box(e.types.avcC, n)) - } - }, { - key: "mvex", - value: function(t) { - return e.box(e.types.mvex, e.trex(t)) - } - }, { - key: "trex", - value: function(t) { - var n = t.id, - i = new Uint8Array([0, 0, 0, 0, n >>> 24 & 255, n >>> 16 & 255, n >>> 8 & 255, 255 & n, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1]); - return e.box(e.types.trex, i) - } - }, { - key: "moof", - value: function(t, n) { - return e.box(e.types.moof, e.mfhd(t.sequenceNumber), e.traf(t, n)) - } - }, { - key: "mfhd", - value: function(t) { - var n = new Uint8Array([0, 0, 0, 0, t >>> 24 & 255, t >>> 16 & 255, t >>> 8 & 255, 255 & t]); - return e.box(e.types.mfhd, n) - } - }, { - key: "traf", - value: function(t, n) { - var i = t.id, - r = e.box(e.types.tfhd, new Uint8Array([0, 0, 0, 0, i >>> 24 & 255, i >>> 16 & 255, i >>> 8 & 255, 255 & i])), - s = e.box(e.types.tfdt, new Uint8Array([0, 0, 0, 0, n >>> 24 & 255, n >>> 16 & 255, n >>> 8 & 255, 255 & n])), - a = e.sdtp(t), - o = e.trun(t, a.byteLength + 16 + 16 + 8 + 16 + 8 + 8); - return e.box(e.types.traf, r, s, o, a) - } - }, { - key: "sdtp", - value: function(t) { - for (var n = t.samples || [], i = n.length, r = new Uint8Array(4 + i), s = 0; s < i; s++) { - var a = n[s].flags; - r[s + 4] = a.isLeading << 6 | a.dependsOn << 4 | a.isDependedOn << 2 | a.hasRedundancy - } - return e.box(e.types.sdtp, r) - } - }, { - key: "trun", - value: function(t, n) { - var i = t.samples || [], - r = i.length, - s = 12 + 16 * r, - a = new Uint8Array(s); - n += 8 + s, a.set([0, 0, 15, 1, r >>> 24 & 255, r >>> 16 & 255, r >>> 8 & 255, 255 & r, n >>> 24 & 255, n >>> 16 & 255, n >>> 8 & 255, 255 & n], 0); - for (var o = 0; o < r; o++) { - var u = i[o].duration, - l = i[o].size, - d = i[o].flags, - h = i[o].cts; - a.set([u >>> 24 & 255, u >>> 16 & 255, u >>> 8 & 255, 255 & u, l >>> 24 & 255, l >>> 16 & 255, l >>> 8 & 255, 255 & l, d.isLeading << 2 | d.dependsOn, d.isDependedOn << 6 | d.hasRedundancy << 4 | d.isNonSync, 0, 0, h >>> 24 & 255, h >>> 16 & 255, h >>> 8 & 255, 255 & h], 12 + 16 * o) - } - return e.box(e.types.trun, a) - } - }, { - key: "mdat", - value: function(t) { - return e.box(e.types.mdat, t) - } - }]), e - }(); - s.init(), n.default = s - }, {}], - 38: [function(e, t, n) { - "use strict"; - - function i(e) { - return e && e.__esModule ? e : { - default: e - } - } - - function r(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var s = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - a = e("../utils/logger.js"), - o = i(a), - u = e("./mp4-generator.js"), - l = i(u), - d = e("./aac-silent.js"), - h = i(d), - f = e("../utils/browser.js"), - c = i(f), - _ = e("../core/media-segment-info.js"), - m = e("../utils/exception.js"), - p = function() { - function e(t) { - r(this, e), this.TAG = "MP4Remuxer", this._config = t, this._isLive = !0 === t.isLive, this._dtsBase = -1, this._dtsBaseInited = !1, this._audioDtsBase = 1 / 0, this._videoDtsBase = 1 / 0, this._audioNextDts = void 0, this._videoNextDts = void 0, this._audioStashedLastSample = null, this._videoStashedLastSample = null, this._audioMeta = null, this._videoMeta = null, this._audioSegmentInfoList = new _.MediaSegmentInfoList("audio"), this._videoSegmentInfoList = new _.MediaSegmentInfoList("video"), this._onInitSegment = null, this._onMediaSegment = null, this._forceFirstIDR = !(!c.default.chrome || !(c.default.version.major < 50 || 50 === c.default.version.major && c.default.version.build < 2661)), this._fillSilentAfterSeek = c.default.msedge || c.default.msie, this._mp3UseMpegAudio = !c.default.firefox, this._fillAudioTimestampGap = this._config.fixAudioTimestampGap - } - return s(e, [{ - key: "destroy", - value: function() { - this._dtsBase = -1, this._dtsBaseInited = !1, this._audioMeta = null, this._videoMeta = null, this._audioSegmentInfoList.clear(), this._audioSegmentInfoList = null, this._videoSegmentInfoList.clear(), this._videoSegmentInfoList = null, this._onInitSegment = null, this._onMediaSegment = null - } - }, { - key: "bindDataSource", - value: function(e) { - return e.onDataAvailable = this.remux.bind(this), e.onTrackMetadata = this._onTrackMetadataReceived.bind(this), this - } - }, { - key: "insertDiscontinuity", - value: function() { - this._audioNextDts = this._videoNextDts = void 0 - } - }, { - key: "seek", - value: function(e) { - this._audioStashedLastSample = null, this._videoStashedLastSample = null, this._videoSegmentInfoList.clear(), this._audioSegmentInfoList.clear() - } - }, { - key: "remux", - value: function(e, t) { - if (!this._onMediaSegment) throw new m.IllegalStateException("MP4Remuxer: onMediaSegment callback must be specificed!"); - this._dtsBaseInited || this._calculateDtsBase(e, t), this._remuxVideo(t), this._remuxAudio(e) - } - }, { - key: "_onTrackMetadataReceived", - value: function(e, t) { - var n = null, - i = "mp4", - r = t.codec; - if ("audio" === e) this._audioMeta = t, "mp3" === t.codec && this._mp3UseMpegAudio ? (i = "mpeg", r = "", n = new Uint8Array) : n = l.default.generateInitSegment(t); - else { - if ("video" !== e) return; - this._videoMeta = t, n = l.default.generateInitSegment(t) - } - if (!this._onInitSegment) throw new m.IllegalStateException("MP4Remuxer: onInitSegment callback must be specified!"); - this._onInitSegment(e, { - type: e, - data: n.buffer, - codec: r, - container: e + "/" + i, - mediaDuration: t.duration - }) - } - }, { - key: "_calculateDtsBase", - value: function(e, t) { - this._dtsBaseInited || (e.samples && e.samples.length && (this._audioDtsBase = e.samples[0].dts), t.samples && t.samples.length && (this._videoDtsBase = t.samples[0].dts), this._dtsBase = Math.min(this._audioDtsBase, this._videoDtsBase), this._dtsBaseInited = !0) - } - }, { - key: "flushStashedSamples", - value: function() { - var e = this._videoStashedLastSample, - t = this._audioStashedLastSample, - n = { - type: "video", - id: 1, - sequenceNumber: 0, - samples: [], - length: 0 - }; - null != e && (n.samples.push(e), n.length = e.length); - var i = { - type: "audio", - id: 2, - sequenceNumber: 0, - samples: [], - length: 0 - }; - null != t && (i.samples.push(t), i.length = t.length), this._videoStashedLastSample = null, this._audioStashedLastSample = null, this._remuxVideo(n, !0), this._remuxAudio(i, !0) - } - }, { - key: "_remuxAudio", - value: function(e, t) { - if (null != this._audioMeta) { - var n = e, - i = n.samples, - r = void 0, - s = -1, - a = -1, - u = this._audioMeta.refSampleDuration, - d = "mp3" === this._audioMeta.codec && this._mp3UseMpegAudio, - f = this._dtsBaseInited && void 0 === this._audioNextDts, - m = !1; - if (i && 0 !== i.length && (1 !== i.length || t)) { - var p = 0, - v = null, - g = 0; - d ? (p = 0, g = n.length) : (p = 8, g = 8 + n.length); - var y = null; - if (i.length > 1 && (y = i.pop(), g -= y.length), null != this._audioStashedLastSample) { - var E = this._audioStashedLastSample; - this._audioStashedLastSample = null, i.unshift(E), g += E.length - } - null != y && (this._audioStashedLastSample = y); - var b = i[0].dts - this._dtsBase; - if (this._audioNextDts) r = b - this._audioNextDts; - else if (this._audioSegmentInfoList.isEmpty()) r = 0, this._fillSilentAfterSeek && !this._videoSegmentInfoList.isEmpty() && "mp3" !== this._audioMeta.originalCodec && (m = !0); - else { - var S = this._audioSegmentInfoList.getLastSampleBefore(b); - if (null != S) { - var k = b - (S.originalDts + S.duration); - k <= 3 && (k = 0); - var L = S.dts + S.duration + k; - r = b - L - } else r = 0 - } - if (m) { - var w = b - r, - R = this._videoSegmentInfoList.getLastSegmentBefore(b); - if (null != R && R.beginDts < w) { - var A = h.default.getSilentFrame(this._audioMeta.originalCodec, this._audioMeta.channelCount); - if (A) { - var O = R.beginDts, - T = w - R.beginDts; - o.default.v(this.TAG, "InsertPrefixSilentAudio: dts: " + O + ", duration: " + T), i.unshift({ - unit: A, - dts: O, - pts: O - }), g += A.byteLength - } - } else m = !1 - } - for (var C = [], I = 0; I < i.length; I++) { - var x = i[I], - M = x.unit, - D = x.dts - this._dtsBase, - B = D - r; - 1 === s && (s = B); - var j = 0; - if (I !== i.length - 1) { - j = i[I + 1].dts - this._dtsBase - r - B - } else if (null != y) { - var P = y.dts - this._dtsBase - r; - j = P - B - } else j = C.length >= 1 ? C[C.length - 1].duration : Math.floor(u); - var U = !1, - N = null; - if (j > 1.5 * u && "mp3" !== this._audioMeta.codec && this._fillAudioTimestampGap && !c.default.safari) { - U = !0; - var F = Math.abs(j - u), - G = Math.ceil(F / u), - V = B + u; - o.default.w(this.TAG, "Large audio timestamp gap detected, may cause AV sync to drift. Silent frames will be generated to avoid unsync.\ndts: " + (B + j) + " ms, expected: " + (B + Math.round(u)) + " ms, delta: " + Math.round(F) + " ms, generate: " + G + " frames"); - var z = h.default.getSilentFrame(this._audioMeta.originalCodec, this._audioMeta.channelCount); - null == z && (o.default.w(this.TAG, "Unable to generate silent frame for " + this._audioMeta.originalCodec + " with " + this._audioMeta.channelCount + " channels, repeat last frame"), z = M), N = []; - for (var H = 0; H < G; H++) { - var K = Math.round(V); - if (N.length > 0) { - var q = N[N.length - 1]; - q.duration = K - q.dts - } - var W = { - dts: K, - pts: K, - cts: 0, - unit: z, - size: z.byteLength, - duration: 0, - originalDts: D, - flags: { - isLeading: 0, - dependsOn: 1, - isDependedOn: 0, - hasRedundancy: 0 - } - }; - N.push(W), g += M.byteLength, V += u - } - var X = N[N.length - 1]; - X.duration = B + j - X.dts, j = Math.round(u) - } - C.push({ - dts: B, - pts: B, - cts: 0, - unit: x.unit, - size: x.unit.byteLength, - duration: j, - originalDts: D, - flags: { - isLeading: 0, - dependsOn: 1, - isDependedOn: 0, - hasRedundancy: 0 - } - }), U && C.push.apply(C, N) - } - d ? v = new Uint8Array(g) : (v = new Uint8Array(g), v[0] = g >>> 24 & 255, v[1] = g >>> 16 & 255, v[2] = g >>> 8 & 255, v[3] = 255 & g, v.set(l.default.types.mdat, 4)); - for (var Y = 0; Y < C.length; Y++) { - var Z = C[Y].unit; - v.set(Z, p), p += Z.byteLength - } - var Q = C[C.length - 1]; - a = Q.dts + Q.duration, this._audioNextDts = a; - var J = new _.MediaSegmentInfo; - J.beginDts = s, J.endDts = a, J.beginPts = s, J.endPts = a, J.originalBeginDts = C[0].originalDts, J.originalEndDts = Q.originalDts + Q.duration, J.firstSample = new _.SampleInfo(C[0].dts, C[0].pts, C[0].duration, C[0].originalDts, !1), J.lastSample = new _.SampleInfo(Q.dts, Q.pts, Q.duration, Q.originalDts, !1), this._isLive || this._audioSegmentInfoList.append(J), n.samples = C, n.sequenceNumber++; - var $ = null; - $ = d ? new Uint8Array : l.default.moof(n, s), n.samples = [], n.length = 0; - var ee = { - type: "audio", - data: this._mergeBoxes($, v).buffer, - sampleCount: C.length, - info: J - }; - d && f && (ee.timestampOffset = s), this._onMediaSegment("audio", ee) - } - } - } - }, { - key: "_remuxVideo", - value: function(e, t) { - if (null != this._videoMeta) { - var n = e, - i = n.samples, - r = void 0, - s = -1, - a = -1, - o = -1, - u = -1; - if (i && 0 !== i.length && (1 !== i.length || t)) { - var d = 8, - h = null, - f = 8 + e.length, - c = null; - if (i.length > 1 && (c = i.pop(), f -= c.length), null != this._videoStashedLastSample) { - var m = this._videoStashedLastSample; - this._videoStashedLastSample = null, i.unshift(m), f += m.length - } - null != c && (this._videoStashedLastSample = c); - var p = i[0].dts - this._dtsBase; - if (this._videoNextDts) r = p - this._videoNextDts; - else if (this._videoSegmentInfoList.isEmpty()) r = 0; - else { - var v = this._videoSegmentInfoList.getLastSampleBefore(p); - if (null != v) { - var g = p - (v.originalDts + v.duration); - g <= 3 && (g = 0); - var y = v.dts + v.duration + g; - r = p - y - } else r = 0 - } - for (var E = new _.MediaSegmentInfo, b = [], S = 0; S < i.length; S++) { - var k = i[S], - L = k.dts - this._dtsBase, - w = k.isKeyframe, - R = L - r, - A = k.cts, - O = R + A; - 1 === s && (s = R, o = O); - var T = 0; - if (S !== i.length - 1) { - T = i[S + 1].dts - this._dtsBase - r - R - } else if (null != c) { - var C = c.dts - this._dtsBase - r; - T = C - R - } else T = b.length >= 1 ? b[b.length - 1].duration : Math.floor(this._videoMeta.refSampleDuration); - if (w) { - var I = new _.SampleInfo(R, O, T, k.dts, !0); - I.fileposition = k.fileposition, E.appendSyncPoint(I) - } - b.push({ - dts: R, - pts: O, - cts: A, - units: k.units, - size: k.length, - isKeyframe: w, - duration: T, - originalDts: L, - flags: { - isLeading: 0, - dependsOn: w ? 2 : 1, - isDependedOn: w ? 1 : 0, - hasRedundancy: 0, - isNonSync: w ? 0 : 1 - } - }) - } - h = new Uint8Array(f), h[0] = f >>> 24 & 255, h[1] = f >>> 16 & 255, h[2] = f >>> 8 & 255, h[3] = 255 & f, h.set(l.default.types.mdat, 4); - for (var x = 0; x < b.length; x++) - for (var M = b[x].units; M.length;) { - var D = M.shift(), - B = D.data; - h.set(B, d), d += B.byteLength - } - var j = b[b.length - 1]; - if (a = j.dts + j.duration, u = j.pts + j.duration, this._videoNextDts = a, E.beginDts = s, E.endDts = a, E.beginPts = o, E.endPts = u, E.originalBeginDts = b[0].originalDts, E.originalEndDts = j.originalDts + j.duration, E.firstSample = new _.SampleInfo(b[0].dts, b[0].pts, b[0].duration, b[0].originalDts, b[0].isKeyframe), E.lastSample = new _.SampleInfo(j.dts, j.pts, j.duration, j.originalDts, j.isKeyframe), this._isLive || this._videoSegmentInfoList.append(E), n.samples = b, n.sequenceNumber++, this._forceFirstIDR) { - var P = b[0].flags; - P.dependsOn = 2, P.isNonSync = 0 - } - var U = l.default.moof(n, s); - n.samples = [], n.length = 0, this._onMediaSegment("video", { - type: "video", - data: this._mergeBoxes(U, h).buffer, - sampleCount: b.length, - info: E - }) - } - } - } - }, { - key: "_mergeBoxes", - value: function(e, t) { - var n = new Uint8Array(e.byteLength + t.byteLength); - return n.set(e, 0), n.set(t, e.byteLength), n - } - }, { - key: "onInitSegment", - get: function() { - return this._onInitSegment - }, - set: function(e) { - this._onInitSegment = e - } - }, { - key: "onMediaSegment", - get: function() { - return this._onMediaSegment - }, - set: function(e) { - this._onMediaSegment = e - } - }]), e - }(); - n.default = p - }, { - "../core/media-segment-info.js": 8, - "../utils/browser.js": 39, - "../utils/exception.js": 40, - "../utils/logger.js": 41, - "./aac-silent.js": 36, - "./mp4-generator.js": 37 - }], - 39: [function(e, t, n) { - "use strict"; - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var i = {}; - ! function() { - var e = self.navigator.userAgent.toLowerCase(), - t = /(edge)\/([\w.]+)/.exec(e) || /(opr)[\/]([\w.]+)/.exec(e) || /(chrome)[ \/]([\w.]+)/.exec(e) || /(iemobile)[\/]([\w.]+)/.exec(e) || /(version)(applewebkit)[ \/]([\w.]+).*(safari)[ \/]([\w.]+)/.exec(e) || /(webkit)[ \/]([\w.]+).*(version)[ \/]([\w.]+).*(safari)[ \/]([\w.]+)/.exec(e) || /(webkit)[ \/]([\w.]+)/.exec(e) || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e) || /(msie) ([\w.]+)/.exec(e) || e.indexOf("trident") >= 0 && /(rv)(?::| )([\w.]+)/.exec(e) || e.indexOf("compatible") < 0 && /(firefox)[ \/]([\w.]+)/.exec(e) || [], - n = /(ipad)/.exec(e) || /(ipod)/.exec(e) || /(windows phone)/.exec(e) || /(iphone)/.exec(e) || /(kindle)/.exec(e) || /(android)/.exec(e) || /(windows)/.exec(e) || /(mac)/.exec(e) || /(linux)/.exec(e) || /(cros)/.exec(e) || [], - r = { - browser: t[5] || t[3] || t[1] || "", - version: t[2] || t[4] || "0", - majorVersion: t[4] || t[2] || "0", - platform: n[0] || "" - }, - s = {}; - if (r.browser) { - s[r.browser] = !0; - var a = r.majorVersion.split("."); - s.version = { - major: parseInt(r.majorVersion, 10), - string: r.version - }, a.length > 1 && (s.version.minor = parseInt(a[1], 10)), a.length > 2 && (s.version.build = parseInt(a[2], 10)) - } - r.platform && (s[r.platform] = !0), (s.chrome || s.opr || s.safari) && (s.webkit = !0), (s.rv || s.iemobile) && (s.rv && delete s.rv, r.browser = "msie", s.msie = !0), s.edge && (delete s.edge, r.browser = "msedge", s.msedge = !0), s.opr && (r.browser = "opera", s.opera = !0), s.safari && s.android && (r.browser = "android", s.android = !0), s.name = r.browser, s.platform = r.platform; - for (var o in i) i.hasOwnProperty(o) && delete i[o]; - Object.assign(i, s) - }(), n.default = i - }, {}], - 40: [function(e, t, n) { - "use strict"; - - function i(e, t) { - if (!e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); - return !t || "object" != typeof t && "function" != typeof t ? e : t - } - - function r(e, t) { - if ("function" != typeof t && null !== t) throw new TypeError("Super expression must either be null or a function, not " + typeof t); - e.prototype = Object.create(t && t.prototype, { - constructor: { - value: e, - enumerable: !1, - writable: !0, - configurable: !0 - } - }), t && (Object.setPrototypeOf ? Object.setPrototypeOf(e, t) : e.__proto__ = t) - } - - function s(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var a = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - o = n.RuntimeException = function() { - function e(t) { - s(this, e), this._message = t - } - return a(e, [{ - key: "toString", - value: function() { - return this.name + ": " + this.message - } - }, { - key: "name", - get: function() { - return "RuntimeException" - } - }, { - key: "message", - get: function() { - return this._message - } - }]), e - }(); - n.IllegalStateException = function(e) { - function t(e) { - return s(this, t), i(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e)) - } - return r(t, e), a(t, [{ - key: "name", - get: function() { - return "IllegalStateException" - } - }]), t - }(o), n.InvalidArgumentException = function(e) { - function t(e) { - return s(this, t), i(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e)) - } - return r(t, e), a(t, [{ - key: "name", - get: function() { - return "InvalidArgumentException" - } - }]), t - }(o), n.NotImplementedException = function(e) { - function t(e) { - return s(this, t), i(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e)) - } - return r(t, e), a(t, [{ - key: "name", - get: function() { - return "NotImplementedException" - } - }]), t - }(o) - }, {}], - 41: [function(e, t, n) { - "use strict"; - - function i(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var r = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - s = e("events"), - a = function(e) { - return e && e.__esModule ? e : { - default: e - } - }(s), - o = function() { - function e() { - i(this, e) - } - return r(e, null, [{ - key: "e", - value: function(t, n) { - t && !e.FORCE_GLOBAL_TAG || (t = e.GLOBAL_TAG); - var i = "[" + t + "] > " + n; - e.ENABLE_CALLBACK && e.emitter.emit("log", "error", i), e.ENABLE_ERROR && (console.error ? console.error(i) : console.warn ? console.warn(i) : console.log(i)) - } - }, { - key: "i", - value: function(t, n) { - t && !e.FORCE_GLOBAL_TAG || (t = e.GLOBAL_TAG); - var i = "[" + t + "] > " + n; - e.ENABLE_CALLBACK && e.emitter.emit("log", "info", i), e.ENABLE_INFO && (console.info ? console.info(i) : console.log(i)) - } - }, { - key: "w", - value: function(t, n) { - t && !e.FORCE_GLOBAL_TAG || (t = e.GLOBAL_TAG); - var i = "[" + t + "] > " + n; - e.ENABLE_CALLBACK && e.emitter.emit("log", "warn", i), e.ENABLE_WARN && (console.warn ? console.warn(i) : console.log(i)) - } - }, { - key: "d", - value: function(t, n) { - t && !e.FORCE_GLOBAL_TAG || (t = e.GLOBAL_TAG); - var i = "[" + t + "] > " + n; - e.ENABLE_CALLBACK && e.emitter.emit("log", "debug", i), e.ENABLE_DEBUG && (console.debug ? console.debug(i) : console.log(i)) - } - }, { - key: "v", - value: function(t, n) { - t && !e.FORCE_GLOBAL_TAG || (t = e.GLOBAL_TAG); - var i = "[" + t + "] > " + n; - e.ENABLE_CALLBACK && e.emitter.emit("log", "verbose", i), e.ENABLE_VERBOSE && console.log(i) - } - }]), e - }(); - o.GLOBAL_TAG = "flv.js", o.FORCE_GLOBAL_TAG = !1, o.ENABLE_ERROR = !0, o.ENABLE_INFO = !0, o.ENABLE_WARN = !0, o.ENABLE_DEBUG = !0, o.ENABLE_VERBOSE = !0, o.ENABLE_CALLBACK = !1, o.emitter = new a.default, n.default = o - }, { - events: 2 - }], - 42: [function(e, t, n) { - "use strict"; - - function i(e) { - return e && e.__esModule ? e : { - default: e - } - } - - function r(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var s = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - a = e("events"), - o = i(a), - u = e("./logger.js"), - l = i(u), - d = function() { - function e() { - r(this, e) - } - return s(e, null, [{ - key: "getConfig", - value: function() { - return { - globalTag: l.default.GLOBAL_TAG, - forceGlobalTag: l.default.FORCE_GLOBAL_TAG, - enableVerbose: l.default.ENABLE_VERBOSE, - enableDebug: l.default.ENABLE_DEBUG, - enableInfo: l.default.ENABLE_INFO, - enableWarn: l.default.ENABLE_WARN, - enableError: l.default.ENABLE_ERROR, - enableCallback: l.default.ENABLE_CALLBACK - } - } - }, { - key: "applyConfig", - value: function(e) { - l.default.GLOBAL_TAG = e.globalTag, l.default.FORCE_GLOBAL_TAG = e.forceGlobalTag, l.default.ENABLE_VERBOSE = e.enableVerbose, l.default.ENABLE_DEBUG = e.enableDebug, l.default.ENABLE_INFO = e.enableInfo, l.default.ENABLE_WARN = e.enableWarn, l.default.ENABLE_ERROR = e.enableError, l.default.ENABLE_CALLBACK = e.enableCallback - } - }, { - key: "_notifyChange", - value: function() { - var t = e.emitter; - if (t.listenerCount("change") > 0) { - var n = e.getConfig(); - t.emit("change", n) - } - } - }, { - key: "registerListener", - value: function(t) { - e.emitter.addListener("change", t) - } - }, { - key: "removeListener", - value: function(t) { - e.emitter.removeListener("change", t) - } - }, { - key: "addLogListener", - value: function(t) { - l.default.emitter.addListener("log", t), l.default.emitter.listenerCount("log") > 0 && (l.default.ENABLE_CALLBACK = !0, e._notifyChange()) - } - }, { - key: "removeLogListener", - value: function(t) { - l.default.emitter.removeListener("log", t), 0 === l.default.emitter.listenerCount("log") && (l.default.ENABLE_CALLBACK = !1, e._notifyChange()) - } - }, { - key: "forceGlobalTag", - get: function() { - return l.default.FORCE_GLOBAL_TAG - }, - set: function(t) { - l.default.FORCE_GLOBAL_TAG = t, e._notifyChange() - } - }, { - key: "globalTag", - get: function() { - return l.default.GLOBAL_TAG - }, - set: function(t) { - l.default.GLOBAL_TAG = t, e._notifyChange() - } - }, { - key: "enableAll", - get: function() { - return l.default.ENABLE_VERBOSE && l.default.ENABLE_DEBUG && l.default.ENABLE_INFO && l.default.ENABLE_WARN && l.default.ENABLE_ERROR - }, - set: function(t) { - l.default.ENABLE_VERBOSE = t, l.default.ENABLE_DEBUG = t, l.default.ENABLE_INFO = t, l.default.ENABLE_WARN = t, l.default.ENABLE_ERROR = t, e._notifyChange() - } - }, { - key: "enableDebug", - get: function() { - return l.default.ENABLE_DEBUG - }, - set: function(t) { - l.default.ENABLE_DEBUG = t, e._notifyChange() - } - }, { - key: "enableVerbose", - get: function() { - return l.default.ENABLE_VERBOSE - }, - set: function(t) { - l.default.ENABLE_VERBOSE = t, e._notifyChange() - } - }, { - key: "enableInfo", - get: function() { - return l.default.ENABLE_INFO - }, - set: function(t) { - l.default.ENABLE_INFO = t, e._notifyChange() - } - }, { - key: "enableWarn", - get: function() { - return l.default.ENABLE_WARN - }, - set: function(t) { - l.default.ENABLE_WARN = t, e._notifyChange() - } - }, { - key: "enableError", - get: function() { - return l.default.ENABLE_ERROR - }, - set: function(t) { - l.default.ENABLE_ERROR = t, e._notifyChange() - } - }]), e - }(); - d.emitter = new o.default, n.default = d - }, { - "./logger.js": 41, - events: 2 - }], - 43: [function(e, t, n) { - "use strict"; - - function i(e, t) { - if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }); - var r = function() { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var i = t[n]; - i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i) - } - } - return function(t, n, i) { - return n && e(t.prototype, n), i && e(t, i), t - } - }(), - s = function() { - function t() { - i(this, t) - } - return r(t, null, [{ - key: "install", - value: function() { - Object.setPrototypeOf = Object.setPrototypeOf || function(e, t) { - return e.__proto__ = t, e - }, Object.assign = Object.assign || function(e) { - if (void 0 === e || null === e) throw new TypeError("Cannot convert undefined or null to object"); - for (var t = Object(e), n = 1; n < arguments.length; n++) { - var i = arguments[n]; - if (void 0 !== i && null !== i) - for (var r in i) i.hasOwnProperty(r) && (t[r] = i[r]) - } - return t - }, "function" != typeof self.Promise && e("es6-promise").polyfill() - } - }]), t - }(); - s.install(), n.default = s - }, { - "es6-promise": 1 - }], - 44: [function(e, t, n) { - "use strict"; - - function i(e, t, n) { - var i = e; - if (t + n < i.length) { - for (; n--;) - if (128 != (192 & i[++t])) return !1; - return !0 - } - return !1 - } - - function r(e) { - for (var t = [], n = e, r = 0, s = e.length; r < s;) - if (n[r] < 128) t.push(String.fromCharCode(n[r])), ++r; - else { - if (n[r] < 192); - else if (n[r] < 224) { - if (i(n, r, 1)) { - var a = (31 & n[r]) << 6 | 63 & n[r + 1]; - if (a >= 128) { - t.push(String.fromCharCode(65535 & a)), r += 2; - continue - } - } - } else if (n[r] < 240) { - if (i(n, r, 2)) { - var o = (15 & n[r]) << 12 | (63 & n[r + 1]) << 6 | 63 & n[r + 2]; - if (o >= 2048 && 55296 != (63488 & o)) { - t.push(String.fromCharCode(65535 & o)), r += 3; - continue - } - } - } else if (n[r] < 248 && i(n, r, 3)) { - var u = (7 & n[r]) << 18 | (63 & n[r + 1]) << 12 | (63 & n[r + 2]) << 6 | 63 & n[r + 3]; - if (u > 65536 && u < 1114112) { - u -= 65536, t.push(String.fromCharCode(u >>> 10 | 55296)), t.push(String.fromCharCode(1023 & u | 56320)), r += 4; - continue - } - } - t.push(String.fromCharCode(65533)), ++r - } return t.join("") - } - Object.defineProperty(n, "__esModule", { - value: !0 - }), n.default = r - }, {}] - }, {}, [21])(21) -}); -//# sourceMappingURL=flv.min.js.map \ No newline at end of file +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.flvjs=e()}}(function(){var e;return function e(t,n,i){function r(a,o){if(!n[a]){if(!t[a]){var u="function"==typeof require&&require;if(!o&&u)return u(a,!0);if(s)return s(a,!0);var l=new Error("Cannot find module '"+a+"'");throw l.code="MODULE_NOT_FOUND",l}var d=n[a]={exports:{}};t[a][0].call(d.exports,function(e){var n=t[a][1][e];return r(n||e)},d,d.exports,e,t,n,i)}return n[a].exports}for(var s="function"==typeof require&&require,a=0;a0&&this._events[e].length>n&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"==typeof console.trace&&console.trace()),this},i.prototype.on=i.prototype.addListener,i.prototype.once=function(e,t){function n(){this.removeListener(e,n),i||(i=!0,t.apply(this,arguments))}if(!r(t))throw TypeError("listener must be a function");var i=!1;return n.listener=t,this.on(e,n),this},i.prototype.removeListener=function(e,t){var n,i,s,o;if(!r(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;if(n=this._events[e],s=n.length,i=-1,n===t||r(n.listener)&&n.listener===t)delete this._events[e],this._events.removeListener&&this.emit("removeListener",e,t);else if(a(n)){for(o=s;o-- >0;)if(n[o]===t||n[o].listener&&n[o].listener===t){i=o;break}if(i<0)return this;1===n.length?(n.length=0,delete this._events[e]):n.splice(i,1),this._events.removeListener&&this.emit("removeListener",e,t)}return this},i.prototype.removeAllListeners=function(e){var t,n;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[e]&&delete this._events[e],this;if(0===arguments.length){for(t in this._events)"removeListener"!==t&&this.removeAllListeners(t);return this.removeAllListeners("removeListener"),this._events={},this}if(n=this._events[e],r(n))this.removeListener(e,n);else if(n)for(;n.length;)this.removeListener(e,n[n.length-1]);return delete this._events[e],this},i.prototype.listeners=function(e){return this._events&&this._events[e]?r(this._events[e])?[this._events[e]]:this._events[e].slice():[]},i.prototype.listenerCount=function(e){if(this._events){var t=this._events[e];if(r(t))return 1;if(t)return t.length}return 0},i.listenerCount=function(e,t){return e.listenerCount(t)}},{}],3:[function(e,t,n){function i(){throw new Error("setTimeout has not been defined")}function r(){throw new Error("clearTimeout has not been defined")}function s(e){if(h===setTimeout)return setTimeout(e,0);if((h===i||!h)&&setTimeout)return h=setTimeout,setTimeout(e,0);try{return h(e,0)}catch(t){try{return h.call(null,e,0)}catch(t){return h.call(this,e,0)}}}function a(e){if(f===clearTimeout)return clearTimeout(e);if((f===r||!f)&&clearTimeout)return f=clearTimeout,clearTimeout(e);try{return f(e)}catch(t){try{return f.call(null,e)}catch(t){return f.call(this,e)}}}function o(){p&&_&&(p=!1,_.length?m=_.concat(m):v=-1,m.length&&u())}function u(){if(!p){var e=s(o);p=!0;for(var t=m.length;t;){for(_=m,m=[];++v1)for(var n=1;n=e[r]&&t0&&e[0].originalDts=t[r].dts&&et[i].lastSample.originalDts&&e=t[i].lastSample.originalDts&&(i===t.length-1||i0&&(r=this._searchNearestSegmentBefore(n.originalBeginDts)+1),this._lastAppendLocation=r,this._list.splice(r,0,n)}},{key:"getLastSegmentBefore",value:function(e){var t=this._searchNearestSegmentBefore(e);return t>=0?this._list[t]:null}},{key:"getLastSampleBefore",value:function(e){var t=this.getLastSegmentBefore(e);return null!=t?t.lastSample:null}},{key:"getLastSyncPointBefore",value:function(e){for(var t=this._searchNearestSegmentBefore(e),n=this._list[t].syncPoints;0===n.length&&t>0;)t--,n=this._list[t].syncPoints;return n.length>0?n[n.length-1]:null}},{key:"type",get:function(){return this._type}},{key:"length",get:function(){return this._list.length}}]),e}()},{}],9:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var s=function(){function e(e,t){for(var n=0;n0&&(i+=";codecs="+n.codec);var r=!1;if(l.default.v(this.TAG,"Received Initialization Segment, mimeType: "+i),this._lastInitSegments[n.type]=n,i!==this._mimeTypes[n.type]){if(this._mimeTypes[n.type])l.default.v(this.TAG,"Notice: "+n.type+" mimeType changed, origin: "+this._mimeTypes[n.type]+", target: "+i);else{r=!0;try{var s=this._sourceBuffers[n.type]=this._mediaSource.addSourceBuffer(i);s.addEventListener("error",this.e.onSourceBufferError),s.addEventListener("updateend",this.e.onSourceBufferUpdateEnd)}catch(e){return l.default.e(this.TAG,e.message),void this._emitter.emit(c.default.ERROR,{code:e.code,msg:e.message})}}this._mimeTypes[n.type]=i}t||this._pendingSegments[n.type].push(n),r||this._sourceBuffers[n.type]&&!this._sourceBuffers[n.type].updating&&this._doAppendSegments(),h.default.safari&&"audio/mpeg"===n.container&&n.mediaDuration>0&&(this._requireSetMediaDuration=!0,this._pendingMediaDuration=n.mediaDuration/1e3,this._updateMediaSourceDuration())}},{key:"appendMediaSegment",value:function(e){var t=e;this._pendingSegments[t.type].push(t),this._config.autoCleanupSourceBuffer&&this._needCleanupSourceBuffer()&&this._doCleanupSourceBuffer();var n=this._sourceBuffers[t.type];!n||n.updating||this._hasPendingRemoveRanges()||this._doAppendSegments()}},{key:"seek",value:function(e){for(var t in this._sourceBuffers)if(this._sourceBuffers[t]){var n=this._sourceBuffers[t];if("open"===this._mediaSource.readyState)try{n.abort()}catch(e){l.default.e(this.TAG,e.message)}this._idrList.clear();var i=this._pendingSegments[t];if(i.splice(0,i.length),"closed"!==this._mediaSource.readyState){for(var r=0;r=1&&e-i.start(0)>=this._config.autoCleanupMaxBackwardDuration)return!0}}return!1}},{key:"_doCleanupSourceBuffer",value:function(){var e=this._mediaElement.currentTime;for(var t in this._sourceBuffers){var n=this._sourceBuffers[t];if(n){for(var i=n.buffered,r=!1,s=0;s=this._config.autoCleanupMaxBackwardDuration){r=!0;var u=e-this._config.autoCleanupMinBackwardDuration;this._pendingRemoveRanges[t].push({start:a,end:u})}}else o0&&(isNaN(t)||n>t)&&(l.default.v(this.TAG,"Update MediaSource duration from "+t+" to "+n),this._mediaSource.duration=n),this._requireSetMediaDuration=!1,this._pendingMediaDuration=0}}},{key:"_doRemoveRanges",value:function(){for(var e in this._pendingRemoveRanges)if(this._sourceBuffers[e]&&!this._sourceBuffers[e].updating)for(var t=this._sourceBuffers[e],n=this._pendingRemoveRanges[e];n.length&&!t.updating;){var i=n.shift();t.remove(i.start,i.end)}}},{key:"_doAppendSegments",value:function(){var e=this._pendingSegments;for(var t in e)if(this._sourceBuffers[t]&&!this._sourceBuffers[t].updating&&e[t].length>0){var n=e[t].shift();if(n.timestampOffset){var i=this._sourceBuffers[t].timestampOffset,r=n.timestampOffset/1e3,s=Math.abs(i-r);s>.1&&(l.default.v(this.TAG,"Update MPEG audio timestampOffset from "+i+" to "+r),this._sourceBuffers[t].timestampOffset=r),delete n.timestampOffset}if(!n.data||0===n.data.byteLength)continue;try{this._sourceBuffers[t].appendBuffer(n.data),this._isBufferFull=!1,"video"===t&&n.hasOwnProperty("info")&&this._idrList.appendArray(n.info.syncPoints)}catch(e){this._pendingSegments[t].unshift(n),22===e.code?(this._isBufferFull||this._emitter.emit(c.default.BUFFER_FULL),this._isBufferFull=!0):(l.default.e(this.TAG,e.message),this._emitter.emit(c.default.ERROR,{code:e.code,msg:e.message}))}}}},{key:"_onSourceOpen",value:function(){if(l.default.v(this.TAG,"MediaSource onSourceOpen"),this._mediaSource.removeEventListener("sourceopen",this.e.onSourceOpen),this._pendingSourceBufferInit.length>0)for(var e=this._pendingSourceBufferInit;e.length;){var t=e.shift();this.appendInitSegment(t,!0)}this._hasPendingSegments()&&this._doAppendSegments(),this._emitter.emit(c.default.SOURCE_OPEN)}},{key:"_onSourceEnded",value:function(){l.default.v(this.TAG,"MediaSource onSourceEnded")}},{key:"_onSourceClose",value:function(){l.default.v(this.TAG,"MediaSource onSourceClose"),this._mediaSource&&null!=this.e&&(this._mediaSource.removeEventListener("sourceopen",this.e.onSourceOpen),this._mediaSource.removeEventListener("sourceended",this.e.onSourceEnded),this._mediaSource.removeEventListener("sourceclose",this.e.onSourceClose))}},{key:"_hasPendingSegments",value:function(){var e=this._pendingSegments;return e.video.length>0||e.audio.length>0}},{key:"_hasPendingRemoveRanges",value:function(){var e=this._pendingRemoveRanges;return e.video.length>0||e.audio.length>0}},{key:"_onSourceBufferUpdateEnd",value:function(){this._requireSetMediaDuration?this._updateMediaSourceDuration():this._hasPendingRemoveRanges()?this._doRemoveRanges():this._hasPendingSegments()?this._doAppendSegments():this._hasPendingEos&&this.endOfStream(),this._emitter.emit(c.default.UPDATE_END)}},{key:"_onSourceBufferError",value:function(e){l.default.e(this.TAG,"SourceBuffer Error: "+e)}}]),e}();n.default=p},{"../utils/browser.js":39,"../utils/exception.js":40,"../utils/logger.js":41,"./media-segment-info.js":8,"./mse-events.js":10,events:2}],10:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var i={ERROR:"error",SOURCE_OPEN:"source_open",UPDATE_END:"update_end",BUFFER_FULL:"buffer_full"};n.default=i},{}],11:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var s=function(){function e(e,t){for(var n=0;n0)this._demuxer.bindDataSource(this._ioctl),this._demuxer.timestampBase=this._mediaDataSource.segments[this._currentSegmentIndex].timestampBase,r=this._demuxer.parseChunks(e,t);else if((i=m.default.probe(e)).match){this._demuxer=new m.default(i,this._config),this._remuxer||(this._remuxer=new v.default(this._config));var s=this._mediaDataSource;void 0==s.duration||isNaN(s.duration)||(this._demuxer.overridedDuration=s.duration),"boolean"==typeof s.hasAudio&&(this._demuxer.overridedHasAudio=s.hasAudio),"boolean"==typeof s.hasVideo&&(this._demuxer.overridedHasVideo=s.hasVideo),this._demuxer.timestampBase=s.segments[this._currentSegmentIndex].timestampBase,this._demuxer.onError=this._onDemuxException.bind(this),this._demuxer.onMediaInfo=this._onMediaInfo.bind(this),this._remuxer.bindDataSource(this._demuxer.bindDataSource(this._ioctl)),this._remuxer.onInitSegment=this._onRemuxerInitSegmentArrival.bind(this),this._remuxer.onMediaSegment=this._onRemuxerMediaSegmentArrival.bind(this),r=this._demuxer.parseChunks(e,t)}else i=null,l.default.e(this.TAG,"Non-FLV, Unsupported media type!"),Promise.resolve().then(function(){n._internalAbort()}),this._emitter.emit(k.default.DEMUX_ERROR,y.default.FORMAT_UNSUPPORTED,"Non-FLV, Unsupported media type"),r=0;return r}},{key:"_onMediaInfo",value:function(e){var t=this;null==this._mediaInfo&&(this._mediaInfo=Object.assign({},e),this._mediaInfo.keyframesIndex=null,this._mediaInfo.segments=[],this._mediaInfo.segmentCount=this._mediaDataSource.segments.length,Object.setPrototypeOf(this._mediaInfo,c.default.prototype));var n=Object.assign({},e);Object.setPrototypeOf(n,c.default.prototype),this._mediaInfo.segments[this._currentSegmentIndex]=n,this._reportSegmentMediaInfo(this._currentSegmentIndex),null!=this._pendingSeekTime&&Promise.resolve().then(function(){var e=t._pendingSeekTime;t._pendingSeekTime=null,t.seek(e)})}},{key:"_onIOSeeked",value:function(){this._remuxer.insertDiscontinuity()}},{key:"_onIOComplete",value:function(e){var t=e,n=t+1;n0&&n[0].originalDts===i&&(i=n[0].pts),this._emitter.emit(k.default.RECOMMEND_SEEKPOINT,i)}}},{key:"_enableStatisticsReporter",value:function(){null==this._statisticsReporter&&(this._statisticsReporter=self.setInterval(this._reportStatisticsInfo.bind(this),this._config.statisticsInfoReportInterval))}},{key:"_disableStatisticsReporter",value:function(){this._statisticsReporter&&(self.clearInterval(this._statisticsReporter),this._statisticsReporter=null)}},{key:"_reportSegmentMediaInfo",value:function(e){var t=this._mediaInfo.segments[e],n=Object.assign({},t);n.duration=this._mediaInfo.duration,n.segmentCount=this._mediaInfo.segmentCount,delete n.segments,delete n.keyframesIndex,this._emitter.emit(k.default.MEDIA_INFO,n)}},{key:"_reportStatisticsInfo",value:function(){var e={};e.url=this._ioctl.currentURL,e.hasRedirect=this._ioctl.hasRedirect,e.hasRedirect&&(e.redirectedURL=this._ioctl.currentRedirectedURL),e.speed=this._ioctl.currentSpeed,e.loaderType=this._ioctl.loaderType,e.currentSegmentIndex=this._currentSegmentIndex,e.totalSegmentCount=this._mediaDataSource.segments.length,this._emitter.emit(k.default.STATISTICS_INFO,e)}}]),e}());n.default=L},{"../demux/demux-errors.js":16,"../demux/flv-demuxer.js":18,"../io/io-controller.js":23,"../io/loader.js":24,"../remux/mp4-remuxer.js":38,"../utils/browser.js":39,"../utils/logger.js":41,"./media-info.js":7,"./transmuxing-events.js":13,events:2}],13:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var i={IO_ERROR:"io_error",DEMUX_ERROR:"demux_error",INIT_SEGMENT:"init_segment",MEDIA_SEGMENT:"media_segment",LOADING_COMPLETE:"loading_complete",RECOVERED_EARLY_EOF:"recovered_early_eof",MEDIA_INFO:"media_info",STATISTICS_INFO:"statistics_info",RECOMMEND_SEEKPOINT:"recommend_seekpoint"};n.default=i},{}],14:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(n,"__esModule",{value:!0});var r=e("../utils/logger.js"),s=(i(r),e("../utils/logging-control.js")),a=i(s),o=e("../utils/polyfill.js"),u=i(o),l=e("./transmuxing-controller.js"),d=i(l),h=e("./transmuxing-events.js"),f=i(h),c=function(e){function t(t,n){var i={msg:f.default.INIT_SEGMENT,data:{type:t,data:n}};e.postMessage(i,[n.data])}function n(t,n){var i={msg:f.default.MEDIA_SEGMENT,data:{type:t,data:n}};e.postMessage(i,[n.data])}function i(){var t={msg:f.default.LOADING_COMPLETE};e.postMessage(t)}function r(){var t={msg:f.default.RECOVERED_EARLY_EOF};e.postMessage(t)}function s(t){var n={msg:f.default.MEDIA_INFO,data:t};e.postMessage(n)}function o(t){var n={msg:f.default.STATISTICS_INFO,data:t};e.postMessage(n)}function l(t,n){e.postMessage({msg:f.default.IO_ERROR,data:{type:t,info:n}})}function h(t,n){e.postMessage({msg:f.default.DEMUX_ERROR,data:{type:t,info:n}})}function c(t){e.postMessage({msg:f.default.RECOMMEND_SEEKPOINT,data:t})}function _(t,n){e.postMessage({msg:"logcat_callback",data:{type:t,logcat:n}})}var m=null,p=_.bind(this);u.default.install(),e.addEventListener("message",function(u){switch(u.data.cmd){case"init":m=new d.default(u.data.param[0],u.data.param[1]),m.on(f.default.IO_ERROR,l.bind(this)),m.on(f.default.DEMUX_ERROR,h.bind(this)),m.on(f.default.INIT_SEGMENT,t.bind(this)),m.on(f.default.MEDIA_SEGMENT,n.bind(this)),m.on(f.default.LOADING_COMPLETE,i.bind(this)),m.on(f.default.RECOVERED_EARLY_EOF,r.bind(this)),m.on(f.default.MEDIA_INFO,s.bind(this)),m.on(f.default.STATISTICS_INFO,o.bind(this)),m.on(f.default.RECOMMEND_SEEKPOINT,c.bind(this));break;case"destroy":m&&(m.destroy(),m=null),e.postMessage({msg:"destroyed"});break;case"start":m.start();break;case"stop":m.stop();break;case"seek":m.seek(u.data.param);break;case"pause":m.pause();break;case"resume":m.resume();break;case"logging_config":var _=u.data.param;a.default.applyConfig(_),!0===_.enableCallback?a.default.addLogListener(p):a.default.removeLogListener(p)}})};n.default=c},{"../utils/logger.js":41,"../utils/logging-control.js":42,"../utils/polyfill.js":43,"./transmuxing-controller.js":12,"./transmuxing-events.js":13}],15:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var s=function(){function e(e,t){for(var n=0;n0?(0,l.default)(new Uint8Array(e,t+2,r)):"",{data:s,size:2+r}}},{key:"parseLongString",value:function(e,t,n){if(n<4)throw new d.IllegalStateException("Data not enough when parse LongString");var i=new DataView(e,t,n),r=i.getUint32(0,!h),s=void 0;return s=r>0?(0,l.default)(new Uint8Array(e,t+4,r)):"",{data:s,size:4+r}}},{key:"parseDate",value:function(e,t,n){if(n<10)throw new d.IllegalStateException("Data size invalid when parse Date");var i=new DataView(e,t,n),r=i.getFloat64(0,!h);return r+=60*i.getInt16(8,!h)*1e3,{data:new Date(r),size:10}}},{key:"parseValue",value:function(t,n,i){if(i<1)throw new d.IllegalStateException("Data not enough when parse Value");var r=new DataView(t,n,i),s=1,a=r.getUint8(0),u=void 0,l=!1;try{switch(a){case 0:u=r.getFloat64(1,!h),s+=8;break;case 1:u=!!r.getUint8(1),s+=1;break;case 2:var f=e.parseString(t,n+1,i-1);u=f.data,s+=f.size;break;case 3:u={};var c=0;for(9==(16777215&r.getUint32(i-4,!h))&&(c=3);s32)throw new s.InvalidArgumentException("ExpGolomb: readBits() bits exceeded max 32bits!");if(e<=this._current_word_bits_left){var t=this._current_word>>>32-e;return this._current_word<<=e,this._current_word_bits_left-=e,t}var n=this._current_word_bits_left?this._current_word:0;n>>>=32-this._current_word_bits_left;var i=e-this._current_word_bits_left;this._fillCurrentWord();var r=Math.min(i,this._current_word_bits_left),a=this._current_word>>>32-r;return this._current_word<<=r,this._current_word_bits_left-=r,n=n<>>e))return this._current_word<<=e,this._current_word_bits_left-=e,e;return this._fillCurrentWord(),e+this._skipLeadingZero()}},{key:"readUEG",value:function(){var e=this._skipLeadingZero();return this.readBits(e+1)-1}},{key:"readSEG",value:function(){var e=this.readUEG();return 1&e?e+1>>>1:-1*(e>>>1)}}]),e}();n.default=a},{"../utils/exception.js":40}],18:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function s(e,t){return e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3]}Object.defineProperty(n,"__esModule",{value:!0});var a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},o=function(){function e(e,t){for(var n=0;n13))return 0;i=e.probe(t).dataOffset}if(this._firstParse){this._firstParse=!1,n+i!==this._dataOffset&&l.default.w(this.TAG,"First time parsing but chunk byteStart invalid!");0!==new DataView(t,i).getUint32(0,!r)&&l.default.w(this.TAG,"PrevTagSize0 !== 0 !!!"),i+=4}for(;it.byteLength)break;var a=s.getUint8(0),o=16777215&s.getUint32(0,!r);if(i+11+o+4>t.byteLength)break;if(8===a||9===a||18===a){var u=s.getUint8(4),d=s.getUint8(5),h=s.getUint8(6),f=s.getUint8(7),c=h|d<<8|u<<16|f<<24;0!==(16777215&s.getUint32(7,!r))&&l.default.w(this.TAG,"Meet tag which has StreamID != 0!");var _=i+11;switch(a){case 8:this._parseAudioData(t,_,o,c);break;case 9:this._parseVideoData(t,_,o,c,n+i);break;case 18:this._parseScriptData(t,_,o)}var m=s.getUint32(11+o,!r);m!==11+o&&l.default.w(this.TAG,"Invalid PrevTagSize "+m),i+=11+o+4}else l.default.w(this.TAG,"Unsupported tag type "+a+", skipped"),i+=11+o+4}return this._isInitialMetadataDispatched()&&this._dispatch&&(this._audioTrack.length||this._videoTrack.length)&&this._onDataAvailable(this._audioTrack,this._videoTrack),i}},{key:"_parseScriptData",value:function(e,t,n){var i=h.default.parseScriptData(e,t,n);if(i.hasOwnProperty("onMetaData")){if(null==i.onMetaData||"object"!==a(i.onMetaData))return void l.default.w(this.TAG,"Invalid onMetaData structure!");this._metadata&&l.default.w(this.TAG,"Found another onMetaData tag!"),this._metadata=i;var r=this._metadata.onMetaData;if("boolean"==typeof r.hasAudio&&!1===this._hasAudioFlagOverrided&&(this._hasAudio=r.hasAudio,this._mediaInfo.hasAudio=this._hasAudio),"boolean"==typeof r.hasVideo&&!1===this._hasVideoFlagOverrided&&(this._hasVideo=r.hasVideo,this._mediaInfo.hasVideo=this._hasVideo),"number"==typeof r.audiodatarate&&(this._mediaInfo.audioDataRate=r.audiodatarate),"number"==typeof r.videodatarate&&(this._mediaInfo.videoDataRate=r.videodatarate),"number"==typeof r.width&&(this._mediaInfo.width=r.width),"number"==typeof r.height&&(this._mediaInfo.height=r.height),"number"==typeof r.duration){if(!this._durationOverrided){var s=Math.floor(r.duration*this._timescale);this._duration=s,this._mediaInfo.duration=s}}else this._mediaInfo.duration=0;if("number"==typeof r.framerate){var o=Math.floor(1e3*r.framerate);if(o>0){var u=o/1e3;this._referenceFrameRate.fixed=!0,this._referenceFrameRate.fps=u,this._referenceFrameRate.fps_num=o,this._referenceFrameRate.fps_den=1e3,this._mediaInfo.fps=u}}if("object"===a(r.keyframes)){this._mediaInfo.hasKeyframesIndex=!0;var d=r.keyframes;this._mediaInfo.keyframesIndex=this._parseKeyframesIndex(d),r.keyframes=null}else this._mediaInfo.hasKeyframesIndex=!1;this._dispatch=!1,this._mediaInfo.metadata=r,l.default.v(this.TAG,"Parsed onMetaData"),this._mediaInfo.isComplete()&&this._onMediaInfo(this._mediaInfo)}}},{key:"_parseKeyframesIndex",value:function(e){for(var t=[],n=[],i=1;i>>4;if(2!==a&&10!==a)return void this._onError(m.default.CODEC_UNSUPPORTED,"Flv: Unsupported audio codec idx: "+a);var o=0,u=(12&s)>>>2;if(!(u>=0&&u<=4))return void this._onError(m.default.FORMAT_ERROR,"Flv: Invalid audio sample rate idx: "+u);o=this._flvSoundRateTable[u];var d=1&s,h=this._audioMetadata,f=this._audioTrack;if(h||(!1===this._hasAudio&&!1===this._hasAudioFlagOverrided&&(this._hasAudio=!0,this._mediaInfo.hasAudio=!0),h=this._audioMetadata={},h.type="audio",h.id=f.id,h.timescale=this._timescale,h.duration=this._duration,h.audioSampleRate=o,h.channelCount=0===d?1:2),10===a){var c=this._parseAACAudioData(e,t+1,n-1);if(void 0==c)return;if(0===c.packetType){h.config&&l.default.w(this.TAG,"Found another AudioSpecificConfig!");var _=c.data;h.audioSampleRate=_.samplingRate,h.channelCount=_.channelCount,h.codec=_.codec,h.originalCodec=_.originalCodec,h.config=_.config,h.refSampleDuration=1024/h.audioSampleRate*h.timescale,l.default.v(this.TAG,"Parsed AudioSpecificConfig"),this._isInitialMetadataDispatched()?this._dispatch&&(this._audioTrack.length||this._videoTrack.length)&&this._onDataAvailable(this._audioTrack,this._videoTrack):this._audioInitialMetadataDispatched=!0,this._dispatch=!1,this._onTrackMetadata("audio",h);var p=this._mediaInfo;p.audioCodec=h.originalCodec,p.audioSampleRate=h.audioSampleRate,p.audioChannelCount=h.channelCount,p.hasVideo?null!=p.videoCodec&&(p.mimeType='video/x-flv; codecs="'+p.videoCodec+","+p.audioCodec+'"'):p.mimeType='video/x-flv; codecs="'+p.audioCodec+'"',p.isComplete()&&this._onMediaInfo(p)}else if(1===c.packetType){var v=this._timestampBase+i,g={unit:c.data,length:c.data.byteLength,dts:v,pts:v};f.samples.push(g),f.length+=c.data.length}else l.default.e(this.TAG,"Flv: Unsupported AAC data type "+c.packetType)}else if(2===a){if(!h.codec){var y=this._parseMP3AudioData(e,t+1,n-1,!0);if(void 0==y)return;h.audioSampleRate=y.samplingRate,h.channelCount=y.channelCount,h.codec=y.codec,h.originalCodec=y.originalCodec,h.refSampleDuration=1152/h.audioSampleRate*h.timescale,l.default.v(this.TAG,"Parsed MPEG Audio Frame Header"),this._audioInitialMetadataDispatched=!0,this._onTrackMetadata("audio",h);var E=this._mediaInfo;E.audioCodec=h.codec,E.audioSampleRate=h.audioSampleRate,E.audioChannelCount=h.channelCount,E.audioDataRate=y.bitRate,E.hasVideo?null!=E.videoCodec&&(E.mimeType='video/x-flv; codecs="'+E.videoCodec+","+E.audioCodec+'"'):E.mimeType='video/x-flv; codecs="'+E.audioCodec+'"',E.isComplete()&&this._onMediaInfo(E)}var b=this._parseMP3AudioData(e,t+1,n-1,!1);if(void 0==b)return +;var S=this._timestampBase+i,k={unit:b,length:b.byteLength,dts:S,pts:S};f.samples.push(k),f.length+=b.length}}}},{key:"_parseAACAudioData",value:function(e,t,n){if(n<=1)return void l.default.w(this.TAG,"Flv: Invalid AAC packet, missing AACPacketType or/and Data!");var i={},r=new Uint8Array(e,t,n);return i.packetType=r[0],0===r[0]?i.data=this._parseAACAudioSpecificConfig(e,t+1,n-1):i.data=r.subarray(1),i}},{key:"_parseAACAudioSpecificConfig",value:function(e,t,n){var i=new Uint8Array(e,t,n),r=null,s=0,a=0,o=0,u=null;if(s=a=i[0]>>>3,(o=(7&i[0])<<1|i[1]>>>7)<0||o>=this._mpegSamplingRates.length)return void this._onError(m.default.FORMAT_ERROR,"Flv: AAC invalid sampling frequency index!");var l=this._mpegSamplingRates[o],d=(120&i[1])>>>3;if(d<0||d>=8)return void this._onError(m.default.FORMAT_ERROR,"Flv: AAC invalid channel configuration");5===s&&(u=(7&i[1])<<1|i[2]>>>7,i[2]);var h=self.navigator.userAgent.toLowerCase();return-1!==h.indexOf("firefox")?o>=6?(s=5,r=new Array(4),u=o-3):(s=2,r=new Array(2),u=o):-1!==h.indexOf("android")?(s=2,r=new Array(2),u=o):(s=5,u=o,r=new Array(4),o>=6?u=o-3:1===d&&(s=2,r=new Array(2),u=o)),r[0]=s<<3,r[0]|=(15&o)>>>1,r[1]=(15&o)<<7,r[1]|=(15&d)<<3,5===s&&(r[1]|=(15&u)>>>1,r[2]=(1&u)<<7,r[2]|=8,r[3]=0),{config:r,samplingRate:l,channelCount:d,codec:"mp4a.40."+s,originalCodec:"mp4a.40."+a}}},{key:"_parseMP3AudioData",value:function(e,t,n,i){if(n<4)return void l.default.w(this.TAG,"Flv: Invalid MP3 packet, header missing!");var r=(this._littleEndian,new Uint8Array(e,t,n)),s=null;if(i){if(255!==r[0])return;var a=r[1]>>>3&3,o=(6&r[1])>>1,u=(240&r[2])>>>4,d=(12&r[2])>>>2,h=r[3]>>>6&3,f=3!==h?2:1,c=0,_=0;switch(a){case 0:c=this._mpegAudioV25SampleRateTable[d];break;case 2:c=this._mpegAudioV20SampleRateTable[d];break;case 3:c=this._mpegAudioV10SampleRateTable[d]}switch(o){case 1:34,u>>4,o=15&s;if(7!==o)return void this._onError(m.default.CODEC_UNSUPPORTED,"Flv: Unsupported codec in video frame: "+o);this._parseAVCVideoPacket(e,t+1,n-1,i,r,a)}}},{key:"_parseAVCVideoPacket",value:function(e,t,n,i,r,s){if(n<4)return void l.default.w(this.TAG,"Flv: Invalid AVC packet, missing AVCPacketType or/and CompositionTime");var a=this._littleEndian,o=new DataView(e,t,n),u=o.getUint8(0),d=16777215&o.getUint32(0,!a),h=d<<8>>8;if(0===u)this._parseAVCDecoderConfigurationRecord(e,t+4,n-4);else if(1===u)this._parseAVCVideoData(e,t+4,n-4,i,r,s,h);else if(2!==u)return void this._onError(m.default.FORMAT_ERROR,"Flv: Invalid video packet type "+u)}},{key:"_parseAVCDecoderConfigurationRecord",value:function(e,t,n){if(n<7)return void l.default.w(this.TAG,"Flv: Invalid AVCDecoderConfigurationRecord, lack of data!");var i=this._videoMetadata,r=this._videoTrack,s=this._littleEndian,a=new DataView(e,t,n);i?void 0!==i.avcc&&l.default.w(this.TAG,"Found another AVCDecoderConfigurationRecord!"):(!1===this._hasVideo&&!1===this._hasVideoFlagOverrided&&(this._hasVideo=!0,this._mediaInfo.hasVideo=!0),i=this._videoMetadata={},i.type="video",i.id=r.id,i.timescale=this._timescale,i.duration=this._duration);var o=a.getUint8(0),u=a.getUint8(1);a.getUint8(2),a.getUint8(3);if(1!==o||0===u)return void this._onError(m.default.FORMAT_ERROR,"Flv: Invalid AVCDecoderConfigurationRecord");if(this._naluLengthSize=1+(3&a.getUint8(4)),3!==this._naluLengthSize&&4!==this._naluLengthSize)return void this._onError(m.default.FORMAT_ERROR,"Flv: Strange NaluLengthSizeMinusOne: "+(this._naluLengthSize-1));var d=31&a.getUint8(5);if(0===d)return void this._onError(m.default.FORMAT_ERROR,"Flv: Invalid AVCDecoderConfigurationRecord: No SPS");d>1&&l.default.w(this.TAG,"Flv: Strange AVCDecoderConfigurationRecord: SPS Count = "+d);for(var h=6,f=0;f1&&l.default.w(this.TAG,"Flv: Strange AVCDecoderConfigurationRecord: PPS Count = "+w),h++;for(var R=0;R=n){l.default.w(this.TAG,"Malformed Nalu near timestamp "+_+", offset = "+f+", dataSize = "+n);break}var p=u.getUint32(f,!o);if(3===c&&(p>>>=8),p>n-c)return void l.default.w(this.TAG,"Malformed Nalus near timestamp "+_+", NaluSize > DataSize!");var v=31&u.getUint8(f+c);5===v&&(m=!0);var g=new Uint8Array(e,t+f,c+p),y={type:v,data:g};d.push(y),h+=g.byteLength,f+=c+p}if(d.length){var E=this._videoTrack,b={units:d,length:h,isKeyframe:m,dts:_,cts:a,pts:_+a};m&&(b.fileposition=r),E.samples.push(b),E.length+=h}}},{key:"onTrackMetadata",get:function(){return this._onTrackMetadata},set:function(e){this._onTrackMetadata=e}},{key:"onMediaInfo",get:function(){return this._onMediaInfo},set:function(e){this._onMediaInfo=e}},{key:"onError",get:function(){return this._onError},set:function(e){this._onError=e}},{key:"onDataAvailable",get:function(){return this._onDataAvailable},set:function(e){this._onDataAvailable=e}},{key:"timestampBase",get:function(){return this._timestampBase},set:function(e){this._timestampBase=e}},{key:"overridedDuration",get:function(){return this._duration},set:function(e){this._durationOverrided=!0,this._duration=e,this._mediaInfo.duration=e}},{key:"overridedHasAudio",set:function(e){this._hasAudioFlagOverrided=!0,this._hasAudio=e,this._mediaInfo.hasAudio=e}},{key:"overridedHasVideo",set:function(e){this._hasVideoFlagOverrided=!0,this._hasVideo=e,this._mediaInfo.hasVideo=e}}],[{key:"probe",value:function(e){var t=new Uint8Array(e),n={match:!1};if(70!==t[0]||76!==t[1]||86!==t[2]||1!==t[3])return n;var i=(4&t[4])>>>2!=0,r=0!=(1&t[4]),a=s(t,5);return a<9?n:{match:!0,consumed:a,dataOffset:a,hasAudioTrack:i,hasVideoTrack:r}}}]),e}();n.default=y},{"../core/media-info.js":7,"../utils/exception.js":40,"../utils/logger.js":41,"./amf-parser.js":15,"./demux-errors.js":16,"./sps-parser.js":19}],19:[function(e,t,n){"use strict";function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var r=function(){function e(e,t){for(var n=0;n=2&&3===t[s]&&0===t[s-1]&&0===t[s-2]||(i[r]=t[s],r++);return new Uint8Array(i.buffer,0,r)}},{key:"parseSPS",value:function(t){var n=e._ebsp2rbsp(t),i=new a.default(n);i.readByte();var r=i.readByte();i.readByte();var s=i.readByte();i.readUEG();var o=e.getProfileString(r),u=e.getLevelString(s),l=1,d=420,h=[0,420,422,444],f=8;if((100===r||110===r||122===r||244===r||44===r||83===r||86===r||118===r||128===r||138===r||144===r)&&(l=i.readUEG(),3===l&&i.readBits(1),l<=3&&(d=h[l]),f=i.readUEG()+8,i.readUEG(),i.readBits(1),i.readBool()))for(var c=3!==l?8:12,_=0;_0&&x<16?(R=M[x-1],A=D[x-1]):255===x&&(R=i.readByte()<<8|i.readByte(),A=i.readByte()<<8|i.readByte())}if(i.readBool()&&i.readBool(),i.readBool()&&(i.readBits(4),i.readBool()&&i.readBits(24)),i.readBool()&&(i.readUEG(),i.readUEG()),i.readBool()){var B=i.readBits(32),j=i.readBits(32);T=i.readBool(),C=j,I=2*B,O=C/I}}var P=1;1===R&&1===A||(P=R/A);var U=0,N=0;if(0===l)U=1,N=2-b;else{var F=3===l?1:2,G=1===l?2:1;U=F,N=G*(2-b)}var V=16*(y+1),z=16*(E+1)*(2-b);V-=(S+k)*U,z-=(L+w)*N;var H=Math.ceil(V*P);return i.destroy(),i=null,{profile_string:o,level_string:u,bit_depth:f,ref_frames:g,chroma_format:d,chroma_format_string:e.getChromaFormatString(d),frame_rate:{fixed:T,fps:O,fps_den:I,fps_num:C},sar_ratio:{width:R,height:A},codec_size:{width:V,height:z},present_size:{width:H,height:z}}}},{key:"_skipScalingList",value:function(e,t){for(var n=8,i=8,r=0,s=0;s=15048,t=!f.default.msedge||e;return self.fetch&&self.ReadableStream&&t}catch(e){return!1}}}]),l(t,[{key:"destroy",value:function(){this.isWorking()&&this.abort(),u(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"destroy",this).call(this)}},{key:"open",value:function(e,t){var n=this;this._dataSource=e,this._range=t;var i=e.url;this._config.reuseRedirectedURL&&void 0!=e.redirectedURL&&(i=e.redirectedURL);var r=this._seekHandler.getConfig(i,t),s=new self.Headers;if("object"===o(r.headers)){var a=r.headers;for(var u in a)a.hasOwnProperty(u)&&s.append(u,a[u])}var l={method:"GET",headers:s,mode:"cors",cache:"default",referrerPolicy:"no-referrer-when-downgrade"};!1===e.cors&&(l.mode="same-origin"),e.withCredentials&&(l.credentials="include"),e.referrerPolicy&&(l.referrerPolicy=e.referrerPolicy),this._status=c.LoaderStatus.kConnecting,self.fetch(r.url,l).then(function(e){if(n._requestAbort)return n._requestAbort=!1,void(n._status=c.LoaderStatus.kIdle);if(e.ok&&e.status>=200&&e.status<=299){if(e.url!==r.url&&n._onURLRedirect){var t=n._seekHandler.removeURLParameters(e.url);n._onURLRedirect(t)}var i=e.headers.get("Content-Length");return null!=i&&(n._contentLength=parseInt(i),0!==n._contentLength&&n._onContentLengthKnown&&n._onContentLengthKnown(n._contentLength)),n._pump.call(n,e.body.getReader())}if(n._status=c.LoaderStatus.kError,!n._onError)throw new _.RuntimeException("FetchStreamLoader: Http code invalid, "+e.status+" "+e.statusText);n._onError(c.LoaderErrors.HTTP_STATUS_CODE_INVALID,{code:e.status,msg:e.statusText})}).catch(function(e){if(n._status=c.LoaderStatus.kError,!n._onError)throw e;n._onError(c.LoaderErrors.EXCEPTION,{code:-1,msg:e.message})})}},{key:"abort",value:function(){this._requestAbort=!0}},{key:"_pump",value:function(e){var t=this;return e.read().then(function(n){if(n.done)if(null!==t._contentLength&&t._receivedLength0&&(this._stashInitialSize=n.stashInitialSize),this._stashUsed=0,this._stashSize=this._stashInitialSize,this._bufferSize=3145728,this._stashBuffer=new ArrayBuffer(this._bufferSize),this._stashByteStart=0,this._enableStash=!0,!1===n.enableStashBuffer&&(this._enableStash=!1),this._loader=null,this._loaderClass=null,this._seekHandler=null,this._dataSource=t,this._isWebSocketURL=/wss?:\/\/(.+?)/.test(t.url),this._refTotalLength=t.filesize?t.filesize:null,this._totalLength=this._refTotalLength,this._fullRequestFlag=!1,this._currentRange=null,this._redirectedURL=null,this._speedNormalized=0,this._speedSampler=new l.default,this._speedNormalizeList=[64,128,256,384,512,768,1024,1536,2048,3072,4096],this._isEarlyEofReconnecting=!1,this._paused=!1,this._resumeFrom=0,this._onDataArrival=null,this._onSeeked=null,this._onError=null,this._onComplete=null,this._onRedirect=null,this._onRecoveredEarlyEof=null,this._selectSeekHandler(),this._selectLoader(),this._createLoader()}return s(e,[{key:"destroy",value:function(){this._loader.isWorking()&&this._loader.abort(),this._loader.destroy(),this._loader=null,this._loaderClass=null,this._dataSource=null,this._stashBuffer=null,this._stashUsed=this._stashSize=this._bufferSize=this._stashByteStart=0,this._currentRange=null,this._speedSampler=null,this._isEarlyEofReconnecting=!1,this._onDataArrival=null,this._onSeeked=null,this._onError=null,this._onComplete=null,this._onRedirect=null,this._onRecoveredEarlyEof=null,this._extraData=null}},{key:"isWorking",value:function(){return this._loader&&this._loader.isWorking()&&!this._paused}},{key:"isPaused",value:function(){return this._paused}},{key:"_selectSeekHandler",value:function(){var e=this._config;if("range"===e.seekType)this._seekHandler=new b.default(this._config.rangeLoadZeroStart);else if("param"===e.seekType){var t=e.seekParamStart||"bstart",n=e.seekParamEnd||"bend";this._seekHandler=new k.default(t,n)}else{if("custom"!==e.seekType)throw new L.InvalidArgumentException("Invalid seekType in config: "+e.seekType);if("function"!=typeof e.customSeekHandler)throw new L.InvalidArgumentException("Custom seekType specified in config but invalid customSeekHandler!");this._seekHandler=new e.customSeekHandler}}},{key:"_selectLoader",value:function(){if(this._isWebSocketURL)this._loaderClass=y.default;else if(f.default.isSupported())this._loaderClass=f.default;else if(_.default.isSupported())this._loaderClass=_.default;else{if(!v.default.isSupported())throw new L.RuntimeException("Your browser doesn't support xhr with arraybuffer responseType!");this._loaderClass=v.default}}},{key:"_createLoader",value:function(){this._loader=new this._loaderClass(this._seekHandler,this._config),!1===this._loader.needStashBuffer&&(this._enableStash=!1),this._loader.onContentLengthKnown=this._onContentLengthKnown.bind(this),this._loader.onURLRedirect=this._onURLRedirect.bind(this),this._loader.onDataArrival=this._onLoaderChunkArrival.bind(this),this._loader.onComplete=this._onLoaderComplete.bind(this),this._loader.onError=this._onLoaderError.bind(this)}},{key:"open",value:function(e){this._currentRange={from:0,to:-1},e&&(this._currentRange.from=e),this._speedSampler.reset(),e||(this._fullRequestFlag=!0),this._loader.open(this._dataSource,Object.assign({},this._currentRange))}},{key:"abort",value:function(){this._loader.abort(),this._paused&&(this._paused=!1,this._resumeFrom=0)}},{key:"pause",value:function(){this.isWorking()&&(this._loader.abort(),0!==this._stashUsed?(this._resumeFrom=this._stashByteStart,this._currentRange.to=this._stashByteStart-1):this._resumeFrom=this._currentRange.to+1,this._stashUsed=0,this._stashByteStart=0,this._paused=!0)}},{key:"resume",value:function(){if(this._paused){this._paused=!1;var e=this._resumeFrom;this._resumeFrom=0,this._internalSeek(e,!0)}}},{key:"seek",value:function(e){this._paused=!1,this._stashUsed=0,this._stashByteStart=0,this._internalSeek(e,!0)}},{key:"_internalSeek",value:function(e,t){this._loader.isWorking()&&this._loader.abort(),this._flushStashBuffer(t),this._loader.destroy(),this._loader=null;var n={from:e,to:-1};this._currentRange={from:n.from,to:-1},this._speedSampler.reset(),this._stashSize=this._stashInitialSize,this._createLoader(),this._loader.open(this._dataSource,n),this._onSeeked&&this._onSeeked()}},{key:"updateUrl",value:function(e){if(!e||"string"!=typeof e||0===e.length)throw new L.InvalidArgumentException("Url must be a non-empty string!");this._dataSource.url=e}},{key:"_expandBuffer",value:function(e){for(var t=this._stashSize;t+10485760){var i=new Uint8Array(this._stashBuffer,0,this._stashUsed);new Uint8Array(n,0,t).set(i,0)}this._stashBuffer=n,this._bufferSize=t}}},{key:"_normalizeSpeed",value:function(e){var t=this._speedNormalizeList,n=t.length-1,i=0,r=0,s=n;if(e=t[i]&&e=512&&e<=1024?Math.floor(1.5*e):2*e)>8192&&(t=8192);var n=1024*t+1048576;this._bufferSize0){var o=this._stashBuffer.slice(0,this._stashUsed),u=this._dispatchChunks(o,this._stashByteStart);if(u0){var l=new Uint8Array(o,u);a.set(l,0),this._stashUsed=l.byteLength,this._stashByteStart+=u}}else this._stashUsed=0,this._stashByteStart+=u;this._stashUsed+e.byteLength>this._bufferSize&&(this._expandBuffer(this._stashUsed+e.byteLength),a=new Uint8Array(this._stashBuffer,0,this._bufferSize)),a.set(new Uint8Array(e),this._stashUsed),this._stashUsed+=e.byteLength}else{var d=this._dispatchChunks(e,t);if(dthis._bufferSize&&(this._expandBuffer(h),a=new Uint8Array(this._stashBuffer,0,this._bufferSize)),a.set(new Uint8Array(e,d),0),this._stashUsed+=h,this._stashByteStart=t+d}}}else if(0===this._stashUsed){var f=this._dispatchChunks(e,t);if(fthis._bufferSize&&this._expandBuffer(c);var _=new Uint8Array(this._stashBuffer,0,this._bufferSize);_.set(new Uint8Array(e,f),0),this._stashUsed+=c,this._stashByteStart=t+f}}else{this._stashUsed+e.byteLength>this._bufferSize&&this._expandBuffer(this._stashUsed+e.byteLength);var m=new Uint8Array(this._stashBuffer,0,this._bufferSize);m.set(new Uint8Array(e),this._stashUsed),this._stashUsed+=e.byteLength;var p=this._dispatchChunks(this._stashBuffer.slice(0,this._stashUsed),this._stashByteStart);if(p0){var v=new Uint8Array(this._stashBuffer,p);m.set(v,0)}this._stashUsed-=p,this._stashByteStart+=p}}}},{key:"_flushStashBuffer",value:function(e){if(this._stashUsed>0){var t=this._stashBuffer.slice(0,this._stashUsed),n=this._dispatchChunks(t,this._stashByteStart),i=t.byteLength-n;if(n0){var r=new Uint8Array(this._stashBuffer,0,this._bufferSize),s=new Uint8Array(t,n);r.set(s,0),this._stashUsed=s.byteLength,this._stashByteStart+=n}return 0}o.default.w(this.TAG,i+" bytes unconsumed data remain when flush buffer, dropped")}return this._stashUsed=0,this._stashByteStart=0,i}return 0}},{key:"_onLoaderComplete",value:function(e,t){this._flushStashBuffer(!0),this._onComplete&&this._onComplete(this._extraData)}},{key:"_onLoaderError",value:function(e,t){switch(o.default.e(this.TAG,"Loader error, code = "+t.code+", msg = "+t.msg),this._flushStashBuffer(!1),this._isEarlyEofReconnecting&&(this._isEarlyEofReconnecting=!1,e=d.LoaderErrors.UNRECOVERABLE_EARLY_EOF),e){case d.LoaderErrors.EARLY_EOF:if(!this._config.isLive&&this._totalLength){var n=this._currentRange.to+1;return void(n0)for(var s=n.split("&"),a=0;a0;o[0]!==this._startName&&o[0]!==this._endName&&(u&&(r+="&"),r+=s[a])}return 0===r.length?t:t+"?"+r}}]),e}();n.default=s},{}],26:[function(e,t,n){"use strict";function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var r=function(){function e(e,t){for(var n=0;n=500?this.currentKBps:0}},{key:"averageKBps",get:function(){var e=(this._now()-this._firstCheckpoint)/1e3;return this._totalBytes/e/1024}}]),e}();n.default=s},{}],28:[function(e,t,n){"use strict";function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function r(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function s(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(n,"__esModule",{value:!0});var a=function e(t,n,i){null===t&&(t=Function.prototype);var r=Object.getOwnPropertyDescriptor(t,n);if(void 0===r){var s=Object.getPrototypeOf(t);return null===s?void 0:e(s,n,i)}if("value"in r)return r.value;var a=r.get;if(void 0!==a)return a.call(i)},o=function(){function e(e,t){for(var n=0;n299)){if(this._status=h.LoaderStatus.kError,!this._onError)throw new f.RuntimeException("MozChunkedLoader: Http code invalid, "+t.status+" "+t.statusText);this._onError(h.LoaderErrors.HTTP_STATUS_CODE_INVALID,{code:t.status,msg:t.statusText})}else this._status=h.LoaderStatus.kBuffering}}},{key:"_onProgress",value:function(e){if(this._status!==h.LoaderStatus.kError){null===this._contentLength&&null!==e.total&&0!==e.total&&(this._contentLength=e.total,this._onContentLengthKnown&&this._onContentLengthKnown(this._contentLength));var t=e.target.response,n=this._range.from+this._receivedLength;this._receivedLength+=t.byteLength,this._onDataArrival&&this._onDataArrival(t,n,this._receivedLength)}}},{key:"_onLoadEnd",value:function(e){if(!0===this._requestAbort)return void(this._requestAbort=!1);this._status!==h.LoaderStatus.kError&&(this._status=h.LoaderStatus.kComplete,this._onComplete&&this._onComplete(this._range.from,this._range.from+this._receivedLength-1))}},{key:"_onXhrError",value:function(e){this._status=h.LoaderStatus.kError;var t=0,n=null;if(this._contentLength&&e.loaded=200&&t.status<=299){if(this._status=h.LoaderStatus.kBuffering,void 0!=t.responseURL){var n=this._seekHandler.removeURLParameters(t.responseURL);t.responseURL!==this._currentRequestURL&&n!==this._currentRedirectedURL&&(this._currentRedirectedURL=n,this._onURLRedirect&&this._onURLRedirect(n))}var i=t.getResponseHeader("Content-Length");if(null!=i&&null==this._contentLength){var r=parseInt(i);r>0&&(this._contentLength=r,this._onContentLengthKnown&&this._onContentLengthKnown(this._contentLength))}}else{if(this._status=h.LoaderStatus.kError,!this._onError)throw new f.RuntimeException("MSStreamLoader: Http code invalid, "+t.status+" "+t.statusText);this._onError(h.LoaderErrors.HTTP_STATUS_CODE_INVALID,{code:t.status,msg:t.statusText})}else if(3===t.readyState&&t.status>=200&&t.status<=299){this._status=h.LoaderStatus.kBuffering;var s=t.response;this._reader.readAsArrayBuffer(s)}}},{key:"_xhrOnError",value:function(e){this._status=h.LoaderStatus.kError;var t=h.LoaderErrors.EXCEPTION,n={code:-1,msg:e.constructor.name+" "+e.type};if(!this._onError)throw new f.RuntimeException(n.msg);this._onError(t,n)}},{key:"_msrOnProgress",value:function(e){var t=e.target,n=t.result;if(null==n)return void this._doReconnectIfNeeded();var i=n.slice(this._lastTimeBufferSize);this._lastTimeBufferSize=n.byteLength;var r=this._totalRange.from+this._receivedLength;this._receivedLength+=i.byteLength,this._onDataArrival&&this._onDataArrival(i,r,this._receivedLength),n.byteLength>=this._bufferLimit&&(d.default.v(this.TAG,"MSStream buffer exceeded max size near "+(r+i.byteLength)+", reconnecting..."),this._doReconnectIfNeeded())}},{key:"_doReconnectIfNeeded",value:function(){if(null==this._contentLength||this._receivedLength=this._contentLength&&(n=this._range.from+this._contentLength-1),this._currentRequestRange={from:t,to:n},this._internalOpen(this._dataSource,this._currentRequestRange)}},{key:"_internalOpen",value:function(e,t){this._lastTimeLoaded=0;var n=e.url;this._config.reuseRedirectedURL&&(void 0!=this._currentRedirectedURL?n=this._currentRedirectedURL:void 0!=e.redirectedURL&&(n=e.redirectedURL));var i=this._seekHandler.getConfig(n,t);this._currentRequestURL=i.url;var r=this._xhr=new XMLHttpRequest;if(r.open("GET",i.url,!0),r.responseType="arraybuffer",r.onreadystatechange=this._onReadyStateChange.bind(this),r.onprogress=this._onProgress.bind(this),r.onload=this._onLoad.bind(this),r.onerror=this._onXhrError.bind(this),e.withCredentials&&(r.withCredentials=!0),"object"===o(i.headers)){var s=i.headers;for(var a in s)s.hasOwnProperty(a)&&r.setRequestHeader(a,s[a])}r.send()}},{key:"abort",value:function(){this._requestAbort=!0,this._internalAbort(),this._status=_.LoaderStatus.kComplete}},{key:"_internalAbort",value:function(){this._xhr&&(this._xhr.onreadystatechange=null,this._xhr.onprogress=null,this._xhr.onload=null,this._xhr.onerror=null,this._xhr.abort(),this._xhr=null)}},{key:"_onReadyStateChange",value:function(e){var t=e.target;if(2===t.readyState){if(void 0!=t.responseURL){var n=this._seekHandler.removeURLParameters(t.responseURL);t.responseURL!==this._currentRequestURL&&n!==this._currentRedirectedURL&&(this._currentRedirectedURL=n,this._onURLRedirect&&this._onURLRedirect(n))}if(t.status>=200&&t.status<=299){if(this._waitForTotalLength)return;this._status=_.LoaderStatus.kBuffering}else{if(this._status=_.LoaderStatus.kError,!this._onError)throw new m.RuntimeException("RangeLoader: Http code invalid, "+t.status+" "+t.statusText);this._onError(_.LoaderErrors.HTTP_STATUS_CODE_INVALID,{code:t.status,msg:t.statusText})}}}},{key:"_onProgress",value:function(e){if(this._status!==_.LoaderStatus.kError){if(null===this._contentLength){var t=!1;if(this._waitForTotalLength){this._waitForTotalLength=!1,this._totalLengthReceived=!0,t=!0;var n=e.total;this._internalAbort(),null!=n&0!==n&&(this._totalLength=n)}if(-1===this._range.to?this._contentLength=this._totalLength-this._range.from:this._contentLength=this._range.to-this._range.from+1,t)return void this._openSubRange();this._onContentLengthKnown&&this._onContentLengthKnown(this._contentLength)}var i=e.loaded-this._lastTimeLoaded;this._lastTimeLoaded=e.loaded,this._speedSampler.addBytes(i)}}},{key:"_normalizeSpeed",value:function(e){var t=this._chunkSizeKBList,n=t.length-1,i=0,r=0,s=n;if(e=t[i]&&e=3&&(t=this._speedSampler.currentKBps),0!==t){var n=this._normalizeSpeed(t);this._currentSpeedNormalized!==n&&(this._currentSpeedNormalized=n,this._currentChunkSizeKB=n)}var i=e.target.response,r=this._range.from+this._receivedLength;this._receivedLength+=i.byteLength;var s=!1;null!=this._contentLength&&this._receivedLength0&&this._receivedLength0&&(this._requestSetTime=!0,this._mediaElement.currentTime=0),this._transmuxer=new p.default(this._mediaDataSource,this._config),this._transmuxer.on(g.default.INIT_SEGMENT,function(t,n){e._msectl.appendInitSegment(n)}),this._transmuxer.on(g.default.MEDIA_SEGMENT,function(t,n){if(e._msectl.appendMediaSegment(n),e._config.lazyLoad&&!e._config.isLive){var i=e._mediaElement.currentTime;n.info.endDts>=1e3*(i+e._config.lazyLoadMaxDuration)&&null==e._progressChecker&&(d.default.v(e.TAG,"Maximum buffering duration exceeded, suspend transmuxing task"),e._suspendTransmuxer())}}),this._transmuxer.on(g.default.LOADING_COMPLETE,function(){e._msectl.endOfStream(),e._emitter.emit(_.default.LOADING_COMPLETE)}), +this._transmuxer.on(g.default.RECOVERED_EARLY_EOF,function(){e._emitter.emit(_.default.RECOVERED_EARLY_EOF)}),this._transmuxer.on(g.default.IO_ERROR,function(t,n){e._emitter.emit(_.default.ERROR,k.ErrorTypes.NETWORK_ERROR,t,n)}),this._transmuxer.on(g.default.DEMUX_ERROR,function(t,n){e._emitter.emit(_.default.ERROR,k.ErrorTypes.MEDIA_ERROR,t,{code:-1,msg:n})}),this._transmuxer.on(g.default.MEDIA_INFO,function(t){e._mediaInfo=t,e._emitter.emit(_.default.MEDIA_INFO,Object.assign({},t))}),this._transmuxer.on(g.default.STATISTICS_INFO,function(t){e._statisticsInfo=e._fillStatisticsInfo(t),e._emitter.emit(_.default.STATISTICS_INFO,Object.assign({},e._statisticsInfo))}),this._transmuxer.on(g.default.RECOMMEND_SEEKPOINT,function(t){e._mediaElement&&!e._config.accurateSeek&&(e._requestSetTime=!0,e._mediaElement.currentTime=t/1e3)}),this._transmuxer.open()}}},{key:"unload",value:function(){this._mediaElement&&this._mediaElement.pause(),this._msectl&&this._msectl.seek(0),this._transmuxer&&(this._transmuxer.close(),this._transmuxer.destroy(),this._transmuxer=null)}},{key:"play",value:function(){return this._mediaElement.play()}},{key:"pause",value:function(){this._mediaElement.pause()}},{key:"_fillStatisticsInfo",value:function(e){if(e.playerType=this._type,!(this._mediaElement instanceof HTMLVideoElement))return e;var t=!0,n=0,i=0;if(this._mediaElement.getVideoPlaybackQuality){var r=this._mediaElement.getVideoPlaybackQuality();n=r.totalVideoFrames,i=r.droppedVideoFrames}else void 0!=this._mediaElement.webkitDecodedFrameCount?(n=this._mediaElement.webkitDecodedFrameCount,i=this._mediaElement.webkitDroppedFrameCount):t=!1;return t&&(e.decodedFrames=n,e.droppedFrames=i),e}},{key:"_onmseUpdateEnd",value:function(){if(this._config.lazyLoad&&!this._config.isLive){for(var e=this._mediaElement.buffered,t=this._mediaElement.currentTime,n=0,i=0;i=t+this._config.lazyLoadMaxDuration&&null==this._progressChecker&&(d.default.v(this.TAG,"Maximum buffering duration exceeded, suspend transmuxing task"),this._suspendTransmuxer())}}},{key:"_onmseBufferFull",value:function(){d.default.v(this.TAG,"MSE SourceBuffer is full, suspend transmuxing task"),null==this._progressChecker&&this._suspendTransmuxer()}},{key:"_suspendTransmuxer",value:function(){this._transmuxer&&(this._transmuxer.pause(),null==this._progressChecker&&(this._progressChecker=window.setInterval(this._checkProgressAndResume.bind(this),1e3)))}},{key:"_checkProgressAndResume",value:function(){for(var e=this._mediaElement.currentTime,t=this._mediaElement.buffered,n=!1,i=0;i=r&&e=s-this._config.lazyLoadRecoverDuration&&(n=!0);break}}n&&(window.clearInterval(this._progressChecker),this._progressChecker=null,n&&(d.default.v(this.TAG,"Continue loading from paused position"),this._transmuxer.resume()))}},{key:"_isTimepointBuffered",value:function(e){for(var t=this._mediaElement.buffered,n=0;n=i&&e0){var r=this._mediaElement.buffered.start(0);(r<1&&e0&&t.currentTime0){var i=n.start(0);if(i<1&&t0&&(this._mediaElement.currentTime=0),this._mediaElement.preload="auto",this._mediaElement.load(),this._statisticsReporter=window.setInterval(this._reportStatisticsInfo.bind(this),this._config.statisticsInfoReportInterval)}},{key:"unload",value:function(){this._mediaElement&&(this._mediaElement.src="",this._mediaElement.removeAttribute("src")),null!=this._statisticsReporter&&(window.clearInterval(this._statisticsReporter),this._statisticsReporter=null)}},{key:"play",value:function(){return this._mediaElement.play()}},{key:"pause",value:function(){this._mediaElement.pause()}},{key:"_onvLoadedMetadata",value:function(e){null!=this._pendingSeekTime&&(this._mediaElement.currentTime=this._pendingSeekTime,this._pendingSeekTime=null),this._emitter.emit(d.default.MEDIA_INFO,this.mediaInfo)}},{key:"_reportStatisticsInfo",value:function(){this._emitter.emit(d.default.STATISTICS_INFO,this.statisticsInfo)}},{key:"type",get:function(){return this._type}},{key:"buffered",get:function(){return this._mediaElement.buffered}},{key:"duration",get:function(){return this._mediaElement.duration}},{key:"volume",get:function(){return this._mediaElement.volume},set:function(e){this._mediaElement.volume=e}},{key:"muted",get:function(){return this._mediaElement.muted},set:function(e){this._mediaElement.muted=e}},{key:"currentTime",get:function(){return this._mediaElement?this._mediaElement.currentTime:0},set:function(e){this._mediaElement?this._mediaElement.currentTime=e:this._pendingSeekTime=e}},{key:"mediaInfo",get:function(){var e=this._mediaElement instanceof HTMLAudioElement?"audio/":"video/",t={mimeType:e+this._mediaDataSource.type};return this._mediaElement&&(t.duration=Math.floor(1e3*this._mediaElement.duration),this._mediaElement instanceof HTMLVideoElement&&(t.width=this._mediaElement.videoWidth,t.height=this._mediaElement.videoHeight)),t}},{key:"statisticsInfo",get:function(){var e={playerType:this._type,url:this._mediaDataSource.url};if(!(this._mediaElement instanceof HTMLVideoElement))return e;var t=!0,n=0,i=0;if(this._mediaElement.getVideoPlaybackQuality){var r=this._mediaElement.getVideoPlaybackQuality();n=r.totalVideoFrames,i=r.droppedVideoFrames}else void 0!=this._mediaElement.webkitDecodedFrameCount?(n=this._mediaElement.webkitDecodedFrameCount,i=this._mediaElement.webkitDroppedFrameCount):t=!1;return t&&(e.decodedFrames=n,e.droppedFrames=i),e}}]),e}();n.default=c},{"../config.js":5,"../utils/exception.js":40,"./player-events.js":35,events:2}],34:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.ErrorDetails=n.ErrorTypes=void 0;var i=e("../io/loader.js"),r=e("../demux/demux-errors.js"),s=function(e){return e&&e.__esModule?e:{default:e}}(r);n.ErrorTypes={NETWORK_ERROR:"NetworkError",MEDIA_ERROR:"MediaError",OTHER_ERROR:"OtherError"},n.ErrorDetails={NETWORK_EXCEPTION:i.LoaderErrors.EXCEPTION,NETWORK_STATUS_CODE_INVALID:i.LoaderErrors.HTTP_STATUS_CODE_INVALID,NETWORK_TIMEOUT:i.LoaderErrors.CONNECTING_TIMEOUT,NETWORK_UNRECOVERABLE_EARLY_EOF:i.LoaderErrors.UNRECOVERABLE_EARLY_EOF,MEDIA_MSE_ERROR:"MediaMSEError",MEDIA_FORMAT_ERROR:s.default.FORMAT_ERROR,MEDIA_FORMAT_UNSUPPORTED:s.default.FORMAT_UNSUPPORTED,MEDIA_CODEC_UNSUPPORTED:s.default.CODEC_UNSUPPORTED}},{"../demux/demux-errors.js":16,"../io/loader.js":24}],35:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var i={ERROR:"error",LOADING_COMPLETE:"loading_complete",RECOVERED_EARLY_EOF:"recovered_early_eof",MEDIA_INFO:"media_info",STATISTICS_INFO:"statistics_info"};n.default=i},{}],36:[function(e,t,n){"use strict";function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var r=function(){function e(e,t){for(var n=0;n>>24&255,n[1]=t>>>16&255,n[2]=t>>>8&255,n[3]=255&t,n.set(e,4);for(var a=8,o=0;o>>24&255,t>>>16&255,t>>>8&255,255&t,n>>>24&255,n>>>16&255,n>>>8&255,255&n,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255]))}},{key:"trak",value:function(t){return e.box(e.types.trak,e.tkhd(t),e.mdia(t))}},{key:"tkhd",value:function(t){var n=t.id,i=t.duration,r=t.presentWidth,s=t.presentHeight;return e.box(e.types.tkhd,new Uint8Array([0,0,0,7,0,0,0,0,0,0,0,0,n>>>24&255,n>>>16&255,n>>>8&255,255&n,0,0,0,0,i>>>24&255,i>>>16&255,i>>>8&255,255&i,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,r>>>8&255,255&r,0,0,s>>>8&255,255&s,0,0]))}},{key:"mdia",value:function(t){return e.box(e.types.mdia,e.mdhd(t),e.hdlr(t),e.minf(t))}},{key:"mdhd",value:function(t){var n=t.timescale,i=t.duration;return e.box(e.types.mdhd,new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,n>>>24&255,n>>>16&255,n>>>8&255,255&n,i>>>24&255,i>>>16&255,i>>>8&255,255&i,85,196,0,0]))}},{key:"hdlr",value:function(t){var n=null;return n="audio"===t.type?e.constants.HDLR_AUDIO:e.constants.HDLR_VIDEO,e.box(e.types.hdlr,n)}},{key:"minf",value:function(t){var n=null;return n="audio"===t.type?e.box(e.types.smhd,e.constants.SMHD):e.box(e.types.vmhd,e.constants.VMHD),e.box(e.types.minf,n,e.dinf(),e.stbl(t))}},{key:"dinf",value:function(){return e.box(e.types.dinf,e.box(e.types.dref,e.constants.DREF))}},{key:"stbl",value:function(t){return e.box(e.types.stbl,e.stsd(t),e.box(e.types.stts,e.constants.STTS),e.box(e.types.stsc,e.constants.STSC),e.box(e.types.stsz,e.constants.STSZ),e.box(e.types.stco,e.constants.STCO))}},{key:"stsd",value:function(t){return"audio"===t.type?"mp3"===t.codec?e.box(e.types.stsd,e.constants.STSD_PREFIX,e.mp3(t)):e.box(e.types.stsd,e.constants.STSD_PREFIX,e.mp4a(t)):e.box(e.types.stsd,e.constants.STSD_PREFIX,e.avc1(t))}},{key:"mp3",value:function(t){var n=t.channelCount,i=t.audioSampleRate,r=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,n,0,16,0,0,0,0,i>>>8&255,255&i,0,0]);return e.box(e.types[".mp3"],r)}},{key:"mp4a",value:function(t){var n=t.channelCount,i=t.audioSampleRate,r=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,n,0,16,0,0,0,0,i>>>8&255,255&i,0,0]);return e.box(e.types.mp4a,r,e.esds(t))}},{key:"esds",value:function(t){var n=t.config||[],i=n.length,r=new Uint8Array([0,0,0,0,3,23+i,0,1,0,4,15+i,64,21,0,0,0,0,0,0,0,0,0,0,0,5].concat([i]).concat(n).concat([6,1,2]));return e.box(e.types.esds,r)}},{key:"avc1",value:function(t){var n=t.avcc,i=t.codecWidth,r=t.codecHeight,s=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,i>>>8&255,255&i,r>>>8&255,255&r,0,72,0,0,0,72,0,0,0,0,0,0,0,1,10,120,113,113,47,102,108,118,46,106,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,255,255]);return e.box(e.types.avc1,s,e.box(e.types.avcC,n))}},{key:"mvex",value:function(t){return e.box(e.types.mvex,e.trex(t))}},{key:"trex",value:function(t){var n=t.id,i=new Uint8Array([0,0,0,0,n>>>24&255,n>>>16&255,n>>>8&255,255&n,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1]);return e.box(e.types.trex,i)}},{key:"moof",value:function(t,n){return e.box(e.types.moof,e.mfhd(t.sequenceNumber),e.traf(t,n))}},{key:"mfhd",value:function(t){var n=new Uint8Array([0,0,0,0,t>>>24&255,t>>>16&255,t>>>8&255,255&t]);return e.box(e.types.mfhd,n)}},{key:"traf",value:function(t,n){var i=t.id,r=e.box(e.types.tfhd,new Uint8Array([0,0,0,0,i>>>24&255,i>>>16&255,i>>>8&255,255&i])),s=e.box(e.types.tfdt,new Uint8Array([0,0,0,0,n>>>24&255,n>>>16&255,n>>>8&255,255&n])),a=e.sdtp(t),o=e.trun(t,a.byteLength+16+16+8+16+8+8);return e.box(e.types.traf,r,s,o,a)}},{key:"sdtp",value:function(t){for(var n=t.samples||[],i=n.length,r=new Uint8Array(4+i),s=0;s>>24&255,r>>>16&255,r>>>8&255,255&r,n>>>24&255,n>>>16&255,n>>>8&255,255&n],0);for(var o=0;o>>24&255,u>>>16&255,u>>>8&255,255&u,l>>>24&255,l>>>16&255,l>>>8&255,255&l,d.isLeading<<2|d.dependsOn,d.isDependedOn<<6|d.hasRedundancy<<4|d.isNonSync,0,0,h>>>24&255,h>>>16&255,h>>>8&255,255&h],12+16*o)}return e.box(e.types.trun,a)}},{key:"mdat",value:function(t){return e.box(e.types.mdat,t)}}]),e}();s.init(),n.default=s},{}],38:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var s=function(){function e(e,t){for(var n=0;n1&&(y=i.pop(),g-=y.length),null!=this._audioStashedLastSample){var E=this._audioStashedLastSample;this._audioStashedLastSample=null,i.unshift(E),g+=E.length}null!=y&&(this._audioStashedLastSample=y);var b=i[0].dts-this._dtsBase;if(this._audioNextDts)r=b-this._audioNextDts;else if(this._audioSegmentInfoList.isEmpty())r=0,this._fillSilentAfterSeek&&!this._videoSegmentInfoList.isEmpty()&&"mp3"!==this._audioMeta.originalCodec&&(m=!0);else{var S=this._audioSegmentInfoList.getLastSampleBefore(b);if(null!=S){var k=b-(S.originalDts+S.duration);k<=3&&(k=0);var L=S.dts+S.duration+k;r=b-L}else r=0}if(m){var w=b-r,R=this._videoSegmentInfoList.getLastSegmentBefore(b);if(null!=R&&R.beginDts=1?C[C.length-1].duration:Math.floor(u);var U=!1,N=null;if(j>1.5*u&&"mp3"!==this._audioMeta.codec&&this._fillAudioTimestampGap&&!c.default.safari){U=!0;var F=Math.abs(j-u),G=Math.ceil(F/u),V=B+u;o.default.w(this.TAG,"Large audio timestamp gap detected, may cause AV sync to drift. Silent frames will be generated to avoid unsync.\ndts: "+(B+j)+" ms, expected: "+(B+Math.round(u))+" ms, delta: "+Math.round(F)+" ms, generate: "+G+" frames");var z=h.default.getSilentFrame(this._audioMeta.originalCodec,this._audioMeta.channelCount);null==z&&(o.default.w(this.TAG,"Unable to generate silent frame for "+this._audioMeta.originalCodec+" with "+this._audioMeta.channelCount+" channels, repeat last frame"),z=M),N=[];for(var H=0;H0){var q=N[N.length-1];q.duration=K-q.dts}var W={dts:K,pts:K,cts:0,unit:z,size:z.byteLength,duration:0,originalDts:D,flags:{isLeading:0,dependsOn:1,isDependedOn:0,hasRedundancy:0}};N.push(W),g+=M.byteLength,V+=u}var X=N[N.length-1];X.duration=B+j-X.dts,j=Math.round(u)}C.push({dts:B,pts:B,cts:0,unit:x.unit,size:x.unit.byteLength,duration:j,originalDts:D,flags:{isLeading:0,dependsOn:1,isDependedOn:0,hasRedundancy:0}}),U&&C.push.apply(C,N)}d?v=new Uint8Array(g):(v=new Uint8Array(g),v[0]=g>>>24&255,v[1]=g>>>16&255,v[2]=g>>>8&255,v[3]=255&g,v.set(l.default.types.mdat,4));for(var Y=0;Y1&&(c=i.pop(),f-=c.length),null!=this._videoStashedLastSample){var m=this._videoStashedLastSample;this._videoStashedLastSample=null,i.unshift(m),f+=m.length}null!=c&&(this._videoStashedLastSample=c);var p=i[0].dts-this._dtsBase;if(this._videoNextDts)r=p-this._videoNextDts;else if(this._videoSegmentInfoList.isEmpty())r=0;else{var v=this._videoSegmentInfoList.getLastSampleBefore(p);if(null!=v){var g=p-(v.originalDts+v.duration);g<=3&&(g=0);var y=v.dts+v.duration+g;r=p-y}else r=0}for(var E=new _.MediaSegmentInfo,b=[],S=0;S=1?b[b.length-1].duration:Math.floor(this._videoMeta.refSampleDuration);if(w){var I=new _.SampleInfo(R,O,T,k.dts,!0);I.fileposition=k.fileposition,E.appendSyncPoint(I)}b.push({dts:R,pts:O,cts:A,units:k.units,size:k.length,isKeyframe:w,duration:T,originalDts:L,flags:{isLeading:0,dependsOn:w?2:1,isDependedOn:w?1:0,hasRedundancy:0,isNonSync:w?0:1}})}h=new Uint8Array(f),h[0]=f>>>24&255,h[1]=f>>>16&255,h[2]=f>>>8&255,h[3]=255&f,h.set(l.default.types.mdat,4);for(var x=0;x=0&&/(rv)(?::| )([\w.]+)/.exec(e)||e.indexOf("compatible")<0&&/(firefox)[ \/]([\w.]+)/.exec(e)||[],n=/(ipad)/.exec(e)||/(ipod)/.exec(e)||/(windows phone)/.exec(e)||/(iphone)/.exec(e)||/(kindle)/.exec(e)||/(android)/.exec(e)||/(windows)/.exec(e)||/(mac)/.exec(e)||/(linux)/.exec(e)||/(cros)/.exec(e)||[],r={browser:t[5]||t[3]||t[1]||"",version:t[2]||t[4]||"0",majorVersion:t[4]||t[2]||"0",platform:n[0]||""},s={};if(r.browser){s[r.browser]=!0;var a=r.majorVersion.split(".");s.version={major:parseInt(r.majorVersion,10),string:r.version},a.length>1&&(s.version.minor=parseInt(a[1],10)),a.length>2&&(s.version.build=parseInt(a[2],10))}r.platform&&(s[r.platform]=!0),(s.chrome||s.opr||s.safari)&&(s.webkit=!0),(s.rv||s.iemobile)&&(s.rv&&delete s.rv,r.browser="msie",s.msie=!0),s.edge&&(delete s.edge,r.browser="msedge",s.msedge=!0),s.opr&&(r.browser="opera",s.opera=!0),s.safari&&s.android&&(r.browser="android",s.android=!0),s.name=r.browser,s.platform=r.platform;for(var o in i)i.hasOwnProperty(o)&&delete i[o];Object.assign(i,s)}(),n.default=i},{}],40:[function(e,t,n){"use strict";function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function r(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function s(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var a=function(){function e(e,t){for(var n=0;n "+n;e.ENABLE_CALLBACK&&e.emitter.emit("log","error",i),e.ENABLE_ERROR&&(console.error?console.error(i):console.warn?console.warn(i):console.log(i))}},{key:"i",value:function(t,n){t&&!e.FORCE_GLOBAL_TAG||(t=e.GLOBAL_TAG);var i="["+t+"] > "+n;e.ENABLE_CALLBACK&&e.emitter.emit("log","info",i),e.ENABLE_INFO&&(console.info?console.info(i):console.log(i))}},{key:"w",value:function(t,n){t&&!e.FORCE_GLOBAL_TAG||(t=e.GLOBAL_TAG);var i="["+t+"] > "+n;e.ENABLE_CALLBACK&&e.emitter.emit("log","warn",i),e.ENABLE_WARN&&(console.warn?console.warn(i):console.log(i))}},{key:"d",value:function(t,n){t&&!e.FORCE_GLOBAL_TAG||(t=e.GLOBAL_TAG);var i="["+t+"] > "+n;e.ENABLE_CALLBACK&&e.emitter.emit("log","debug",i),e.ENABLE_DEBUG&&(console.debug?console.debug(i):console.log(i))}},{key:"v",value:function(t,n){t&&!e.FORCE_GLOBAL_TAG||(t=e.GLOBAL_TAG);var i="["+t+"] > "+n;e.ENABLE_CALLBACK&&e.emitter.emit("log","verbose",i),e.ENABLE_VERBOSE&&console.log(i)}}]),e}();o.GLOBAL_TAG="flv.js",o.FORCE_GLOBAL_TAG=!1,o.ENABLE_ERROR=!0,o.ENABLE_INFO=!0,o.ENABLE_WARN=!0,o.ENABLE_DEBUG=!0,o.ENABLE_VERBOSE=!0,o.ENABLE_CALLBACK=!1,o.emitter=new a.default,n.default=o},{events:2}],42:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var s=function(){function e(e,t){for(var n=0;n0){var n=e.getConfig();t.emit("change",n)}}},{key:"registerListener",value:function(t){e.emitter.addListener("change",t)}},{key:"removeListener",value:function(t){e.emitter.removeListener("change",t)}},{key:"addLogListener",value:function(t){l.default.emitter.addListener("log",t),l.default.emitter.listenerCount("log")>0&&(l.default.ENABLE_CALLBACK=!0,e._notifyChange())}},{key:"removeLogListener",value:function(t){l.default.emitter.removeListener("log",t),0===l.default.emitter.listenerCount("log")&&(l.default.ENABLE_CALLBACK=!1,e._notifyChange())}},{key:"forceGlobalTag",get:function(){return l.default.FORCE_GLOBAL_TAG},set:function(t){l.default.FORCE_GLOBAL_TAG=t,e._notifyChange()}},{key:"globalTag",get:function(){return l.default.GLOBAL_TAG},set:function(t){l.default.GLOBAL_TAG=t,e._notifyChange()}},{key:"enableAll",get:function(){return l.default.ENABLE_VERBOSE&&l.default.ENABLE_DEBUG&&l.default.ENABLE_INFO&&l.default.ENABLE_WARN&&l.default.ENABLE_ERROR},set:function(t){l.default.ENABLE_VERBOSE=t,l.default.ENABLE_DEBUG=t,l.default.ENABLE_INFO=t,l.default.ENABLE_WARN=t,l.default.ENABLE_ERROR=t,e._notifyChange()}},{key:"enableDebug",get:function(){return l.default.ENABLE_DEBUG},set:function(t){l.default.ENABLE_DEBUG=t,e._notifyChange()}},{key:"enableVerbose",get:function(){return l.default.ENABLE_VERBOSE},set:function(t){l.default.ENABLE_VERBOSE=t,e._notifyChange()}},{key:"enableInfo",get:function(){return l.default.ENABLE_INFO},set:function(t){l.default.ENABLE_INFO=t,e._notifyChange()}},{key:"enableWarn",get:function(){return l.default.ENABLE_WARN},set:function(t){l.default.ENABLE_WARN=t,e._notifyChange()}},{key:"enableError",get:function(){return l.default.ENABLE_ERROR},set:function(t){l.default.ENABLE_ERROR=t,e._notifyChange()}}]),e}();d.emitter=new o.default,n.default=d},{"./logger.js":41,events:2}],43:[function(e,t,n){"use strict";function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var r=function(){function e(e,t){for(var n=0;n=128){t.push(String.fromCharCode(65535&a)),r+=2;continue}}}else if(n[r]<240){if(i(n,r,2)){var o=(15&n[r])<<12|(63&n[r+1])<<6|63&n[r+2];if(o>=2048&&55296!=(63488&o)){t.push(String.fromCharCode(65535&o)),r+=3;continue}}}else if(n[r]<248&&i(n,r,3)){var u=(7&n[r])<<18|(63&n[r+1])<<12|(63&n[r+2])<<6|63&n[r+3];if(u>65536&&u<1114112){u-=65536,t.push(String.fromCharCode(u>>>10|55296)),t.push(String.fromCharCode(1023&u|56320)),r+=4;continue}}t.push(String.fromCharCode(65533)),++r}return t.join("")}Object.defineProperty(n,"__esModule",{value:!0}),n.default=r},{}]},{},[21])(21)}); +//# sourceMappingURL=flv.min.js.map diff --git a/src/bower_components/emby-webcomponents/focusmanager.js b/src/bower_components/emby-webcomponents/focusmanager.js index c80ca2e3a3..5977dc0715 100644 --- a/src/bower_components/emby-webcomponents/focusmanager.js +++ b/src/bower_components/emby-webcomponents/focusmanager.js @@ -1,249 +1,544 @@ -define(["dom"], function(dom) { - "use strict"; +define(['dom'], function (dom) { + 'use strict'; + var scopes = []; function pushScope(elem) { - scopes.push(elem) + scopes.push(elem); } function popScope(elem) { - scopes.length && (scopes.length -= 1) - } - function autoFocus(view, defaultToFirst, findAutoFocusElement) { - var element; - return !1 !== findAutoFocusElement && (element = view.querySelector("*[autofocus]")) ? (focus(element), element) : !1 !== defaultToFirst && (element = getFocusableElements(view, 1, "noautofocus")[0]) ? (focus(element), element) : null - } - - function focus(element) { - try { - element.focus({ - preventScroll: !0 - }) - } catch (err) { - console.log("Error in focusManager.autoFocus: " + err) + if (scopes.length) { + scopes.length -= 1; } } + function autoFocus(view, defaultToFirst, findAutoFocusElement) { + + var element; + if (findAutoFocusElement !== false) { + element = view.querySelector('*[autofocus]'); + if (element) { + focus(element); + return element; + } + } + + if (defaultToFirst !== false) { + element = getFocusableElements(view, 1, 'noautofocus')[0]; + + if (element) { + focus(element); + return element; + } + } + + return null; + } + + function focus(element) { + + try { + element.focus({ + preventScroll: true + }); + } catch (err) { + console.log('Error in focusManager.autoFocus: ' + err); + } + } + + var focusableTagNames = ['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON', 'A']; + var focusableContainerTagNames = ['BODY', 'DIALOG']; + var focusableQuery = focusableTagNames.map(function (t) { + + if (t === 'INPUT') { + t += ':not([type="range"]):not([type="file"])'; + } + return t + ':not([tabindex="-1"]):not(:disabled)'; + + }).join(',') + ',.focusable'; + function isFocusable(elem) { - return -1 !== focusableTagNames.indexOf(elem.tagName) || !(!elem.classList || !elem.classList.contains("focusable")) + + if (focusableTagNames.indexOf(elem.tagName) !== -1) { + return true; + } + + if (elem.classList && elem.classList.contains('focusable')) { + return true; + } + + return false; } function normalizeFocusable(elem, originalElement) { if (elem) { var tagName = elem.tagName; - tagName && "HTML" !== tagName && "BODY" !== tagName || (elem = originalElement) + if (!tagName || tagName === 'HTML' || tagName === 'BODY') { + elem = originalElement; + } } - return elem + + return elem; } function focusableParent(elem) { - for (var originalElement = elem; !isFocusable(elem);) { + + var originalElement = elem; + + while (!isFocusable(elem)) { var parent = elem.parentNode; - if (!parent) return normalizeFocusable(elem, originalElement); - elem = parent + + if (!parent) { + return normalizeFocusable(elem, originalElement); + } + + elem = parent; } - return normalizeFocusable(elem, originalElement) + + return normalizeFocusable(elem, originalElement); } + // Determines if a focusable element can be focused at a given point in time function isCurrentlyFocusableInternal(elem) { - return null !== elem.offsetParent + + // http://stackoverflow.com/questions/19669786/check-if-element-is-visible-in-dom + if (elem.offsetParent === null) { + return false; + } + + return true; } + // Determines if a focusable element can be focused at a given point in time function isCurrentlyFocusable(elem) { - if (elem.disabled) return !1; - if ("-1" === elem.getAttribute("tabindex")) return !1; - if ("INPUT" === elem.tagName) { - var type = elem.type; - if ("range" === type) return !1; - if ("file" === type) return !1 + + if (elem.disabled) { + return false; } - return isCurrentlyFocusableInternal(elem) + + if (elem.getAttribute('tabindex') === "-1") { + return false; + } + + if (elem.tagName === 'INPUT') { + var type = elem.type; + if (type === 'range') { + return false; + } + if (type === 'file') { + return false; + } + } + + return isCurrentlyFocusableInternal(elem); } function getDefaultScope() { - return scopes[0] || document.body + return scopes[0] || document.body; } function getFocusableElements(parent, limit, excludeClass) { - for (var elems = (parent || getDefaultScope()).querySelectorAll(focusableQuery), focusableElements = [], i = 0, length = elems.length; i < length; i++) { + var elems = (parent || getDefaultScope()).querySelectorAll(focusableQuery); + var focusableElements = []; + + for (var i = 0, length = elems.length; i < length; i++) { + var elem = elems[i]; - if ((!excludeClass || !elem.classList.contains(excludeClass)) && (isCurrentlyFocusableInternal(elem) && (focusableElements.push(elem), limit && focusableElements.length >= limit))) break + + if (excludeClass && elem.classList.contains(excludeClass)) { + continue; + } + + if (isCurrentlyFocusableInternal(elem)) { + focusableElements.push(elem); + + if (limit && focusableElements.length >= limit) { + break; + } + } } - return focusableElements + + return focusableElements; } function isFocusContainer(elem, direction) { - if (-1 !== focusableContainerTagNames.indexOf(elem.tagName)) return !0; - var classList = elem.classList; - if (classList.contains("focuscontainer")) return !0; - if (0 === direction) { - if (classList.contains("focuscontainer-x")) return !0; - if (classList.contains("focuscontainer-left")) return !0 - } else if (1 === direction) { - if (classList.contains("focuscontainer-x")) return !0; - if (classList.contains("focuscontainer-right")) return !0 - } else if (2 === direction) { - if (classList.contains("focuscontainer-y")) return !0 - } else if (3 === direction) { - if (classList.contains("focuscontainer-y")) return !0; - if (classList.contains("focuscontainer-down")) return !0 + + if (focusableContainerTagNames.indexOf(elem.tagName) !== -1) { + return true; } - return !1 + + var classList = elem.classList; + + if (classList.contains('focuscontainer')) { + return true; + } + + if (direction === 0) { + if (classList.contains('focuscontainer-x')) { + return true; + } + if (classList.contains('focuscontainer-left')) { + return true; + } + } + else if (direction === 1) { + if (classList.contains('focuscontainer-x')) { + return true; + } + if (classList.contains('focuscontainer-right')) { + return true; + } + } + else if (direction === 2) { + if (classList.contains('focuscontainer-y')) { + return true; + } + } + else if (direction === 3) { + if (classList.contains('focuscontainer-y')) { + return true; + } + if (classList.contains('focuscontainer-down')) { + return true; + } + } + + return false; } function getFocusContainer(elem, direction) { - for (; !isFocusContainer(elem, direction);) - if (!(elem = elem.parentNode)) return getDefaultScope(); - return elem + while (!isFocusContainer(elem, direction)) { + elem = elem.parentNode; + + if (!elem) { + return getDefaultScope(); + } + } + + return elem; } function getOffset(elem) { + var box; - if (box = elem.getBoundingClientRect ? elem.getBoundingClientRect() : { + + // Support: BlackBerry 5, iOS 3 (original iPhone) + // If we don't have gBCR, just use 0,0 rather than error + if (elem.getBoundingClientRect) { + box = elem.getBoundingClientRect(); + } else { + box = { top: 0, left: 0, width: 0, height: 0 - }, null === box.right) { - box = { + }; + } + + if (box.right === null) { + + // Create a new object because some browsers will throw an error when trying to set data onto the Rect object + var newBox = { top: box.top, left: box.left, width: box.width, height: box.height - }, box.right = box.left + box.width, box.bottom = box.top + box.height + }; + + box = newBox; + + box.right = box.left + box.width; + box.bottom = box.top + box.height; } - return box + + return box; } function nav(activeElement, direction, container, focusableElements) { - if (activeElement = activeElement || document.activeElement, activeElement && (activeElement = focusableParent(activeElement)), container = container || (activeElement ? getFocusContainer(activeElement, direction) : getDefaultScope()), !activeElement) return void autoFocus(container, !0, !1); - for (var nearestElement, focusableContainer = dom.parentWithClass(activeElement, "focusable"), rect = getOffset(activeElement), point1x = parseFloat(rect.left) || 0, point1y = parseFloat(rect.top) || 0, point2x = parseFloat(point1x + rect.width - 1) || point1x, point2y = parseFloat(point1y + rect.height - 1) || point1y, sourceMidX = (Math.min, Math.max, rect.left + rect.width / 2), sourceMidY = rect.top + rect.height / 2, focusable = focusableElements || container.querySelectorAll(focusableQuery), minDistance = 1 / 0, i = 0, length = focusable.length; i < length; i++) { + + activeElement = activeElement || document.activeElement; + + if (activeElement) { + activeElement = focusableParent(activeElement); + } + + container = container || (activeElement ? getFocusContainer(activeElement, direction) : getDefaultScope()); + + if (!activeElement) { + autoFocus(container, true, false); + return; + } + + var focusableContainer = dom.parentWithClass(activeElement, 'focusable'); + + var rect = getOffset(activeElement); + + // Get elements and work out x/y points + var cache = [], + point1x = parseFloat(rect.left) || 0, + point1y = parseFloat(rect.top) || 0, + point2x = parseFloat(point1x + rect.width - 1) || point1x, + point2y = parseFloat(point1y + rect.height - 1) || point1y, + // Shortcuts to help with compression + min = Math.min, + max = Math.max; + + var sourceMidX = rect.left + (rect.width / 2); + var sourceMidY = rect.top + (rect.height / 2); + + var focusable = focusableElements || container.querySelectorAll(focusableQuery); + + var maxDistance = Infinity; + var minDistance = maxDistance; + var nearestElement; + + for (var i = 0, length = focusable.length; i < length; i++) { var curr = focusable[i]; - if (curr !== activeElement && curr !== focusableContainer) { - var elementRect = getOffset(curr); - if (elementRect.width || elementRect.height) { - switch (direction) { - case 0: - if (elementRect.left >= rect.left) continue; - if (elementRect.right === rect.right) continue; - break; - case 1: - if (elementRect.right <= rect.right) continue; - if (elementRect.left === rect.left) continue; - break; - case 2: - if (elementRect.top >= rect.top) continue; - if (elementRect.bottom >= rect.bottom) continue; - break; - case 3: - if (elementRect.bottom <= rect.bottom) continue; - if (elementRect.top <= rect.top) continue + + if (curr === activeElement) { + continue; + } + // Don't refocus into the same container + if (curr === focusableContainer) { + continue; + } + + //if (!isCurrentlyFocusableInternal(curr)) { + // continue; + //} + + var elementRect = getOffset(curr); + + // not currently visible + if (!elementRect.width && !elementRect.height) { + continue; + } + + switch (direction) { + + case 0: + // left + if (elementRect.left >= rect.left) { + continue; } - var distX, distY, x = elementRect.left, - y = elementRect.top, - x2 = x + elementRect.width - 1, - y2 = y + elementRect.height - 1, - intersectX = intersects(point1x, point2x, x, x2), - intersectY = intersects(point1y, point2y, y, y2), - midX = elementRect.left + elementRect.width / 2, - midY = elementRect.top + elementRect.height / 2; - switch (direction) { - case 0: - distX = Math.abs(point1x - Math.min(point1x, x2)), distY = intersectY ? 0 : Math.abs(sourceMidY - midY); - break; - case 1: - distX = Math.abs(point2x - Math.max(point2x, x)), distY = intersectY ? 0 : Math.abs(sourceMidY - midY); - break; - case 2: - distY = Math.abs(point1y - Math.min(point1y, y2)), distX = intersectX ? 0 : Math.abs(sourceMidX - midX); - break; - case 3: - distY = Math.abs(point2y - Math.max(point2y, y)), distX = intersectX ? 0 : Math.abs(sourceMidX - midX) + if (elementRect.right === rect.right) { + continue; } - var dist = Math.sqrt(distX * distX + distY * distY); - dist < minDistance && (nearestElement = curr, minDistance = dist) - } + break; + case 1: + // right + if (elementRect.right <= rect.right) { + continue; + } + if (elementRect.left === rect.left) { + continue; + } + break; + case 2: + // up + if (elementRect.top >= rect.top) { + continue; + } + if (elementRect.bottom >= rect.bottom) { + continue; + } + break; + case 3: + // down + if (elementRect.bottom <= rect.bottom) { + continue; + } + if (elementRect.top <= rect.top) { + continue; + } + break; + default: + break; + } + + var x = elementRect.left, + y = elementRect.top, + x2 = x + elementRect.width - 1, + y2 = y + elementRect.height - 1; + + var intersectX = intersects(point1x, point2x, x, x2); + var intersectY = intersects(point1y, point2y, y, y2); + + var midX = elementRect.left + (elementRect.width / 2); + var midY = elementRect.top + (elementRect.height / 2); + + var distX; + var distY; + + switch (direction) { + + case 0: + // left + distX = Math.abs(point1x - Math.min(point1x, x2)); + distY = intersectY ? 0 : Math.abs(sourceMidY - midY); + break; + case 1: + // right + distX = Math.abs(point2x - Math.max(point2x, x)); + distY = intersectY ? 0 : Math.abs(sourceMidY - midY); + break; + case 2: + // up + distY = Math.abs(point1y - Math.min(point1y, y2)); + distX = intersectX ? 0 : Math.abs(sourceMidX - midX); + break; + case 3: + // down + distY = Math.abs(point2y - Math.max(point2y, y)); + distX = intersectX ? 0 : Math.abs(sourceMidX - midX); + break; + default: + break; + } + + var dist = Math.sqrt(distX * distX + distY * distY); + + if (dist < minDistance) { + nearestElement = curr; + minDistance = dist; } } + if (nearestElement) { + + // See if there's a focusable container, and if so, send the focus command to that if (activeElement) { - var nearestElementFocusableParent = dom.parentWithClass(nearestElement, "focusable"); - nearestElementFocusableParent && nearestElementFocusableParent !== nearestElement && focusableContainer !== nearestElementFocusableParent && (nearestElement = nearestElementFocusableParent) + var nearestElementFocusableParent = dom.parentWithClass(nearestElement, 'focusable'); + if (nearestElementFocusableParent && nearestElementFocusableParent !== nearestElement) { + if (focusableContainer !== nearestElementFocusableParent) { + nearestElement = nearestElementFocusableParent; + } + } } - focus(nearestElement) + focus(nearestElement); } } function intersectsInternal(a1, a2, b1, b2) { - return b1 >= a1 && b1 <= a2 || b2 >= a1 && b2 <= a2 + + return (b1 >= a1 && b1 <= a2) || (b2 >= a1 && b2 <= a2); } function intersects(a1, a2, b1, b2) { - return intersectsInternal(a1, a2, b1, b2) || intersectsInternal(b1, b2, a1, a2) + + return intersectsInternal(a1, a2, b1, b2) || intersectsInternal(b1, b2, a1, a2); } function sendText(text) { - document.activeElement.value = text + var elem = document.activeElement; + + elem.value = text; } function focusFirst(container, focusableSelector) { - for (var elems = container.querySelectorAll(focusableSelector), i = 0, length = elems.length; i < length; i++) { + + var elems = container.querySelectorAll(focusableSelector); + + for (var i = 0, length = elems.length; i < length; i++) { + var elem = elems[i]; + if (isCurrentlyFocusableInternal(elem)) { focus(elem); - break + break; } } } function focusLast(container, focusableSelector) { - for (var elems = [].slice.call(container.querySelectorAll(focusableSelector), 0).reverse(), i = 0, length = elems.length; i < length; i++) { + + var elems = [].slice.call(container.querySelectorAll(focusableSelector), 0).reverse(); + + for (var i = 0, length = elems.length; i < length; i++) { + var elem = elems[i]; + if (isCurrentlyFocusableInternal(elem)) { focus(elem); - break + break; } } } function moveFocus(sourceElement, container, focusableSelector, offset) { - var i, length, elem, elems = container.querySelectorAll(focusableSelector), - list = []; - for (i = 0, length = elems.length; i < length; i++) elem = elems[i], isCurrentlyFocusableInternal(elem) && list.push(elem); + + var elems = container.querySelectorAll(focusableSelector); + var list = []; + var i, length, elem; + + for (i = 0, length = elems.length; i < length; i++) { + + elem = elems[i]; + + if (isCurrentlyFocusableInternal(elem)) { + list.push(elem); + } + } + var currentIndex = -1; - for (i = 0, length = list.length; i < length; i++) - if (elem = list[i], sourceElement === elem || elem.contains(sourceElement)) { + + for (i = 0, length = list.length; i < length; i++) { + + elem = list[i]; + + if (sourceElement === elem || elem.contains(sourceElement)) { currentIndex = i; - break - } if (-1 !== currentIndex) { - var newIndex = currentIndex + offset; - newIndex = Math.max(0, newIndex), newIndex = Math.min(newIndex, list.length - 1); - var newElem = list[newIndex]; - newElem && focus(newElem) + break; + } + } + + if (currentIndex === -1) { + return; + } + + var newIndex = currentIndex + offset; + newIndex = Math.max(0, newIndex); + newIndex = Math.min(newIndex, list.length - 1); + + var newElem = list[newIndex]; + if (newElem) { + focus(newElem); } } - var scopes = [], - focusableTagNames = ["INPUT", "TEXTAREA", "SELECT", "BUTTON", "A"], - focusableContainerTagNames = ["BODY", "DIALOG"], - focusableQuery = focusableTagNames.map(function(t) { - return "INPUT" === t && (t += ':not([type="range"]):not([type="file"])'), t + ':not([tabindex="-1"]):not(:disabled)' - }).join(",") + ",.focusable"; + return { autoFocus: autoFocus, focus: focus, focusableParent: focusableParent, getFocusableElements: getFocusableElements, - moveLeft: function(sourceElement, options) { - nav(sourceElement, 0, options ? options.container : null, options ? options.focusableElements : null) + moveLeft: function (sourceElement, options) { + + var container = options ? options.container : null; + var focusableElements = options ? options.focusableElements : null; + nav(sourceElement, 0, container, focusableElements); + }, - moveRight: function(sourceElement, options) { - nav(sourceElement, 1, options ? options.container : null, options ? options.focusableElements : null) + moveRight: function (sourceElement, options) { + + var container = options ? options.container : null; + var focusableElements = options ? options.focusableElements : null; + nav(sourceElement, 1, container, focusableElements); + }, - moveUp: function(sourceElement, options) { - nav(sourceElement, 2, options ? options.container : null, options ? options.focusableElements : null) + moveUp: function (sourceElement, options) { + + var container = options ? options.container : null; + var focusableElements = options ? options.focusableElements : null; + nav(sourceElement, 2, container, focusableElements); + }, - moveDown: function(sourceElement, options) { - nav(sourceElement, 3, options ? options.container : null, options ? options.focusableElements : null) + moveDown: function (sourceElement, options) { + + var container = options ? options.container : null; + var focusableElements = options ? options.focusableElements : null; + nav(sourceElement, 3, container, focusableElements); + }, sendText: sendText, isCurrentlyFocusable: isCurrentlyFocusable, @@ -252,5 +547,5 @@ define(["dom"], function(dom) { focusFirst: focusFirst, focusLast: focusLast, moveFocus: moveFocus - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/fonts/fonts.css b/src/bower_components/emby-webcomponents/fonts/fonts.css index 112441ad18..12f1eaf4b7 100644 --- a/src/bower_components/emby-webcomponents/fonts/fonts.css +++ b/src/bower_components/emby-webcomponents/fonts/fonts.css @@ -1,39 +1,37 @@ -h1, -h2, -h3 { - font-weight: 500 +html { + font-family: -apple-system, "Helvetica", system-ui, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen-Sans", "Ubuntu", "Cantarell", "Helvetica Neue", 'Open Sans', sans-serif; } html { - font-family: -apple-system, Helvetica, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", 'Open Sans', sans-serif; font-size: 93%; -webkit-text-size-adjust: 100%; - -moz-text-size-adjust: 100%; - text-size-adjust: 100% + text-size-adjust: 100%; } -h1, -h2, -h3 { - font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", 'Open Sans', sans-serif +h1, h2, h3 { + /* For better bolding, since Helvetica does not support 500 weight, and 600 is too thick */ + font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen-Sans", "Ubuntu", "Cantarell", "Helvetica Neue", 'Open Sans', sans-serif; } h1 { - font-size: 1.8em + font-weight: 500; + font-size: 1.8em; } h2 { - font-size: 1.5em + font-weight: 500; + font-size: 1.5em; } h3 { - font-size: 1.17em + font-weight: 500; + font-size: 1.17em; } .layout-tv { - font-size: 2.5vh + font-size: 2.5vh; } .layout-mobile { - font-size: 90% -} \ No newline at end of file + font-size: 90%; +} diff --git a/src/bower_components/emby-webcomponents/fonts/fonts.sized.css b/src/bower_components/emby-webcomponents/fonts/fonts.sized.css index f8722d0016..073b74167c 100644 --- a/src/bower_components/emby-webcomponents/fonts/fonts.sized.css +++ b/src/bower_components/emby-webcomponents/fonts/fonts.sized.css @@ -1,33 +1,32 @@ -h1, -h2, -h3 { - font-weight: 500 -} - h1 { - font-size: 1.8em + font-weight: 500; + font-size: 1.8em; } .layout-desktop h1 { - font-size: 2em + font-size: 2em; } h2 { - font-size: 1.5em + font-weight: 500; + font-size: 1.5em; } h3 { - font-size: 1.17em + font-weight: 500; + font-size: 1.17em; } -@media all and (min-height:720px) { +@media all and (min-height: 720px) { html { - font-size: 20px + font-size: 20px; } } -@media all and (min-height:1000px) { +/* This is supposed to be 1080p, but had to reduce the min height to account for possible browser chrome */ +@media all and (min-height: 1000px) { + html { - font-size: 27px + font-size: 27px; } -} \ No newline at end of file +} diff --git a/src/bower_components/emby-webcomponents/fonts/material-icons/style.css b/src/bower_components/emby-webcomponents/fonts/material-icons/style.css index bcaf041b93..2d410b9985 100644 --- a/src/bower_components/emby-webcomponents/fonts/material-icons/style.css +++ b/src/bower_components/emby-webcomponents/fonts/material-icons/style.css @@ -2,12 +2,12 @@ font-family: 'Material Icons'; font-style: normal; font-weight: 400; - src: local('Material Icons'), local('MaterialIcons-Regular'), url(flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2) format('woff2'), url(flUhRq6tzZclQEJ-Vdg-IuiaDsNa.woff) format('woff') + src: local('Material Icons'), local('MaterialIcons-Regular'), url(flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2) format('woff2'), url(flUhRq6tzZclQEJ-Vdg-IuiaDsNa.woff) format('woff'); } .md-icon { font-family: 'Material Icons'; - font-weight: 400; + font-weight: normal; font-style: normal; letter-spacing: normal; text-transform: none; @@ -15,12 +15,11 @@ white-space: nowrap; word-wrap: normal; direction: ltr; + -webkit-font-feature-settings: 'liga'; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; - -webkit-font-feature-settings: "liga"1; - -moz-font-feature-settings: "liga"1; - font-feature-settings: "liga"1; + font-feature-settings: "liga" 1; line-height: 1; overflow: hidden; - vertical-align: middle -} \ No newline at end of file + vertical-align: middle; +} diff --git a/src/bower_components/emby-webcomponents/formdialog.css b/src/bower_components/emby-webcomponents/formdialog.css index d44a7ed871..b976d8130e 100644 --- a/src/bower_components/emby-webcomponents/formdialog.css +++ b/src/bower_components/emby-webcomponents/formdialog.css @@ -1,145 +1,115 @@ -.formDialog, -.formDialogHeader { - display: -webkit-box; - display: -webkit-flex -} - -.formDialog, -.formDialogFooter-vertical { - -webkit-box-orient: vertical; - -webkit-box-direction: normal -} - .formDialog { display: flex; - -webkit-flex-direction: column; flex-direction: column; - position: relative + position: relative; } .formDialogHeader { padding: 1em .5em; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - -webkit-flex-shrink: 0; - flex-shrink: 0 + flex-shrink: 0; } .formDialogHeaderTitle { margin-left: .25em; + /* In case of h1, h2, h3 */ margin-top: 0; - margin-bottom: 0 + margin-bottom: 0; } .formDialogContent:not(.no-grow) { - -webkit-box-flex: 1; - -webkit-flex-grow: 1; - flex-grow: 1 + flex-grow: 1; } .dialogContentInner { - padding: .5em 1em 20em + padding: .5em 1em 20em 1em; } .dialogContentInner-mini { - padding-bottom: 10em + padding-bottom: 10em; } .dialog-content-centered { margin: 0 auto; - max-width: 53em + max-width: 53em; } .dialogContentTitle { - margin-top: 1em + margin-top: 1em; } .formDialogFooter { bottom: 0; left: 0; right: 0; - display: -webkit-box; - display: -webkit-flex; display: flex; position: absolute; padding: 1.25em 1em; + /* Without this emby-checkbox is able to appear on top */ z-index: 1; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; - -webkit-flex-wrap: wrap; - flex-wrap: wrap + flex-wrap: wrap; } .formDialogFooter-flex { position: static; - width: 100% + width: 100%; } .formDialogFooter-vertical { padding-bottom: 1.5em; - -webkit-flex-direction: column; flex-direction: column; width: 80% !important; - padding-top: .5em + padding-top: .5em; } .formDialogFooterItem { margin: .5em !important; - -webkit-box-flex: 1; - -webkit-flex-grow: 1; flex-grow: 1; text-align: center; - -webkit-flex-basis: 0; - flex-basis: 0 + flex-basis: 0; } .formDialogFooterItem-vertical { max-width: none !important; width: 100%; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; - margin: 1em !important + margin: 1em !important; } .formDialogFooterItem-nomarginbottom { - margin-bottom: 0 !important + margin-bottom: 0 !important; } .formDialogFooterItem-autosize { - -webkit-flex-basis: initial; flex-basis: initial; - -webkit-box-flex: initial; - -webkit-flex-grow: initial; flex-grow: initial; padding-left: 2em; - padding-right: 2em + padding-right: 2em; } -@media all and (min-width:50em) { +@media all and (min-width: 50em) { + .formDialogFooterItem { - max-width: 80% + max-width: 80%; } .dialogContentInner { padding-left: 1.5em; - padding-right: 1.5em + padding-right: 1.5em; } } -@media all and (min-width:80em) { +@media all and (min-width: 80em) { + .formDialogFooterItem { - max-width: 70% + max-width: 70%; } .dialogContentInner { padding-left: 2em; - padding-right: 2em + padding-right: 2em; } -} \ No newline at end of file +} diff --git a/src/bower_components/emby-webcomponents/fullscreen/fullscreen-dc.js b/src/bower_components/emby-webcomponents/fullscreen/fullscreen-dc.js index a7f6ff1e13..bc12a6a76d 100644 --- a/src/bower_components/emby-webcomponents/fullscreen/fullscreen-dc.js +++ b/src/bower_components/emby-webcomponents/fullscreen/fullscreen-dc.js @@ -1,12 +1,26 @@ -define(["dom", "fullscreenManager"], function(dom, fullscreenManager) { - "use strict"; +define(['dom', 'fullscreenManager'], function (dom, fullscreenManager) { + 'use strict'; function isTargetValid(target) { - return !dom.parentWithTag(target, ["BUTTON", "INPUT", "TEXTAREA"]) + + if (dom.parentWithTag(target, ['BUTTON', 'INPUT', 'TEXTAREA'])) { + return false; + } + + return true; } - dom.addEventListener(window, "dblclick", function(e) { - isTargetValid(e.target) && (fullscreenManager.isFullScreen() ? fullscreenManager.exitFullscreen() : fullscreenManager.requestFullscreen()) + + dom.addEventListener(window, 'dblclick', function (e) { + + if (isTargetValid(e.target)) { + if (fullscreenManager.isFullScreen()) { + fullscreenManager.exitFullscreen(); + } else { + fullscreenManager.requestFullscreen(); + } + } + }, { - passive: !0 - }) + passive: true + }); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/fullscreen/fullscreenmanager.js b/src/bower_components/emby-webcomponents/fullscreen/fullscreenmanager.js index adfee87460..d39d8fd3b6 100644 --- a/src/bower_components/emby-webcomponents/fullscreen/fullscreenmanager.js +++ b/src/bower_components/emby-webcomponents/fullscreen/fullscreenmanager.js @@ -1,24 +1,74 @@ -define(["events", "dom"], function(events, dom) { - "use strict"; +define(['events', 'dom'], function (events, dom) { + 'use strict'; - function fullscreenManager() {} + function fullscreenManager() { + + } + + fullscreenManager.prototype.requestFullscreen = function (element) { + + element = element || document.documentElement; + + if (element.requestFullscreen) { + element.requestFullscreen(); + return; + } else if (element.mozRequestFullScreen) { + element.mozRequestFullScreen(); + return; + } else if (element.webkitRequestFullscreen) { + element.webkitRequestFullscreen(); + return; + } else if (element.msRequestFullscreen) { + element.msRequestFullscreen(); + return; + } + + // Hack - This is only available for video elements in ios safari + if (element.tagName !== 'VIDEO') { + element = document.querySelector('video') || element; + } + if (element.webkitEnterFullscreen) { + element.webkitEnterFullscreen(); + } + }; + + fullscreenManager.prototype.exitFullscreen = function () { + + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } else if (document.webkitCancelFullscreen) { + document.webkitCancelFullscreen(); + } else if (document.msExitFullscreen) { + document.msExitFullscreen(); + } + }; + + fullscreenManager.prototype.isFullScreen = function () { + + return document.fullscreen || document.mozFullScreen || document.webkitIsFullScreen || document.msFullscreenElement ? true : false; + }; + + var manager = new fullscreenManager(); function onFullScreenChange() { - events.trigger(manager, "fullscreenchange") + events.trigger(manager, 'fullscreenchange'); } - fullscreenManager.prototype.requestFullscreen = function(element) { - return element = element || document.documentElement, element.requestFullscreen ? void element.requestFullscreen() : element.mozRequestFullScreen ? void element.mozRequestFullScreen() : element.webkitRequestFullscreen ? void element.webkitRequestFullscreen() : element.msRequestFullscreen ? void element.msRequestFullscreen() : ("VIDEO" !== element.tagName && (element = document.querySelector("video") || element), void(element.webkitEnterFullscreen && element.webkitEnterFullscreen())) - }, fullscreenManager.prototype.exitFullscreen = function() { - document.exitFullscreen ? document.exitFullscreen() : document.mozCancelFullScreen ? document.mozCancelFullScreen() : document.webkitExitFullscreen ? document.webkitExitFullscreen() : document.webkitCancelFullscreen ? document.webkitCancelFullscreen() : document.msExitFullscreen && document.msExitFullscreen() - }, fullscreenManager.prototype.isFullScreen = function() { - return !!(document.fullscreen || document.mozFullScreen || document.webkitIsFullScreen || document.msFullscreenElement) - }; - var manager = new fullscreenManager; - return dom.addEventListener(document, "fullscreenchange", onFullScreenChange, { - passive: !0 - }), dom.addEventListener(document, "webkitfullscreenchange", onFullScreenChange, { - passive: !0 - }), dom.addEventListener(document, "mozfullscreenchange", onFullScreenChange, { - passive: !0 - }), manager + + dom.addEventListener(document, 'fullscreenchange', onFullScreenChange, { + passive: true + }); + + dom.addEventListener(document, 'webkitfullscreenchange', onFullScreenChange, { + passive: true + }); + + dom.addEventListener(document, 'mozfullscreenchange', onFullScreenChange, { + passive: true + }); + + return manager; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/globalize.js b/src/bower_components/emby-webcomponents/globalize.js index 0390860404..c36dea1910 100644 --- a/src/bower_components/emby-webcomponents/globalize.js +++ b/src/bower_components/emby-webcomponents/globalize.js @@ -1,127 +1,287 @@ -define(["connectionManager", "userSettings", "events"], function(connectionManager, userSettings, events) { - "use strict"; +define(['connectionManager', 'userSettings', 'events'], function (connectionManager, userSettings, events) { + 'use strict'; + + var allTranslations = {}; + var currentCulture; + var currentDateTimeCulture; function getCurrentLocale() { - return currentCulture + + return currentCulture; } function getCurrentDateTimeLocale() { - return currentDateTimeCulture + return currentDateTimeCulture; } function getDefaultLanguage() { - var culture = document.documentElement.getAttribute("data-culture"); - return culture || (navigator.language ? navigator.language : navigator.userLanguage ? navigator.userLanguage : navigator.languages && navigator.languages.length ? navigator.languages[0] : "en-us") + + var culture = document.documentElement.getAttribute('data-culture'); + + if (culture) { + return culture; + } + + if (navigator.language) { + return navigator.language; + } + if (navigator.userLanguage) { + return navigator.userLanguage; + } + if (navigator.languages && navigator.languages.length) { + return navigator.languages[0]; + } + + return 'en-us'; } function updateCurrentCulture() { + var culture; try { - culture = userSettings.language() - } catch (err) {} - culture = culture || getDefaultLanguage(), currentCulture = normalizeLocaleName(culture); + culture = userSettings.language(); + } catch (err) { + + } + culture = culture || getDefaultLanguage(); + + currentCulture = normalizeLocaleName(culture); + var dateTimeCulture; try { - dateTimeCulture = userSettings.dateTimeLocale() - } catch (err) {} - currentDateTimeCulture = dateTimeCulture ? normalizeLocaleName(dateTimeCulture) : currentCulture, ensureTranslations(currentCulture) + dateTimeCulture = userSettings.dateTimeLocale(); + } catch (err) { + + } + + if (dateTimeCulture) { + currentDateTimeCulture = normalizeLocaleName(dateTimeCulture); + } + else { + currentDateTimeCulture = currentCulture; + } + + ensureTranslations(currentCulture); } function ensureTranslations(culture) { - for (var i in allTranslations) ensureTranslation(allTranslations[i], culture) - } - function ensureTranslation(translationInfo, culture) { - return translationInfo.dictionaries[culture] ? Promise.resolve() : loadTranslation(translationInfo.translations, culture).then(function(dictionary) { - translationInfo.dictionaries[culture] = dictionary - }) - } - - function normalizeLocaleName(culture) { - culture = culture.replace("_", "-"); - var parts = culture.split("-"); - 2 === parts.length && parts[0].toLowerCase() === parts[1].toLowerCase() && (culture = parts[0].toLowerCase()); - var lower = culture.toLowerCase(); - return "ca-es" === lower ? "ca" : "sv-se" === lower ? "sv" : lower - } - - function getDictionary(module) { - module || (module = defaultModule()); - var translations = allTranslations[module]; - return translations ? translations.dictionaries[getCurrentLocale()] : {} - } - - function register(options) { - allTranslations[options.name] = { - translations: options.strings || options.translations, - dictionaries: {} + for (var i in allTranslations) { + ensureTranslation(allTranslations[i], culture); } } - function loadStrings(options) { - var locale = getCurrentLocale(); - return "string" == typeof options ? ensureTranslation(allTranslations[options], locale) : (register(options), ensureTranslation(allTranslations[options.name], locale)) + function ensureTranslation(translationInfo, culture) { + + if (translationInfo.dictionaries[culture]) { + return Promise.resolve(); + } + + return loadTranslation(translationInfo.translations, culture).then(function (dictionary) { + + translationInfo.dictionaries[culture] = dictionary; + }); } + function normalizeLocaleName(culture) { + + culture = culture.replace('_', '-'); + + // If it's de-DE, convert to just de + var parts = culture.split('-'); + if (parts.length === 2) { + if (parts[0].toLowerCase() === parts[1].toLowerCase()) { + culture = parts[0].toLowerCase(); + } + } + + var lower = culture.toLowerCase(); + + if (lower === 'ca-es') { + return 'ca'; + } + + // normalize Swedish + if (lower === 'sv-se') { + return 'sv'; + } + + return lower; + } + + function getDictionary(module) { + + if (!module) { + module = defaultModule(); + } + + var translations = allTranslations[module]; + + if (!translations) { + return {}; + } + + return translations.dictionaries[getCurrentLocale()]; + } + + function register(options) { + + allTranslations[options.name] = { + translations: options.strings || options.translations, + dictionaries: {} + }; + } + + function loadStrings(options) { + + var locale = getCurrentLocale(); + + if (typeof options === 'string') { + return ensureTranslation(allTranslations[options], locale); + } else { + register(options); + return ensureTranslation(allTranslations[options.name], locale); + } + } + + var cacheParam = new Date().getTime(); function loadTranslation(translations, lang) { + lang = normalizeLocaleName(lang); - var filtered = translations.filter(function(t) { - return normalizeLocaleName(t.lang) === lang + + var filtered = translations.filter(function (t) { + return normalizeLocaleName(t.lang) === lang; }); - return filtered.length || (filtered = translations.filter(function(t) { - return "en-us" === normalizeLocaleName(t.lang) - })), new Promise(function(resolve, reject) { - if (!filtered.length) return void resolve(); + + if (!filtered.length) { + filtered = translations.filter(function (t) { + return normalizeLocaleName(t.lang) === 'en-us'; + }); + } + + return new Promise(function (resolve, reject) { + + if (!filtered.length) { + resolve(); + return; + } + var url = filtered[0].path; - url += -1 === url.indexOf("?") ? "?" : "&", url += "v=" + cacheParam; - var xhr = new XMLHttpRequest; - xhr.open("GET", url, !0), xhr.onload = function(e) { - resolve(this.status < 400 ? JSON.parse(this.response) : {}) - }, xhr.onerror = function() { - resolve({}) - }, xhr.send() - }) + + url += url.indexOf('?') === -1 ? '?' : '&'; + url += 'v=' + cacheParam; + + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + + xhr.onload = function (e) { + if (this.status < 400) { + resolve(JSON.parse(this.response)); + } else { + resolve({}); + } + }; + + xhr.onerror = function () { + resolve({}); + }; + xhr.send(); + }); } function translateKey(key) { - var module, parts = key.split("#"); - return parts.length > 1 && (module = parts[0], key = parts[1]), translateKeyFromModule(key, module) + + var parts = key.split('#'); + var module; + + if (parts.length > 1) { + module = parts[0]; + key = parts[1]; + } + + return translateKeyFromModule(key, module); } function translateKeyFromModule(key, module) { + var dictionary = getDictionary(module); - return dictionary ? dictionary[key] || key : key + + if (!dictionary) { + return key; + } + + return dictionary[key] || key; } function replaceAll(str, find, replace) { - return str.split(find).join(replace) + + return str.split(find).join(replace); } function translate(key) { - for (var val = translateKey(key), i = 1; i < arguments.length; i++) val = replaceAll(val, "{" + (i - 1) + "}", arguments[i]); - return val + + var val = translateKey(key); + + for (var i = 1; i < arguments.length; i++) { + + val = replaceAll(val, '{' + (i - 1) + '}', arguments[i]); + + } + + return val; } function translateHtml(html, module) { - if (module || (module = defaultModule()), !module) throw new Error("module cannot be null or empty"); - var startIndex = html.indexOf("${"); - if (-1 === startIndex) return html; + + if (!module) { + module = defaultModule(); + } + + if (!module) { + throw new Error('module cannot be null or empty'); + } + + var startIndex = html.indexOf('${'); + + if (startIndex === -1) { + return html; + } + startIndex += 2; - var endIndex = html.indexOf("}", startIndex); - if (-1 === endIndex) return html; - var key = html.substring(startIndex, endIndex), - val = translateKeyFromModule(key, module); - return html = html.replace("${" + key + "}", val), translateHtml(html, module) + var endIndex = html.indexOf('}', startIndex); + + if (endIndex === -1) { + return html; + } + + var key = html.substring(startIndex, endIndex); + var val = translateKeyFromModule(key, module); + + html = html.replace('${' + key + '}', val); + return translateHtml(html, module); } + var _defaultModule; function defaultModule(val) { - return val && (_defaultModule = val), _defaultModule + + if (val) { + + _defaultModule = val; + } + + return _defaultModule; } - var currentCulture, currentDateTimeCulture, _defaultModule, allTranslations = {}, - cacheParam = (new Date).getTime(); - return updateCurrentCulture(), events.on(connectionManager, "localusersignedin", updateCurrentCulture), events.on(userSettings, "change", function(e, name) { - "language" !== name && "datetimelocale" !== name || updateCurrentCulture() - }), { + + updateCurrentCulture(); + + events.on(connectionManager, 'localusersignedin', updateCurrentCulture); + events.on(userSettings, 'change', function (e, name) { + if (name === 'language' || name === 'datetimelocale') { + updateCurrentCulture(); + } + }); + + return { getString: translate, translate: translate, translateDocument: translateHtml, @@ -131,5 +291,5 @@ define(["connectionManager", "userSettings", "events"], function(connectionManag getCurrentLocale: getCurrentLocale, getCurrentDateTimeLocale: getCurrentDateTimeLocale, register: register - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/guide/guide-settings.js b/src/bower_components/emby-webcomponents/guide/guide-settings.js index 624a582574..e95d750c9a 100644 --- a/src/bower_components/emby-webcomponents/guide/guide-settings.js +++ b/src/bower_components/emby-webcomponents/guide/guide-settings.js @@ -1,71 +1,172 @@ -define(["dialogHelper", "globalize", "userSettings", "layoutManager", "connectionManager", "require", "loading", "scrollHelper", "emby-checkbox", "emby-radio", "css!./../formdialog", "material-icons"], function(dialogHelper, globalize, userSettings, layoutManager, connectionManager, require, loading, scrollHelper) { - "use strict"; +define(['dialogHelper', 'globalize', 'userSettings', 'layoutManager', 'connectionManager', 'require', 'loading', 'scrollHelper', 'emby-checkbox', 'emby-radio', 'css!./../formdialog', 'material-icons'], function (dialogHelper, globalize, userSettings, layoutManager, connectionManager, require, loading, scrollHelper) { + 'use strict'; function saveCategories(context, options) { - for (var categories = [], chkCategorys = context.querySelectorAll(".chkCategory"), i = 0, length = chkCategorys.length; i < length; i++) { - var type = chkCategorys[i].getAttribute("data-type"); - chkCategorys[i].checked && categories.push(type) + + var categories = []; + + var chkCategorys = context.querySelectorAll('.chkCategory'); + for (var i = 0, length = chkCategorys.length; i < length; i++) { + + var type = chkCategorys[i].getAttribute('data-type'); + + if (chkCategorys[i].checked) { + categories.push(type); + } } - categories.length >= 4 && categories.push("series"), categories.push("all"), options.categories = categories + + if (categories.length >= 4) { + categories.push('series'); + } + + // differentiate between none and all + categories.push('all'); + options.categories = categories; } function loadCategories(context, options) { - for (var selectedCategories = options.categories || [], chkCategorys = context.querySelectorAll(".chkCategory"), i = 0, length = chkCategorys.length; i < length; i++) { - var type = chkCategorys[i].getAttribute("data-type"); - chkCategorys[i].checked = !selectedCategories.length || -1 !== selectedCategories.indexOf(type) + + var selectedCategories = options.categories || []; + + var chkCategorys = context.querySelectorAll('.chkCategory'); + for (var i = 0, length = chkCategorys.length; i < length; i++) { + + var type = chkCategorys[i].getAttribute('data-type'); + + chkCategorys[i].checked = !selectedCategories.length || selectedCategories.indexOf(type) !== -1; } } function save(context) { - var i, length, chkIndicators = context.querySelectorAll(".chkIndicator"); + + var i, length; + + var chkIndicators = context.querySelectorAll('.chkIndicator'); for (i = 0, length = chkIndicators.length; i < length; i++) { - var type = chkIndicators[i].getAttribute("data-type"); - userSettings.set("guide-indicator-" + type, chkIndicators[i].checked) + + var type = chkIndicators[i].getAttribute('data-type'); + userSettings.set('guide-indicator-' + type, chkIndicators[i].checked); } - userSettings.set("guide-colorcodedbackgrounds", context.querySelector(".chkColorCodedBackgrounds").checked), userSettings.set("livetv-favoritechannelsattop", context.querySelector(".chkFavoriteChannelsAtTop").checked); - var sortBys = context.querySelectorAll(".chkSortOrder"); - for (i = 0, length = sortBys.length; i < length; i++) + + userSettings.set('guide-colorcodedbackgrounds', context.querySelector('.chkColorCodedBackgrounds').checked); + userSettings.set('livetv-favoritechannelsattop', context.querySelector('.chkFavoriteChannelsAtTop').checked); + + var sortBys = context.querySelectorAll('.chkSortOrder'); + for (i = 0, length = sortBys.length; i < length; i++) { if (sortBys[i].checked) { - userSettings.set("livetv-channelorder", sortBys[i].value); - break + userSettings.set('livetv-channelorder', sortBys[i].value); + break; } + } } function load(context) { - var i, length, chkIndicators = context.querySelectorAll(".chkIndicator"); + + var i, length; + + var chkIndicators = context.querySelectorAll('.chkIndicator'); for (i = 0, length = chkIndicators.length; i < length; i++) { - var type = chkIndicators[i].getAttribute("data-type"); - "true" === chkIndicators[i].getAttribute("data-default") ? chkIndicators[i].checked = "false" !== userSettings.get("guide-indicator-" + type) : chkIndicators[i].checked = "true" === userSettings.get("guide-indicator-" + type) + + var type = chkIndicators[i].getAttribute('data-type'); + + if (chkIndicators[i].getAttribute('data-default') === 'true') { + chkIndicators[i].checked = userSettings.get('guide-indicator-' + type) !== 'false'; + } else { + chkIndicators[i].checked = userSettings.get('guide-indicator-' + type) === 'true'; + } + } + + context.querySelector('.chkColorCodedBackgrounds').checked = userSettings.get('guide-colorcodedbackgrounds') === 'true'; + context.querySelector('.chkFavoriteChannelsAtTop').checked = userSettings.get('livetv-favoritechannelsattop') !== 'false'; + + var sortByValue = userSettings.get('livetv-channelorder') || 'Number'; + + var sortBys = context.querySelectorAll('.chkSortOrder'); + for (i = 0, length = sortBys.length; i < length; i++) { + sortBys[i].checked = sortBys[i].value === sortByValue; + } + } + + function onSortByChange() { + var newValue = this.value; + if (this.checked) { + var changed = options.query.SortBy !== newValue; + + options.query.SortBy = newValue.replace('_', ','); + options.query.StartIndex = 0; + + if (options.callback && changed) { + options.callback(); + } } - context.querySelector(".chkColorCodedBackgrounds").checked = "true" === userSettings.get("guide-colorcodedbackgrounds"), context.querySelector(".chkFavoriteChannelsAtTop").checked = "false" !== userSettings.get("livetv-favoritechannelsattop"); - var sortByValue = userSettings.get("livetv-channelorder") || "Number", - sortBys = context.querySelectorAll(".chkSortOrder"); - for (i = 0, length = sortBys.length; i < length; i++) sortBys[i].checked = sortBys[i].value === sortByValue } function showEditor(options) { - return new Promise(function(resolve, reject) { - var settingsChanged = !1; - require(["text!./guide-settings.template.html"], function(template) { + + return new Promise(function (resolve, reject) { + + var settingsChanged = false; + + require(['text!./guide-settings.template.html'], function (template) { + var dialogOptions = { - removeOnClose: !0, - scrollY: !1 + removeOnClose: true, + scrollY: false }; - layoutManager.tv ? dialogOptions.size = "fullscreen" : dialogOptions.size = "small"; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'small'; + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"); - var html = ""; - html += globalize.translateDocument(template, "sharedcomponents"), dlg.innerHTML = html, dlg.addEventListener("change", function() { - settingsChanged = !0 - }), dlg.addEventListener("close", function() { - layoutManager.tv && scrollHelper.centerFocus.off(dlg.querySelector(".formDialogContent"), !1), save(dlg), saveCategories(dlg, options), settingsChanged ? resolve() : reject() - }), dlg.querySelector(".btnCancel").addEventListener("click", function() { - dialogHelper.close(dlg) - }), layoutManager.tv && scrollHelper.centerFocus.on(dlg.querySelector(".formDialogContent"), !1), load(dlg), loadCategories(dlg, options), dialogHelper.open(dlg) - }) - }) + + dlg.classList.add('formDialog'); + + var html = ''; + + html += globalize.translateDocument(template, 'sharedcomponents'); + + dlg.innerHTML = html; + + dlg.addEventListener('change', function () { + + settingsChanged = true; + }); + + dlg.addEventListener('close', function () { + + if (layoutManager.tv) { + scrollHelper.centerFocus.off(dlg.querySelector('.formDialogContent'), false); + } + + save(dlg); + saveCategories(dlg, options); + + if (settingsChanged) { + resolve(); + } else { + reject(); + } + }); + + dlg.querySelector('.btnCancel').addEventListener('click', function () { + dialogHelper.close(dlg); + }); + + if (layoutManager.tv) { + scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false); + } + + load(dlg); + loadCategories(dlg, options); + dialogHelper.open(dlg); + }); + }); } + return { show: showEditor - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/guide/guide.css b/src/bower_components/emby-webcomponents/guide/guide.css index fa1d4f0204..fcac17f272 100644 --- a/src/bower_components/emby-webcomponents/guide/guide.css +++ b/src/bower_components/emby-webcomponents/guide/guide.css @@ -1,194 +1,141 @@ -.tvGuideHeader, -.tvguide { - display: -webkit-box; - display: -webkit-flex -} - -.channelPrograms, -.programContainer, -.timeslotHeadersInner, -.tvProgram { - position: relative -} - -.channelPrograms, -.channelsContainer, -.tvGuideHeader, -.tvguide { - -webkit-box-orient: vertical; - -webkit-box-direction: normal -} - -.guideChannelName, -.guideChannelNumber, -.guideProgramName, -.guideProgramNameText { - -o-text-overflow: ellipsis -} - .tvguide { display: flex; - -webkit-flex-direction: column; flex-direction: column; - -webkit-box-align: initial; - -webkit-align-items: initial; - align-items: initial + align-items: initial; } .tvGuideHeader { white-space: nowrap; width: 100%; - -webkit-flex-direction: column; flex-direction: column; - -webkit-flex-shrink: 0; flex-shrink: 0; display: flex; - contain: layout style paint + contain: layout style paint; } .layout-desktop .tvGuideHeader { - margin-bottom: .5em + margin-bottom: .5em; } .guideHeaderDateSelection { font-size: 86%; - padding: .4em 0 + padding: .4em 0; } .guide-headerTimeslots { - display: -webkit-box; - display: -webkit-flex; - display: flex + display: flex; } .tvProgramSectionHeader { - margin: 0 + margin: 0; } .tvProgram { display: block; text-decoration: none; - white-space: nowrap + white-space: nowrap; + position: relative; } .guideProgramIndicator { text-transform: uppercase; - -webkit-border-radius: .25em; border-radius: .25em; margin-right: .5em; font-size: 82%; padding: .2em .25em; - display: -webkit-inline-box; - display: -webkit-inline-flex; display: inline-flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; text-align: center; - margin-left: 1em + margin-left: 1em; } .guide-channelTimeslotHeader { - -webkit-box-pack: center; - -webkit-justify-content: center; - justify-content: center + flex-shrink: 0; + justify-content: center; } .timeslotHeaders { white-space: nowrap; font-weight: 500; - font-size: 120% + font-size: 120%; } .programContainer { white-space: nowrap; - -webkit-box-align: start; - -webkit-align-items: flex-start; + position: relative; align-items: flex-start; - contain: strict + contain: strict; +} + +.channelPrograms { + white-space: nowrap; + position: relative; + contain: strict; + box-sizing: border-box; +} + +.timeslotHeadersInner { + position: relative; } .guideSpacer { width: .3em; - -webkit-flex-shrink: 0; - flex-shrink: 0 + flex-shrink: 0; } -.channelPrograms, -.timeslotHeadersInner { - width: 1800vw +.channelPrograms, .timeslotHeadersInner { + width: 1800vw; } -@media all and (min-width:37.5em) { +@media all and (min-width: 37.5em) { - .channelPrograms, - .timeslotHeadersInner { - width: 1400vw + .channelPrograms, .timeslotHeadersInner { + width: 1400vw; } } -@media all and (min-width:50em) { +@media all and (min-width: 50em) { - .channelPrograms, - .timeslotHeadersInner { - width: 1200vw + .channelPrograms, .timeslotHeadersInner { + width: 1200vw; } } -@media all and (min-width:80em) { +@media all and (min-width: 80em) { - .channelPrograms, - .timeslotHeadersInner { - width: 810vw + .channelPrograms, .timeslotHeadersInner { + width: 810vw; } } .timeslotHeader { - display: -webkit-inline-box; - display: -webkit-inline-flex; display: inline-flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; text-indent: .25em; - width: 2.0833333333333333333333333333333% + width: 2.0833333333333333333333333333333%; } -.guide-channelHeaderCell, -.guide-channelTimeslotHeader, -.programCell { - color: inherit; +.guide-channelHeaderCell, .guide-channelTimeslotHeader { + padding: 0 !important; cursor: pointer; + outline: none !important; + width: 100%; vertical-align: middle; font-family: inherit; - text-decoration: none; - -webkit-box-align: center; - text-align: left; - overflow: hidden -} - -.guide-channelHeaderCell, -.guide-channelTimeslotHeader { - padding: 0 !important; - outline: 0 !important; - width: 100%; font-size: inherit; - -o-text-overflow: ellipsis; + overflow: hidden; text-overflow: ellipsis; margin: 0 1px 0 0; - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-align-items: center; align-items: center; + text-decoration: none; + /* Needed in firefox */ + text-align: left; contain: strict; - -webkit-flex-shrink: 0; flex-shrink: 0; - -webkit-border-radius: .12em; - border-radius: .12em + border-radius: .12em; + color: inherit; } .guide-channelHeaderCell { @@ -198,187 +145,151 @@ height: 4.42em; contain: strict; position: relative; - background: 0 0 + background: transparent; } .guide-channelTimeslotHeader { border: 0 !important; - border-right-color: transparent } -.channelsContainer, -.guide-channelTimeslotHeader { - width: 24vw +/* Important - have to put the fixed width on channelsContainer, not the individual channelHeaderCell + This was causing channelsContainer to extend beyond the fixed width on ps4, tizen, lg and opera tv. +*/ +.channelsContainer, .guide-channelTimeslotHeader { + width: 24vw; } @media all and (min-width:31.25em) { - - .channelsContainer, - .guide-channelTimeslotHeader { - width: 16vw + .channelsContainer, .guide-channelTimeslotHeader { + width: 16vw; } } @media all and (min-width:37.5em) { - - .channelsContainer, - .guide-channelTimeslotHeader { - width: 16vw + .channelsContainer, .guide-channelTimeslotHeader { + width: 16vw; } } @media all and (min-width:50em) { - - .channelsContainer, - .guide-channelTimeslotHeader { - width: 14vw + .channelsContainer, .guide-channelTimeslotHeader { + width: 14vw; } } @media all and (min-width:80em) { - - .channelsContainer, - .guide-channelTimeslotHeader { - width: 12vw + .channelsContainer, .guide-channelTimeslotHeader { + width: 12vw; } } .btnGuideViewSettings { margin: 0; - -webkit-flex-shrink: 0; - flex-shrink: 0 + flex-shrink: 0; } .btnGuideViewSettingsIcon { - font-size: 1.5em !important + font-size: 1.5em !important; } .selectDateIcon { - -webkit-flex-shrink: 0; - flex-shrink: 0 + flex-shrink: 0; } -@media all and (max-width:50em) { +@media all and (max-width: 50em) { - .guideHdIcon, - .liveTvProgram, - .newTvProgram, - .premiereTvProgram { - display: none + .newTvProgram, .liveTvProgram, .premiereTvProgram, .guideHdIcon { + display: none; } } -.channelPrograms, -.programCell { - border-style: solid; - display: -webkit-box; - display: -webkit-flex; - contain: strict -} - .channelPrograms { - white-space: nowrap; - -webkit-box-sizing: border-box; - box-sizing: border-box; height: 4.42em; + contain: strict; display: flex; - -webkit-flex-direction: column; flex-direction: column; - border-width: 1px 0 + border-style: solid; + border-width: 1px 0 1px 0; } -.channelPrograms+.channelPrograms, -.guide-channelHeaderCell+.guide-channelHeaderCell { - margin-top: -1px + .channelPrograms + .channelPrograms, .guide-channelHeaderCell + .guide-channelHeaderCell { + margin-top: -1px; + } + +.channelPrograms-tv, .guide-channelHeaderCell-tv { + height: 3em; } -.channelPrograms-tv, -.guide-channelHeaderCell-tv { - height: 3em +.guide-channelTimeslotHeader { + border-right-color: transparent; } -.guide-channelTimeslotHeader, -.timeslotHeader { - background: 0 0 !important; - height: 2.8em +.guide-channelTimeslotHeader, .timeslotHeader { + background: transparent !important; + height: 2.8em; } .programGrid { padding-bottom: 4px; - -webkit-box-flex: 1; - -webkit-flex-grow: 1; - flex-grow: 1 + flex-grow: 1; } .programCell { - background: 0 0; + color: inherit; + background: transparent; + border-style: solid; border-width: 0 0 0 1px; padding: 0 !important; + cursor: pointer; + outline: none !important; width: 100%; + vertical-align: middle; + font-family: inherit; font-size: inherit; position: absolute; top: 0; bottom: 0; display: flex; - -webkit-align-items: center; + text-decoration: none; + overflow: hidden; align-items: center; - -webkit-box-flex: 1; - -webkit-flex-grow: 1; + /* Needed for Firefox */ + text-align: left; + contain: strict; flex-grow: 1; - margin: 0 !important -} - -.channelsContainer, -.guideProgramName, -.programGrid { - contain: layout style paint -} - -.guide-programNameCaret, -.guideProgramName { - display: -webkit-box; - display: -webkit-flex; - -webkit-box-align: center + margin: 0 !important; } .guideProgramName { - padding: 0 .7em; + padding: 0 .7em 0; overflow: hidden; text-overflow: ellipsis; - -webkit-align-items: center; align-items: center; display: flex; position: relative; - -webkit-box-flex: 1; - -webkit-flex-grow: 1; - flex-grow: 1 + flex-grow: 1; + contain: layout style paint; + /*transition: transform 60ms ease-out;*/ } .guide-programNameCaret { display: flex; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; - font-size: 200% + font-size: 200%; } .guideProgramNameText { margin: 0; - font-weight: 400; + font-weight: normal; overflow: hidden; - text-overflow: ellipsis + text-overflow: ellipsis; } .guideProgramSecondaryInfo { - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - margin-top: .1em + margin-top: .1em; } .programIcon { @@ -387,26 +298,22 @@ width: 1em; font-size: 1.6em; color: #ddd; - -webkit-flex-shrink: 0; flex-shrink: 0; - -webkit-box-flex: 0; - -webkit-flex-grow: 0; - flex-grow: 0 + flex-grow: 0; } .guide-programTextIcon { - font-weight: 700; + font-weight: bold; font-size: .9em; padding: .16em .3em; - -webkit-border-radius: .25em; border-radius: .25em; margin-right: .35em; width: auto; - height: auto + height: auto; } .guide-programTextIcon-tv { - font-size: .74em + font-size: .74em; } .guideChannelNumber { @@ -414,8 +321,8 @@ max-width: 30%; text-overflow: ellipsis; overflow: hidden; - font-weight: 400; - margin: 0 + font-weight: normal; + margin: 0; } .guideChannelName { @@ -423,7 +330,7 @@ margin-right: 1em; text-overflow: ellipsis; overflow: hidden; - max-width: 70% + max-width: 70%; } .guideChannelImage { @@ -432,68 +339,62 @@ top: 15%; bottom: 15%; width: 40%; - -webkit-background-size: contain; background-size: contain; background-repeat: no-repeat; - background-position: right center + background-position: right center; } -@media all and (min-width:62.5em) { +@media all and (min-width: 62.5em) { + .guideChannelName { - max-width: 40% + max-width: 40%; } } -@media all and (max-width:62.5em) { +@media all and (max-width: 62.5em) { + .guideChannelNumber { - display: none + display: none; } .guideChannelImage { - width: 70% + width: 70%; } } .channelsContainer { - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-flex-shrink: 0; flex-shrink: 0; - -webkit-flex-direction: column; - flex-direction: column + flex-direction: column; } -.guide-channelHeaderCell, -.programCell { - outline: 0 !important +.channelsContainer, .programGrid { + contain: layout style paint; } -.seriesTimerIcon, -.timerIcon { - color: #c33 !important +.programCell, .guide-channelHeaderCell { + outline: none !important; +} + +.timerIcon, .seriesTimerIcon { + color: #cc3333 !important; } .seriesTimerIcon-inactive { color: inherit !important; - opacity: .7 + opacity: .7; } .guideOptions { - -webkit-flex-shrink: 0; flex-shrink: 0; - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; - align-items: center + align-items: center; } -@media all and (max-width:50em), -all and (max-height:37.5em) { +@media all and (max-width: 50em), all and (max-height: 37.5em) { + .tvGuideHeader { - padding-left: 0 + padding-left: 0; } } @@ -501,31 +402,29 @@ all and (max-height:37.5em) { margin: 1em auto; text-align: center; padding: 1em; - -webkit-flex-shrink: 0; - flex-shrink: 0 + flex-shrink: 0; } .noRubberBanding { - padding-bottom: 7em + /* This is needed to combat the rubber banding in iOS */ + padding-bottom: 7em; } .guideDateTabsSlider { - text-align: center + text-align: center; } .guide-date-tab-button { padding: .3em .7em !important; margin: 0 .3em !important; - font-weight: 400 + font-weight: normal; } -.guide-date-tab-button.emby-tab-button-active { - border-color: transparent !important -} + .guide-date-tab-button.emby-tab-button-active { + border-color: transparent !important; + } -.guide-date-tab-button.emby-button-tv:focus { - -webkit-border-radius: .15em !important; - border-radius: .15em !important; - -webkit-transform: none !important; - transform: none !important -} \ No newline at end of file + .guide-date-tab-button.emby-button-tv:focus { + border-radius: .15em !important; + transform: none !important; + } diff --git a/src/bower_components/emby-webcomponents/guide/guide.js b/src/bower_components/emby-webcomponents/guide/guide.js index 87fcaf8218..0924339ff2 100644 --- a/src/bower_components/emby-webcomponents/guide/guide.js +++ b/src/bower_components/emby-webcomponents/guide/guide.js @@ -1,517 +1,1290 @@ -define(["require", "inputManager", "browser", "globalize", "connectionManager", "scrollHelper", "serverNotifications", "loading", "datetime", "focusManager", "playbackManager", "userSettings", "imageLoader", "events", "layoutManager", "itemShortcuts", "dom", "css!./guide.css", "programStyles", "material-icons", "scrollStyles", "emby-button", "paper-icon-button-light", "emby-tabs", "emby-scroller", "flexStyles", "registerElement"], function(require, inputManager, browser, globalize, connectionManager, scrollHelper, serverNotifications, loading, datetime, focusManager, playbackManager, userSettings, imageLoader, events, layoutManager, itemShortcuts, dom) { - "use strict"; +define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager', 'scrollHelper', 'serverNotifications', 'loading', 'datetime', 'focusManager', 'playbackManager', 'userSettings', 'imageLoader', 'events', 'layoutManager', 'itemShortcuts', 'dom', 'css!./guide.css', 'programStyles', 'material-icons', 'scrollStyles', 'emby-button', 'paper-icon-button-light', 'emby-tabs', 'emby-scroller', 'flexStyles', 'registerElement'], function (require, inputManager, browser, globalize, connectionManager, scrollHelper, serverNotifications, loading, datetime, focusManager, playbackManager, userSettings, imageLoader, events, layoutManager, itemShortcuts, dom) { + 'use strict'; function showViewSettings(instance) { - require(["guide-settings-dialog"], function(guideSettingsDialog) { - guideSettingsDialog.show(instance.categoryOptions).then(function() { - instance.refresh() - }) - }) + + require(['guide-settings-dialog'], function (guideSettingsDialog) { + guideSettingsDialog.show(instance.categoryOptions).then(function () { + instance.refresh(); + }); + }); } function updateProgramCellOnScroll(cell, scrollPct) { + var left = cell.posLeft; - left || (left = parseFloat(cell.style.left.replace("%", "")), cell.posLeft = left); + if (!left) { + left = parseFloat(cell.style.left.replace('%', '')); + cell.posLeft = left; + } var width = cell.posWidth; - width || (width = parseFloat(cell.style.width.replace("%", "")), cell.posWidth = width); - var right = left + width, - newPct = Math.max(Math.min(scrollPct, right), left), - offset = newPct - left, - pctOfWidth = offset / width * 100, - guideProgramName = cell.guideProgramName; - guideProgramName || (guideProgramName = cell.querySelector(".guideProgramName"), cell.guideProgramName = guideProgramName); + if (!width) { + width = parseFloat(cell.style.width.replace('%', '')); + cell.posWidth = width; + } + + var right = left + width; + var newPct = Math.max(Math.min(scrollPct, right), left); + + var offset = newPct - left; + var pctOfWidth = (offset / width) * 100; + + //console.log(pctOfWidth); + var guideProgramName = cell.guideProgramName; + if (!guideProgramName) { + guideProgramName = cell.querySelector('.guideProgramName'); + cell.guideProgramName = guideProgramName; + } + var caret = cell.caret; - caret || (caret = cell.querySelector(".guide-programNameCaret"), cell.caret = caret), guideProgramName && (pctOfWidth > 0 && pctOfWidth <= 100 ? (guideProgramName.style.transform = "translateX(" + pctOfWidth + "%)", caret.classList.remove("hide")) : (guideProgramName.style.transform = "none", caret.classList.add("hide"))) + if (!caret) { + caret = cell.querySelector('.guide-programNameCaret'); + cell.caret = caret; + } + + if (guideProgramName) { + if (pctOfWidth > 0 && pctOfWidth <= 100) { + //guideProgramName.style.marginLeft = pctOfWidth + '%'; + guideProgramName.style.transform = 'translateX(' + pctOfWidth + '%)'; + caret.classList.remove('hide'); + } else { + //guideProgramName.style.marginLeft = '0'; + guideProgramName.style.transform = 'none'; + caret.classList.add('hide'); + } + } } + var isUpdatingProgramCellScroll = false; function updateProgramCellsOnScroll(programGrid, programCells) { - isUpdatingProgramCellScroll || (isUpdatingProgramCellScroll = !0, requestAnimationFrame(function() { - for (var scrollLeft = programGrid.scrollLeft, scrollPct = scrollLeft ? scrollLeft / programGrid.scrollWidth * 100 : 0, i = 0, length = programCells.length; i < length; i++) updateProgramCellOnScroll(programCells[i], scrollPct); - isUpdatingProgramCellScroll = !1 - })) + + if (isUpdatingProgramCellScroll) { + return; + } + + isUpdatingProgramCellScroll = true; + + requestAnimationFrame(function () { + + var scrollLeft = programGrid.scrollLeft; + + var scrollPct = scrollLeft ? (scrollLeft / programGrid.scrollWidth) * 100 : 0; + + for (var i = 0, length = programCells.length; i < length; i++) { + + updateProgramCellOnScroll(programCells[i], scrollPct); + } + + isUpdatingProgramCellScroll = false; + }); } function onProgramGridClick(e) { - if (layoutManager.tv) { - var programCell = dom.parentWithClass(e.target, "programCell"); - if (programCell) { - var startDate = programCell.getAttribute("data-startdate"), - endDate = programCell.getAttribute("data-enddate"); - startDate = datetime.parseISO8601Date(startDate, { - toLocal: !0 - }).getTime(), endDate = datetime.parseISO8601Date(endDate, { - toLocal: !0 - }).getTime(); - var now = (new Date).getTime(); - if (now >= startDate && now < endDate) { - var channelId = programCell.getAttribute("data-channelid"), - serverId = programCell.getAttribute("data-serverid"); - e.preventDefault(), e.stopPropagation(), playbackManager.play({ - ids: [channelId], - serverId: serverId - }) - } + + if (!layoutManager.tv) { + return; + } + + var programCell = dom.parentWithClass(e.target, 'programCell'); + if (programCell) { + + var startDate = programCell.getAttribute('data-startdate'); + var endDate = programCell.getAttribute('data-enddate'); + startDate = datetime.parseISO8601Date(startDate, { toLocal: true }).getTime(); + endDate = datetime.parseISO8601Date(endDate, { toLocal: true }).getTime(); + + var now = new Date().getTime(); + if (now >= startDate && now < endDate) { + + var channelId = programCell.getAttribute('data-channelid'); + var serverId = programCell.getAttribute('data-serverid'); + + e.preventDefault(); + e.stopPropagation(); + + playbackManager.play({ + ids: [channelId], + serverId: serverId + }); } } } function Guide(options) { - function restartAutoRefresh() { + + var self = this; + var items = {}; + + self.options = options; + self.categoryOptions = { categories: [] }; + + // 30 mins + var cellCurationMinutes = 30; + var cellDurationMs = cellCurationMinutes * 60 * 1000; + var msPerDay = 86400000; + var totalRendererdMs = msPerDay; + + var currentDate; + var currentStartIndex = 0; + var currentChannelLimit = 0; + var autoRefreshInterval; + var programCells; + var lastFocusDirection; + var programGrid; + + self.refresh = function () { + + currentDate = null; + reloadPage(options.element); + restartAutoRefresh(); + }; + + self.pause = function () { stopAutoRefresh(); - autoRefreshInterval = setInterval(function() { - self.refresh() - }, 9e5) + }; + + self.resume = function (refreshData) { + if (refreshData) { + self.refresh(); + } else { + restartAutoRefresh(); + } + }; + + self.destroy = function () { + + stopAutoRefresh(); + + events.off(serverNotifications, 'TimerCreated', onTimerCreated); + events.off(serverNotifications, 'SeriesTimerCreated', onSeriesTimerCreated); + events.off(serverNotifications, 'TimerCancelled', onTimerCancelled); + events.off(serverNotifications, 'SeriesTimerCancelled', onSeriesTimerCancelled); + + setScrollEvents(options.element, false); + itemShortcuts.off(options.element); + items = {}; + }; + + function restartAutoRefresh() { + + stopAutoRefresh(); + + var intervalMs = 60000 * 15; // (minutes) + + autoRefreshInterval = setInterval(function () { + self.refresh(); + }, intervalMs); } function stopAutoRefresh() { - autoRefreshInterval && (clearInterval(autoRefreshInterval), autoRefreshInterval = null) + if (autoRefreshInterval) { + clearInterval(autoRefreshInterval); + autoRefreshInterval = null; + } } function normalizeDateToTimeslot(date) { - return date.getMinutes() - cellCurationMinutes >= 0 ? date.setHours(date.getHours(), cellCurationMinutes, 0, 0) : date.setHours(date.getHours(), 0, 0, 0), date + + var minutesOffset = date.getMinutes() - cellCurationMinutes; + + if (minutesOffset >= 0) { + + date.setHours(date.getHours(), cellCurationMinutes, 0, 0); + + } else { + + date.setHours(date.getHours(), 0, 0, 0); + } + + return date; } function showLoading() { - loading.show() + loading.show(); } function hideLoading() { - loading.hide() + loading.hide(); } function reloadGuide(context, newStartDate, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender) { - var apiClient = connectionManager.getApiClient(options.serverId), - channelQuery = { - StartIndex: 0, - EnableFavoriteSorting: "false" !== userSettings.get("livetv-favoritechannelsattop") - }; + + var apiClient = connectionManager.getApiClient(options.serverId); + + var channelQuery = { + + StartIndex: 0, + EnableFavoriteSorting: userSettings.get('livetv-favoritechannelsattop') !== 'false' + }; + channelQuery.UserId = apiClient.getCurrentUserId(); - currentChannelLimit = 500, showLoading(), channelQuery.StartIndex = currentStartIndex, channelQuery.Limit = 500, channelQuery.AddCurrentProgram = !1, channelQuery.EnableUserData = !1, channelQuery.EnableImageTypes = "Primary"; - var categories = self.categoryOptions.categories || [], - displayMovieContent = !categories.length || -1 !== categories.indexOf("movies"), - displaySportsContent = !categories.length || -1 !== categories.indexOf("sports"), - displayNewsContent = !categories.length || -1 !== categories.indexOf("news"), - displayKidsContent = !categories.length || -1 !== categories.indexOf("kids"), - displaySeriesContent = !categories.length || -1 !== categories.indexOf("series"); - displayMovieContent && displaySportsContent && displayNewsContent && displayKidsContent ? (channelQuery.IsMovie = null, channelQuery.IsSports = null, channelQuery.IsKids = null, channelQuery.IsNews = null, channelQuery.IsSeries = null) : (displayNewsContent && (channelQuery.IsNews = !0), displaySportsContent && (channelQuery.IsSports = !0), displayKidsContent && (channelQuery.IsKids = !0), displayMovieContent && (channelQuery.IsMovie = !0), displaySeriesContent && (channelQuery.IsSeries = !0)), "DatePlayed" === userSettings.get("livetv-channelorder") ? (channelQuery.SortBy = "DatePlayed", channelQuery.SortOrder = "Descending") : (channelQuery.SortBy = null, channelQuery.SortOrder = null); + + var channelLimit = 500; + currentChannelLimit = channelLimit; + + showLoading(); + + channelQuery.StartIndex = currentStartIndex; + channelQuery.Limit = channelLimit; + channelQuery.AddCurrentProgram = false; + channelQuery.EnableUserData = false; + channelQuery.EnableImageTypes = "Primary"; + + var categories = self.categoryOptions.categories || []; + var displayMovieContent = !categories.length || categories.indexOf('movies') !== -1; + var displaySportsContent = !categories.length || categories.indexOf('sports') !== -1; + var displayNewsContent = !categories.length || categories.indexOf('news') !== -1; + var displayKidsContent = !categories.length || categories.indexOf('kids') !== -1; + var displaySeriesContent = !categories.length || categories.indexOf('series') !== -1; + + if (displayMovieContent && displaySportsContent && displayNewsContent && displayKidsContent) { + channelQuery.IsMovie = null; + channelQuery.IsSports = null; + channelQuery.IsKids = null; + channelQuery.IsNews = null; + channelQuery.IsSeries = null; + } else { + if (displayNewsContent) { + channelQuery.IsNews = true; + } + if (displaySportsContent) { + channelQuery.IsSports = true; + } + if (displayKidsContent) { + channelQuery.IsKids = true; + } + if (displayMovieContent) { + channelQuery.IsMovie = true; + } + if (displaySeriesContent) { + channelQuery.IsSeries = true; + } + } + + if (userSettings.get('livetv-channelorder') === 'DatePlayed') { + channelQuery.SortBy = "DatePlayed"; + channelQuery.SortOrder = "Descending"; + } else { + channelQuery.SortBy = null; + channelQuery.SortOrder = null; + } + var date = newStartDate; - date = new Date(date.getTime() + 1e3); - var nextDay = new Date(date.getTime() + msPerDay - 2e3), - allowIndicators = dom.getWindowSize().innerWidth >= 600, - renderOptions = { - showHdIcon: allowIndicators && "true" === userSettings.get("guide-indicator-hd"), - showLiveIndicator: allowIndicators && "false" !== userSettings.get("guide-indicator-live"), - showPremiereIndicator: allowIndicators && "false" !== userSettings.get("guide-indicator-premiere"), - showNewIndicator: allowIndicators && "false" !== userSettings.get("guide-indicator-new"), - showRepeatIndicator: allowIndicators && "true" === userSettings.get("guide-indicator-repeat"), - showEpisodeTitle: !layoutManager.tv + // Add one second to avoid getting programs that are just ending + date = new Date(date.getTime() + 1000); + + // Subtract to avoid getting programs that are starting when the grid ends + var nextDay = new Date(date.getTime() + msPerDay - 2000); + + // Normally we'd want to just let responsive css handle this, + // but since mobile browsers are often underpowered, + // it can help performance to get them out of the markup + var allowIndicators = dom.getWindowSize().innerWidth >= 600; + + var renderOptions = { + showHdIcon: allowIndicators && userSettings.get('guide-indicator-hd') === 'true', + showLiveIndicator: allowIndicators && userSettings.get('guide-indicator-live') !== 'false', + showPremiereIndicator: allowIndicators && userSettings.get('guide-indicator-premiere') !== 'false', + showNewIndicator: allowIndicators && userSettings.get('guide-indicator-new') !== 'false', + showRepeatIndicator: allowIndicators && userSettings.get('guide-indicator-repeat') === 'true', + showEpisodeTitle: layoutManager.tv ? false : true + }; + + apiClient.getLiveTvChannels(channelQuery).then(function (channelsResult) { + + var btnPreviousPage = context.querySelector('.btnPreviousPage'); + var btnNextPage = context.querySelector('.btnNextPage'); + + if (channelsResult.TotalRecordCount > channelLimit) { + + context.querySelector('.guideOptions').classList.remove('hide'); + + btnPreviousPage.classList.remove('hide'); + btnNextPage.classList.remove('hide'); + + if (channelQuery.StartIndex) { + context.querySelector('.btnPreviousPage').disabled = false; + } else { + context.querySelector('.btnPreviousPage').disabled = true; + } + + if ((channelQuery.StartIndex + channelLimit) < channelsResult.TotalRecordCount) { + btnNextPage.disabled = false; + } else { + btnNextPage.disabled = true; + } + + } else { + context.querySelector('.guideOptions').classList.add('hide'); + } + + var programFields = []; + + var programQuery = { + UserId: apiClient.getCurrentUserId(), + MaxStartDate: nextDay.toISOString(), + MinEndDate: date.toISOString(), + channelIds: channelsResult.Items.map(function (c) { + return c.Id; + }).join(','), + ImageTypeLimit: 1, + EnableImages: false, + //EnableImageTypes: layoutManager.tv ? "Primary,Backdrop" : "Primary", + SortBy: "StartDate", + EnableTotalRecordCount: false, + EnableUserData: false }; - apiClient.getLiveTvChannels(channelQuery).then(function(channelsResult) { - var btnPreviousPage = context.querySelector(".btnPreviousPage"), - btnNextPage = context.querySelector(".btnNextPage"); - channelsResult.TotalRecordCount > 500 ? (context.querySelector(".guideOptions").classList.remove("hide"), btnPreviousPage.classList.remove("hide"), btnNextPage.classList.remove("hide"), channelQuery.StartIndex ? context.querySelector(".btnPreviousPage").disabled = !1 : context.querySelector(".btnPreviousPage").disabled = !0, channelQuery.StartIndex + 500 < channelsResult.TotalRecordCount ? btnNextPage.disabled = !1 : btnNextPage.disabled = !0) : context.querySelector(".guideOptions").classList.add("hide"); - var programFields = [], - programQuery = { - UserId: apiClient.getCurrentUserId(), - MaxStartDate: nextDay.toISOString(), - MinEndDate: date.toISOString(), - channelIds: channelsResult.Items.map(function(c) { - return c.Id - }).join(","), - ImageTypeLimit: 1, - EnableImages: !1, - SortBy: "StartDate", - EnableTotalRecordCount: !1, - EnableUserData: !1 - }; - renderOptions.showHdIcon && programFields.push("IsHD"), programFields.length && (programQuery.Fields = programFields.join("")), apiClient.getLiveTvPrograms(programQuery).then(function(programsResult) { - renderGuide(context, date, channelsResult.Items, programsResult.Items, renderOptions, apiClient, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender), hideLoading() - }) - }) + + if (renderOptions.showHdIcon) { + programFields.push('IsHD'); + } + + if (programFields.length) { + programQuery.Fields = programFields.join(''); + } + + apiClient.getLiveTvPrograms(programQuery).then(function (programsResult) { + + renderGuide(context, date, channelsResult.Items, programsResult.Items, renderOptions, apiClient, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender); + + hideLoading(); + + }); + }); } function getDisplayTime(date) { - if ("string" === (typeof date).toString().toLowerCase()) try { - date = datetime.parseISO8601Date(date, { - toLocal: !0 - }) - } catch (err) { - return date + + if ((typeof date).toString().toLowerCase() === 'string') { + try { + + date = datetime.parseISO8601Date(date, { toLocal: true }); + + } catch (err) { + return date; + } } - return datetime.getDisplayTime(date).toLowerCase() + + return datetime.getDisplayTime(date).toLowerCase(); } function getTimeslotHeadersHtml(startDate, endDateTime) { - var html = ""; - for (startDate = new Date(startDate.getTime()), html += '
'; startDate.getTime() < endDateTime;) html += '
', html += getDisplayTime(startDate), html += "
", startDate.setTime(startDate.getTime() + cellDurationMs); - return html + + var html = ''; + + // clone + startDate = new Date(startDate.getTime()); + + html += '
'; + + while (startDate.getTime() < endDateTime) { + + html += '
'; + + html += getDisplayTime(startDate); + html += '
'; + + // Add 30 mins + startDate.setTime(startDate.getTime() + cellDurationMs); + } + + return html; } function parseDates(program) { - if (!program.StartDateLocal) try { - program.StartDateLocal = datetime.parseISO8601Date(program.StartDate, { - toLocal: !0 - }) - } catch (err) {} - if (!program.EndDateLocal) try { - program.EndDateLocal = datetime.parseISO8601Date(program.EndDate, { - toLocal: !0 - }) - } catch (err) {} - return null + + if (!program.StartDateLocal) { + try { + + program.StartDateLocal = datetime.parseISO8601Date(program.StartDate, { toLocal: true }); + + } catch (err) { + + } + + } + + if (!program.EndDateLocal) { + try { + + program.EndDateLocal = datetime.parseISO8601Date(program.EndDate, { toLocal: true }); + + } catch (err) { + + } + + } + + return null; } function getTimerIndicator(item) { + var status; - if ("SeriesTimer" === item.Type) return ''; - if (item.TimerId || item.SeriesTimerId) status = item.Status || "Cancelled"; - else { - if ("Timer" !== item.Type) return ""; - status = item.Status + + if (item.Type === 'SeriesTimer') { + return ''; } - return item.SeriesTimerId ? "Cancelled" !== status ? '' : '' : '' + else if (item.TimerId || item.SeriesTimerId) { + + status = item.Status || 'Cancelled'; + } + else if (item.Type === 'Timer') { + + status = item.Status; + } + else { + return ''; + } + + if (item.SeriesTimerId) { + + if (status !== 'Cancelled') { + return ''; + } + + return ''; + } + + return ''; } function getChannelProgramsHtml(context, date, channel, programs, options, listInfo) { - var html = "", - startMs = date.getTime(), - endMs = startMs + msPerDay - 1; - html += '
'; - for (var programsFound, clickAction = layoutManager.tv ? "link" : "programdialog", categories = self.categoryOptions.categories || [], displayMovieContent = !categories.length || -1 !== categories.indexOf("movies"), displaySportsContent = !categories.length || -1 !== categories.indexOf("sports"), displayNewsContent = !categories.length || -1 !== categories.indexOf("news"), displayKidsContent = !categories.length || -1 !== categories.indexOf("kids"), displaySeriesContent = !categories.length || -1 !== categories.indexOf("series"), enableColorCodedBackgrounds = "true" === userSettings.get("guide-colorcodedbackgrounds"), now = (new Date).getTime(), i = listInfo.startIndex, length = programs.length; i < length; i++) { + + var html = ''; + + var startMs = date.getTime(); + var endMs = startMs + msPerDay - 1; + + var outerCssClass = layoutManager.tv ? 'channelPrograms channelPrograms-tv' : 'channelPrograms'; + + html += '
'; + + var clickAction = layoutManager.tv ? 'link' : 'programdialog'; + + var categories = self.categoryOptions.categories || []; + var displayMovieContent = !categories.length || categories.indexOf('movies') !== -1; + var displaySportsContent = !categories.length || categories.indexOf('sports') !== -1; + var displayNewsContent = !categories.length || categories.indexOf('news') !== -1; + var displayKidsContent = !categories.length || categories.indexOf('kids') !== -1; + var displaySeriesContent = !categories.length || categories.indexOf('series') !== -1; + var enableColorCodedBackgrounds = userSettings.get('guide-colorcodedbackgrounds') === 'true'; + + var programsFound; + var now = new Date().getTime(); + + for (var i = listInfo.startIndex, length = programs.length; i < length; i++) { + var program = programs[i]; - if (program.ChannelId === channel.Id) { - programsFound = !0, listInfo.startIndex++, parseDates(program); - var startDateLocalMs = program.StartDateLocal.getTime(), - endDateLocalMs = program.EndDateLocal.getTime(); - if (!(endDateLocalMs < startMs)) { - if (startDateLocalMs > endMs) break; - items[program.Id] = program; - var renderStartMs = Math.max(startDateLocalMs, startMs), - startPercent = (startDateLocalMs - startMs) / msPerDay; - startPercent *= 100, startPercent = Math.max(startPercent, 0); - var renderEndMs = Math.min(endDateLocalMs, endMs), - endPercent = (renderEndMs - renderStartMs) / msPerDay; - endPercent *= 100; - var cssClass = "programCell itemAction", - accentCssClass = null, - displayInnerContent = !0; - program.IsKids ? (displayInnerContent = displayKidsContent, accentCssClass = "kids") : program.IsSports ? (displayInnerContent = displaySportsContent, accentCssClass = "sports") : program.IsNews ? (displayInnerContent = displayNewsContent, accentCssClass = "news") : program.IsMovie ? (displayInnerContent = displayMovieContent, accentCssClass = "movie") : displayInnerContent = program.IsSeries ? displaySeriesContent : displayMovieContent && displayNewsContent && displaySportsContent && displayKidsContent && displaySeriesContent, displayInnerContent && enableColorCodedBackgrounds && accentCssClass && (cssClass += " programCell-" + accentCssClass), now >= startDateLocalMs && now < endDateLocalMs && (cssClass += " programCell-active"); - var timerAttributes = ""; - program.TimerId && (timerAttributes += ' data-timerid="' + program.TimerId + '"'), program.SeriesTimerId && (timerAttributes += ' data-seriestimerid="' + program.SeriesTimerId + '"'); - if (html += "= 2 ? ' is="emby-programcell"' : "") + ' data-action="' + clickAction + '"' + timerAttributes + ' data-channelid="' + program.ChannelId + '" data-id="' + program.Id + '" data-serverid="' + program.ServerId + '" data-startdate="' + program.StartDate + '" data-enddate="' + program.EndDate + '" data-type="' + program.Type + '" class="' + cssClass + '" style="left:' + startPercent + "%;width:" + endPercent + '%;">', displayInnerContent) { - html += '
', html += '
', html += '
' + program.Name; - var indicatorHtml = null; - program.IsLive && options.showLiveIndicator ? indicatorHtml = '' + globalize.translate("sharedcomponents#Live") + "" : program.IsPremiere && options.showPremiereIndicator ? indicatorHtml = '' + globalize.translate("sharedcomponents#Premiere") + "" : program.IsSeries && !program.IsRepeat && options.showNewIndicator ? indicatorHtml = '' + globalize.translate("sharedcomponents#AttributeNew") + "" : program.IsSeries && program.IsRepeat && options.showRepeatIndicator && (indicatorHtml = '' + globalize.translate("sharedcomponents#Repeat") + ""), html += indicatorHtml || "", program.EpisodeTitle && options.showEpisodeTitle && (html += '
', program.EpisodeTitle && options.showEpisodeTitle && (html += '' + program.EpisodeTitle + ""), html += "
"), html += "
", program.IsHD && options.showHdIcon && (layoutManager.tv ? html += '
HD
' : html += '
HD
'), html += getTimerIndicator(program), html += "
" - } - html += "" + + if (program.ChannelId !== channel.Id) { + + if (programsFound) { + break; } - } else if (programsFound) break + + continue; + } + + programsFound = true; + listInfo.startIndex++; + + parseDates(program); + + var startDateLocalMs = program.StartDateLocal.getTime(); + var endDateLocalMs = program.EndDateLocal.getTime(); + + if (endDateLocalMs < startMs) { + continue; + } + + if (startDateLocalMs > endMs) { + break; + } + + items[program.Id] = program; + + var renderStartMs = Math.max(startDateLocalMs, startMs); + var startPercent = (startDateLocalMs - startMs) / msPerDay; + startPercent *= 100; + startPercent = Math.max(startPercent, 0); + + var renderEndMs = Math.min(endDateLocalMs, endMs); + var endPercent = (renderEndMs - renderStartMs) / msPerDay; + endPercent *= 100; + + var cssClass = "programCell itemAction"; + var accentCssClass = null; + var displayInnerContent = true; + + if (program.IsKids) { + displayInnerContent = displayKidsContent; + accentCssClass = 'kids'; + } else if (program.IsSports) { + displayInnerContent = displaySportsContent; + accentCssClass = 'sports'; + } else if (program.IsNews) { + displayInnerContent = displayNewsContent; + accentCssClass = 'news'; + } else if (program.IsMovie) { + displayInnerContent = displayMovieContent; + accentCssClass = 'movie'; + } + else if (program.IsSeries) { + displayInnerContent = displaySeriesContent; + } + else { + displayInnerContent = displayMovieContent && displayNewsContent && displaySportsContent && displayKidsContent && displaySeriesContent; + } + + if (displayInnerContent && enableColorCodedBackgrounds && accentCssClass) { + cssClass += " programCell-" + accentCssClass; + } + + if (now >= startDateLocalMs && now < endDateLocalMs) { + cssClass += " programCell-active"; + } + + var timerAttributes = ''; + if (program.TimerId) { + timerAttributes += ' data-timerid="' + program.TimerId + '"'; + } + if (program.SeriesTimerId) { + timerAttributes += ' data-seriestimerid="' + program.SeriesTimerId + '"'; + } + + var isAttribute = endPercent >= 2 ? ' is="emby-programcell"' : ''; + + html += ''; + + if (displayInnerContent) { + var guideProgramNameClass = "guideProgramName"; + + html += '
'; + + html += '
'; + + html += '
' + program.Name; + + var indicatorHtml = null; + if (program.IsLive && options.showLiveIndicator) { + indicatorHtml = '' + globalize.translate('sharedcomponents#Live') + ''; + } + else if (program.IsPremiere && options.showPremiereIndicator) { + indicatorHtml = '' + globalize.translate('sharedcomponents#Premiere') + ''; + } + else if (program.IsSeries && !program.IsRepeat && options.showNewIndicator) { + indicatorHtml = '' + globalize.translate('sharedcomponents#AttributeNew') + ''; + } + else if (program.IsSeries && program.IsRepeat && options.showRepeatIndicator) { + indicatorHtml = '' + globalize.translate('sharedcomponents#Repeat') + ''; + } + html += indicatorHtml || ''; + + if ((program.EpisodeTitle && options.showEpisodeTitle)) { + html += '
'; + + if (program.EpisodeTitle && options.showEpisodeTitle) { + html += '' + program.EpisodeTitle + ''; + } + html += '
'; + } + + html += '
'; + + if (program.IsHD && options.showHdIcon) { + //html += 'hd'; + if (layoutManager.tv) { + html += '
HD
'; + } else { + html += '
HD
'; + } + } + + html += getTimerIndicator(program); + + html += '
'; + } + + html += ''; } - return html += "
" + + html += '
'; + + return html; } + function renderChannelHeaders(context, channels, apiClient) { - for (var html = "", i = 0, length = channels.length; i < length; i++) { - var channel = channels[i], - hasChannelImage = channel.ImageTags.Primary, - cssClass = "guide-channelHeaderCell itemAction"; - layoutManager.tv && (cssClass += " guide-channelHeaderCell-tv"); + + var html = ''; + + for (var i = 0, length = channels.length; i < length; i++) { + + var channel = channels[i]; + var hasChannelImage = channel.ImageTags.Primary; + + var cssClass = 'guide-channelHeaderCell itemAction'; + + if (layoutManager.tv) { + cssClass += ' guide-channelHeaderCell-tv'; + } + var title = []; - if (channel.ChannelNumber && title.push(channel.ChannelNumber), channel.Name && title.push(channel.Name), html += '" + + if (channel.ChannelNumber) { + + html += '

' + channel.ChannelNumber + '

'; + } + + if (!hasChannelImage && channel.Name) { + html += '
' + channel.Name + '
'; + } + + html += ''; } - var channelList = context.querySelector(".channelsContainer"); - channelList.innerHTML = html, imageLoader.lazyChildren(channelList) + + var channelList = context.querySelector('.channelsContainer'); + channelList.innerHTML = html; + imageLoader.lazyChildren(channelList); } function renderPrograms(context, date, channels, programs, options) { - for (var listInfo = { - startIndex: 0 - }, html = [], i = 0, length = channels.length; i < length; i++) html.push(getChannelProgramsHtml(context, date, channels[i], programs, options, listInfo)); - programGrid.innerHTML = html.join(""), programCells = programGrid.querySelectorAll("[is=emby-programcell]"), updateProgramCellsOnScroll(programGrid, programCells) + + var listInfo = { + startIndex: 0 + }; + + var html = []; + + for (var i = 0, length = channels.length; i < length; i++) { + + html.push(getChannelProgramsHtml(context, date, channels[i], programs, options, listInfo)); + } + + programGrid.innerHTML = html.join(''); + + programCells = programGrid.querySelectorAll('[is=emby-programcell]'); + + updateProgramCellsOnScroll(programGrid, programCells); } function getProgramSortOrder(program, channels) { - for (var channelId = program.ChannelId, channelIndex = -1, i = 0, length = channels.length; i < length; i++) + + var channelId = program.ChannelId; + var channelIndex = -1; + + for (var i = 0, length = channels.length; i < length; i++) { if (channelId === channels[i].Id) { channelIndex = i; - break - } return 1e7 * channelIndex + datetime.parseISO8601Date(program.StartDate, { - toLocal: !0 - }).getTime() / 6e4 + break; + } + } + + var start = datetime.parseISO8601Date(program.StartDate, { toLocal: true }); + + return (channelIndex * 10000000) + (start.getTime() / 60000); } function renderGuide(context, date, channels, programs, renderOptions, apiClient, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender) { - programs.sort(function(a, b) { - return getProgramSortOrder(a, channels) - getProgramSortOrder(b, channels) + + programs.sort(function (a, b) { + return getProgramSortOrder(a, channels) - getProgramSortOrder(b, channels); }); - var activeElement = document.activeElement, - itemId = activeElement && activeElement.getAttribute ? activeElement.getAttribute("data-id") : null, - channelRowId = null; - activeElement && (channelRowId = dom.parentWithClass(activeElement, "channelPrograms"), channelRowId = channelRowId && channelRowId.getAttribute ? channelRowId.getAttribute("data-channelid") : null), renderChannelHeaders(context, channels, apiClient); - var startDate = date, - endDate = new Date(startDate.getTime() + msPerDay); - context.querySelector(".timeslotHeaders").innerHTML = getTimeslotHeadersHtml(startDate, endDate), items = {}, renderPrograms(context, date, channels, programs, renderOptions), focusProgramOnRender && focusProgram(context, itemId, channelRowId, focusToTimeMs, startTimeOfDayMs), scrollProgramGridToTimeMs(context, scrollToTimeMs, startTimeOfDayMs) + + var activeElement = document.activeElement; + var itemId = activeElement && activeElement.getAttribute ? activeElement.getAttribute('data-id') : null; + var channelRowId = null; + + if (activeElement) { + channelRowId = dom.parentWithClass(activeElement, 'channelPrograms'); + channelRowId = channelRowId && channelRowId.getAttribute ? channelRowId.getAttribute('data-channelid') : null; + } + + renderChannelHeaders(context, channels, apiClient); + + var startDate = date; + var endDate = new Date(startDate.getTime() + msPerDay); + context.querySelector('.timeslotHeaders').innerHTML = getTimeslotHeadersHtml(startDate, endDate); + items = {}; + renderPrograms(context, date, channels, programs, renderOptions); + + if (focusProgramOnRender) { + focusProgram(context, itemId, channelRowId, focusToTimeMs, startTimeOfDayMs); + } + + scrollProgramGridToTimeMs(context, scrollToTimeMs, startTimeOfDayMs); } function scrollProgramGridToTimeMs(context, scrollToTimeMs, startTimeOfDayMs) { + scrollToTimeMs -= startTimeOfDayMs; + var pct = scrollToTimeMs / msPerDay; + programGrid.scrollTop = 0; + var scrollPos = pct * programGrid.scrollWidth; - nativeScrollTo(programGrid, scrollPos, !0) + + nativeScrollTo(programGrid, scrollPos, true); } function focusProgram(context, itemId, channelRowId, focusToTimeMs, startTimeOfDayMs) { + var focusElem; - if (itemId && (focusElem = context.querySelector('[data-id="' + itemId + '"]')), focusElem) focusManager.focus(focusElem); - else { + if (itemId) { + focusElem = context.querySelector('[data-id="' + itemId + '"]'); + } + + if (focusElem) { + focusManager.focus(focusElem); + } else { + var autoFocusParent; - channelRowId && (autoFocusParent = context.querySelector('[data-channelid="' + channelRowId + '"]')), autoFocusParent || (autoFocusParent = programGrid), focusToTimeMs -= startTimeOfDayMs; - for (var pct = focusToTimeMs / msPerDay * 100, programCell = autoFocusParent.querySelector(".programCell"); programCell;) { - var left = (programCell.style.left || "").replace("%", ""); - left = left ? parseFloat(left) : 0; - var width = (programCell.style.width || "").replace("%", ""); - if (width = width ? parseFloat(width) : 0, left >= pct || left + width >= pct) break; - programCell = programCell.nextSibling + + if (channelRowId) { + autoFocusParent = context.querySelector('[data-channelid="' + channelRowId + '"]'); + } + + if (!autoFocusParent) { + autoFocusParent = programGrid; + } + + focusToTimeMs -= startTimeOfDayMs; + + var pct = (focusToTimeMs / msPerDay) * 100; + + var programCell = autoFocusParent.querySelector('.programCell'); + + while (programCell) { + + var left = (programCell.style.left || '').replace('%', ''); + left = left ? parseFloat(left) : 0; + var width = (programCell.style.width || '').replace('%', ''); + width = width ? parseFloat(width) : 0; + + if (left >= pct || (left + width) >= pct) { + break; + } + programCell = programCell.nextSibling; + } + + if (programCell) { + focusManager.focus(programCell); + } else { + focusManager.autoFocus(autoFocusParent, true); } - programCell ? focusManager.focus(programCell) : focusManager.autoFocus(autoFocusParent, !0) } } function nativeScrollTo(container, pos, horizontal) { - container.scrollTo ? horizontal ? container.scrollTo(pos, 0) : container.scrollTo(0, pos) : horizontal ? container.scrollLeft = Math.round(pos) : container.scrollTop = Math.round(pos) + + if (container.scrollTo) { + if (horizontal) { + container.scrollTo(pos, 0); + } else { + container.scrollTo(0, pos); + } + } else { + if (horizontal) { + container.scrollLeft = Math.round(pos); + } else { + container.scrollTop = Math.round(pos); + } + } } + var lastGridScroll = 0; + var lastHeaderScroll = 0; + var scrollXPct = 0; function onProgramGridScroll(context, elem, timeslotHeaders) { - if ((new Date).getTime() - lastHeaderScroll >= 1e3) { - lastGridScroll = (new Date).getTime(); + + if ((new Date().getTime() - lastHeaderScroll) >= 1000) { + lastGridScroll = new Date().getTime(); + var scrollLeft = elem.scrollLeft; - scrollXPct = 100 * scrollLeft / elem.scrollWidth, nativeScrollTo(timeslotHeaders, scrollLeft, !0) + scrollXPct = (scrollLeft * 100) / elem.scrollWidth; + nativeScrollTo(timeslotHeaders, scrollLeft, true); } - updateProgramCellsOnScroll(elem, programCells) + + updateProgramCellsOnScroll(elem, programCells); } function onTimeslotHeadersScroll(context, elem) { - (new Date).getTime() - lastGridScroll >= 1e3 && (lastHeaderScroll = (new Date).getTime(), nativeScrollTo(programGrid, elem.scrollLeft, !0)) + + if ((new Date().getTime() - lastGridScroll) >= 1000) { + lastHeaderScroll = new Date().getTime(); + nativeScrollTo(programGrid, elem.scrollLeft, true); + } } function changeDate(page, date, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender) { + var newStartDate = normalizeDateToTimeslot(date); - currentDate = newStartDate, reloadGuide(page, newStartDate, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender) + currentDate = newStartDate; + + reloadGuide(page, newStartDate, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender); } function getDateTabText(date, isActive, tabIndex) { - var cssClass = isActive ? "emby-tab-button guide-date-tab-button emby-tab-button-active" : "emby-tab-button guide-date-tab-button", - html = '" + + var cssClass = isActive ? 'emby-tab-button guide-date-tab-button emby-tab-button-active' : 'emby-tab-button guide-date-tab-button'; + + var html = ''; + + return html; } function setDateRange(page, guideInfo) { - var today = new Date, - nowHours = today.getHours(); + + var today = new Date(); + var nowHours = today.getHours(); today.setHours(nowHours, 0, 0, 0); - var start = datetime.parseISO8601Date(guideInfo.StartDate, { - toLocal: !0 - }), - end = datetime.parseISO8601Date(guideInfo.EndDate, { - toLocal: !0 - }); - start.setHours(nowHours, 0, 0, 0), end.setHours(0, 0, 0, 0), start.getTime() >= end.getTime() && end.setDate(start.getDate() + 1), start = new Date(Math.max(today, start)); - var dateTabsHtml = "", - tabIndex = 0, - date = new Date; - currentDate && date.setTime(currentDate.getTime()), date.setHours(nowHours, 0, 0, 0); - var startTimeOfDayMs = 60 * start.getHours() * 60 * 1e3; - for (startTimeOfDayMs += 60 * start.getMinutes() * 1e3; start <= end;) { - dateTabsHtml += getDateTabText(start, date.getDate() === start.getDate() && date.getMonth() === start.getMonth() && date.getFullYear() === start.getFullYear(), tabIndex), start.setDate(start.getDate() + 1), start.setHours(0, 0, 0, 0), tabIndex++ + + var start = datetime.parseISO8601Date(guideInfo.StartDate, { toLocal: true }); + var end = datetime.parseISO8601Date(guideInfo.EndDate, { toLocal: true }); + + start.setHours(nowHours, 0, 0, 0); + end.setHours(0, 0, 0, 0); + + if (start.getTime() >= end.getTime()) { + end.setDate(start.getDate() + 1); } - page.querySelector(".emby-tabs-slider").innerHTML = dateTabsHtml, page.querySelector(".guideDateTabs").refresh(); - var newDate = new Date, - newDateHours = newDate.getHours(), - scrollToTimeMs = 60 * newDateHours * 60 * 1e3, - minutes = newDate.getMinutes(); - minutes >= 30 && (scrollToTimeMs += 18e5), changeDate(page, date, scrollToTimeMs, 60 * (60 * newDateHours + minutes) * 1e3, startTimeOfDayMs, layoutManager.tv) + + start = new Date(Math.max(today, start)); + + var dateTabsHtml = ''; + var tabIndex = 0; + + var date = new Date(); + + if (currentDate) { + date.setTime(currentDate.getTime()); + } + + date.setHours(nowHours, 0, 0, 0); + //start.setHours(0, 0, 0, 0); + + var startTimeOfDayMs = (start.getHours() * 60 * 60 * 1000); + startTimeOfDayMs += start.getMinutes() * 60 * 1000; + + while (start <= end) { + + var isActive = date.getDate() === start.getDate() && date.getMonth() === start.getMonth() && date.getFullYear() === start.getFullYear(); + + dateTabsHtml += getDateTabText(start, isActive, tabIndex); + + start.setDate(start.getDate() + 1); + start.setHours(0, 0, 0, 0); + tabIndex++; + } + + page.querySelector('.emby-tabs-slider').innerHTML = dateTabsHtml; + page.querySelector('.guideDateTabs').refresh(); + + var newDate = new Date(); + var newDateHours = newDate.getHours(); + var scrollToTimeMs = newDateHours * 60 * 60 * 1000; + + var minutes = newDate.getMinutes(); + if (minutes >= 30) { + scrollToTimeMs += 30 * 60 * 1000; + } + + var focusToTimeMs = ((newDateHours * 60) + minutes) * 60 * 1000; + changeDate(page, date, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, layoutManager.tv); } function reloadPage(page) { - showLoading(), connectionManager.getApiClient(options.serverId).getLiveTvGuideInfo().then(function(guideInfo) { - setDateRange(page, guideInfo) - }) + + showLoading(); + + var apiClient = connectionManager.getApiClient(options.serverId); + + apiClient.getLiveTvGuideInfo().then(function (guideInfo) { + + setDateRange(page, guideInfo); + }); } function getChannelProgramsFocusableElements(container) { - for (var elements = container.querySelectorAll(".programCell"), list = [], currentScrollXPct = scrollXPct + 1, i = 0, length = elements.length; i < length; i++) { - var elem = elements[i], - left = (elem.style.left || "").replace("%", ""); + + var elements = container.querySelectorAll('.programCell'); + + var list = []; + // add 1 to avoid programs that are out of view to the left + var currentScrollXPct = scrollXPct + 1; + + for (var i = 0, length = elements.length; i < length; i++) { + + var elem = elements[i]; + + var left = (elem.style.left || '').replace('%', ''); left = left ? parseFloat(left) : 0; - var width = (elem.style.width || "").replace("%", ""); - width = width ? parseFloat(width) : 0, left + width >= currentScrollXPct && list.push(elem) + var width = (elem.style.width || '').replace('%', ''); + width = width ? parseFloat(width) : 0; + + if ((left + width) >= currentScrollXPct) { + list.push(elem); + } } - return list + + return list; } function onInputCommand(e) { - var container, channelPrograms, focusableElements, newRow, target = e.target, - programCell = dom.parentWithClass(target, "programCell"); + + var target = e.target; + var programCell = dom.parentWithClass(target, 'programCell'); + var container; + var channelPrograms; + var focusableElements; + var newRow; + + var scrollX = false; + switch (e.detail.command) { - case "up": - programCell ? (container = programGrid, channelPrograms = dom.parentWithClass(programCell, "channelPrograms"), newRow = channelPrograms.previousSibling, newRow ? (focusableElements = getChannelProgramsFocusableElements(newRow), focusableElements.length ? container = newRow : focusableElements = null) : container = null) : container = null, lastFocusDirection = e.detail.command, focusManager.moveUp(target, { + + case 'up': + if (programCell) { + container = programGrid; + channelPrograms = dom.parentWithClass(programCell, 'channelPrograms'); + + newRow = channelPrograms.previousSibling; + if (newRow) { + focusableElements = getChannelProgramsFocusableElements(newRow); + if (focusableElements.length) { + container = newRow; + } else { + focusableElements = null; + } + } else { + container = null; + } + } else { + container = null; + } + lastFocusDirection = e.detail.command; + + focusManager.moveUp(target, { container: container, focusableElements: focusableElements }); break; - case "down": - programCell ? (container = programGrid, channelPrograms = dom.parentWithClass(programCell, "channelPrograms"), newRow = channelPrograms.nextSibling, newRow ? (focusableElements = getChannelProgramsFocusableElements(newRow), focusableElements.length ? container = newRow : focusableElements = null) : container = null) : container = null, lastFocusDirection = e.detail.command, focusManager.moveDown(target, { + case 'down': + if (programCell) { + container = programGrid; + channelPrograms = dom.parentWithClass(programCell, 'channelPrograms'); + + newRow = channelPrograms.nextSibling; + if (newRow) { + focusableElements = getChannelProgramsFocusableElements(newRow); + if (focusableElements.length) { + container = newRow; + } else { + focusableElements = null; + } + } else { + container = null; + } + } else { + container = null; + } + lastFocusDirection = e.detail.command; + + focusManager.moveDown(target, { container: container, focusableElements: focusableElements }); break; - case "left": - container = programCell ? dom.parentWithClass(programCell, "channelPrograms") : null, container && !programCell.previousSibling && (container = null), lastFocusDirection = e.detail.command, focusManager.moveLeft(target, { + case 'left': + container = programCell ? dom.parentWithClass(programCell, 'channelPrograms') : null; + // allow left outside the channelProgramsContainer when the first child is currently focused + if (container && !programCell.previousSibling) { + container = null; + } + lastFocusDirection = e.detail.command; + + focusManager.moveLeft(target, { container: container - }), !0; + }); + scrollX = true; break; - case "right": - container = programCell ? dom.parentWithClass(programCell, "channelPrograms") : null, lastFocusDirection = e.detail.command, focusManager.moveRight(target, { + case 'right': + container = programCell ? dom.parentWithClass(programCell, 'channelPrograms') : null; + lastFocusDirection = e.detail.command; + + focusManager.moveRight(target, { container: container - }), !0; + }); + scrollX = true; break; default: - return + return; } - e.preventDefault(), e.stopPropagation() + + e.preventDefault(); + e.stopPropagation(); } function onScrollerFocus(e) { - var target = e.target, - programCell = dom.parentWithClass(target, "programCell"); + + var target = e.target; + var programCell = dom.parentWithClass(target, 'programCell'); + if (programCell) { - var focused = target, - id = focused.getAttribute("data-id"), - item = items[id]; - item && events.trigger(self, "focus", [{ - item: item - }]) + var focused = target; + + var id = focused.getAttribute('data-id'); + var item = items[id]; + + if (item) { + events.trigger(self, 'focus', [ + { + item: item + }]); + } } - if ("left" === lastFocusDirection) programCell && scrollHelper.toStart(programGrid, programCell, !0, !0); - else if ("right" === lastFocusDirection) programCell && scrollHelper.toCenter(programGrid, programCell, !0, !0); - else if ("up" === lastFocusDirection || "down" === lastFocusDirection) { - var verticalScroller = dom.parentWithClass(target, "guideVerticalScroller"); + + if (lastFocusDirection === 'left') { + + if (programCell) { + + scrollHelper.toStart(programGrid, programCell, true, true); + } + } + + else if (lastFocusDirection === 'right') { + + if (programCell) { + + scrollHelper.toCenter(programGrid, programCell, true, true); + } + } + + else if (lastFocusDirection === 'up' || lastFocusDirection === 'down') { + + var verticalScroller = dom.parentWithClass(target, 'guideVerticalScroller'); if (verticalScroller) { - var focusedElement = programCell || dom.parentWithTag(target, "BUTTON"); - verticalScroller.toCenter(focusedElement, !0) + + var focusedElement = programCell || dom.parentWithTag(target, 'BUTTON'); + verticalScroller.toCenter(focusedElement, true); } } } function setScrollEvents(view, enabled) { + if (layoutManager.tv) { - var guideVerticalScroller = view.querySelector(".guideVerticalScroller"); - enabled ? inputManager.on(guideVerticalScroller, onInputCommand) : inputManager.off(guideVerticalScroller, onInputCommand) + var guideVerticalScroller = view.querySelector('.guideVerticalScroller'); + + if (enabled) { + inputManager.on(guideVerticalScroller, onInputCommand); + } else { + inputManager.off(guideVerticalScroller, onInputCommand); + } } } function onTimerCreated(e, apiClient, data) { - for (var programId = data.ProgramId, newTimerId = data.Id, cells = options.element.querySelectorAll('.programCell[data-id="' + programId + '"]'), i = 0, length = cells.length; i < length; i++) { + + var programId = data.ProgramId; + // This could be null, not supported by all tv providers + var newTimerId = data.Id; + + // find guide cells by program id, ensure timer icon + var cells = options.element.querySelectorAll('.programCell[data-id="' + programId + '"]'); + for (var i = 0, length = cells.length; i < length; i++) { var cell = cells[i]; - cell.querySelector(".timerIcon") || cell.querySelector(".guideProgramName").insertAdjacentHTML("beforeend", ''), newTimerId && cell.setAttribute("data-timerid", newTimerId) + + var icon = cell.querySelector('.timerIcon'); + if (!icon) { + cell.querySelector('.guideProgramName').insertAdjacentHTML('beforeend', ''); + } + + if (newTimerId) { + cell.setAttribute('data-timerid', newTimerId); + } } } - function onSeriesTimerCreated(e, apiClient, data) {} + function onSeriesTimerCreated(e, apiClient, data) { + } function onTimerCancelled(e, apiClient, data) { - for (var id = data.Id, cells = options.element.querySelectorAll('.programCell[data-timerid="' + id + '"]'), i = 0, length = cells.length; i < length; i++) { - var cell = cells[i], - icon = cell.querySelector(".timerIcon"); - icon && icon.parentNode.removeChild(icon), cell.removeAttribute("data-timerid") + var id = data.Id; + // find guide cells by timer id, remove timer icon + var cells = options.element.querySelectorAll('.programCell[data-timerid="' + id + '"]'); + for (var i = 0, length = cells.length; i < length; i++) { + var cell = cells[i]; + var icon = cell.querySelector('.timerIcon'); + if (icon) { + icon.parentNode.removeChild(icon); + } + cell.removeAttribute('data-timerid'); } } function onSeriesTimerCancelled(e, apiClient, data) { - for (var id = data.Id, cells = options.element.querySelectorAll('.programCell[data-seriestimerid="' + id + '"]'), i = 0, length = cells.length; i < length; i++) { - var cell = cells[i], - icon = cell.querySelector(".seriesTimerIcon"); - icon && icon.parentNode.removeChild(icon), cell.removeAttribute("data-seriestimerid") + var id = data.Id; + // find guide cells by timer id, remove timer icon + var cells = options.element.querySelectorAll('.programCell[data-seriestimerid="' + id + '"]'); + for (var i = 0, length = cells.length; i < length; i++) { + var cell = cells[i]; + var icon = cell.querySelector('.seriesTimerIcon'); + if (icon) { + icon.parentNode.removeChild(icon); + } + cell.removeAttribute('data-seriestimerid'); } } - var self = this, - items = {}; - self.options = options, self.categoryOptions = { - categories: [] - }; - var currentDate, autoRefreshInterval, programCells, lastFocusDirection, programGrid, cellCurationMinutes = 30, - cellDurationMs = 60 * cellCurationMinutes * 1e3, - msPerDay = 864e5, - currentStartIndex = 0, - currentChannelLimit = 0; - self.refresh = function() { - currentDate = null, reloadPage(options.element), restartAutoRefresh() - }, self.pause = function() { - stopAutoRefresh() - }, self.resume = function(refreshData) { - refreshData ? self.refresh() : restartAutoRefresh() - }, self.destroy = function() { - stopAutoRefresh(), events.off(serverNotifications, "TimerCreated", onTimerCreated), events.off(serverNotifications, "SeriesTimerCreated", onSeriesTimerCreated), events.off(serverNotifications, "TimerCancelled", onTimerCancelled), events.off(serverNotifications, "SeriesTimerCancelled", onSeriesTimerCancelled), setScrollEvents(options.element, !1), itemShortcuts.off(options.element), items = {} - }; - var lastGridScroll = 0, - lastHeaderScroll = 0, - scrollXPct = 0; - require(["text!./tvguide.template.html"], function(template) { + + require(['text!./tvguide.template.html'], function (template) { + var context = options.element; - context.classList.add("tvguide"), context.innerHTML = globalize.translateDocument(template, "sharedcomponents"), programGrid = context.querySelector(".programGrid"); - var timeslotHeaders = context.querySelector(".timeslotHeaders"); - layoutManager.tv ? dom.addEventListener(context.querySelector(".guideVerticalScroller"), "focus", onScrollerFocus, { - capture: !0, - passive: !0 - }) : layoutManager.desktop && timeslotHeaders.classList.add("timeslotHeaders-desktop"), (browser.iOS || browser.osx) && (context.querySelector(".channelsContainer").classList.add("noRubberBanding"), programGrid.classList.add("noRubberBanding")), dom.addEventListener(programGrid, "scroll", function(e) { - onProgramGridScroll(context, this, timeslotHeaders) + + context.classList.add('tvguide'); + + context.innerHTML = globalize.translateDocument(template, 'sharedcomponents'); + + programGrid = context.querySelector('.programGrid'); + var timeslotHeaders = context.querySelector('.timeslotHeaders'); + + if (layoutManager.tv) { + dom.addEventListener(context.querySelector('.guideVerticalScroller'), 'focus', onScrollerFocus, { + capture: true, + passive: true + }); + } else if (layoutManager.desktop) { + timeslotHeaders.classList.add('timeslotHeaders-desktop'); + } + + if (browser.iOS || browser.osx) { + context.querySelector('.channelsContainer').classList.add('noRubberBanding'); + + programGrid.classList.add('noRubberBanding'); + } + + dom.addEventListener(programGrid, 'scroll', function (e) { + onProgramGridScroll(context, this, timeslotHeaders); }, { - passive: !0 - }), dom.addEventListener(timeslotHeaders, "scroll", function() { - onTimeslotHeadersScroll(context, this) + passive: true + }); + + dom.addEventListener(timeslotHeaders, 'scroll', function () { + onTimeslotHeadersScroll(context, this); }, { - passive: !0 - }), programGrid.addEventListener("click", onProgramGridClick), context.querySelector(".btnNextPage").addEventListener("click", function() { - currentStartIndex += currentChannelLimit, reloadPage(context), restartAutoRefresh() - }), context.querySelector(".btnPreviousPage").addEventListener("click", function() { - currentStartIndex = Math.max(currentStartIndex - currentChannelLimit, 0), reloadPage(context), restartAutoRefresh() - }), context.querySelector(".btnGuideViewSettings").addEventListener("click", function() { - showViewSettings(self), restartAutoRefresh() - }), context.querySelector(".guideDateTabs").addEventListener("tabchange", function(e) { - var allTabButtons = e.target.querySelectorAll(".guide-date-tab-button"), - tabButton = allTabButtons[parseInt(e.detail.selectedTabIndex)]; + passive: true + }); + + programGrid.addEventListener('click', onProgramGridClick); + + context.querySelector('.btnNextPage').addEventListener('click', function () { + currentStartIndex += currentChannelLimit; + reloadPage(context); + restartAutoRefresh(); + }); + + context.querySelector('.btnPreviousPage').addEventListener('click', function () { + currentStartIndex = Math.max(currentStartIndex - currentChannelLimit, 0); + reloadPage(context); + restartAutoRefresh(); + }); + + context.querySelector('.btnGuideViewSettings').addEventListener('click', function () { + showViewSettings(self); + restartAutoRefresh(); + }); + + context.querySelector('.guideDateTabs').addEventListener('tabchange', function (e) { + + var allTabButtons = e.target.querySelectorAll('.guide-date-tab-button'); + + var tabButton = allTabButtons[parseInt(e.detail.selectedTabIndex)]; if (tabButton) { - var previousButton = null == e.detail.previousIndex ? null : allTabButtons[parseInt(e.detail.previousIndex)], - date = new Date; - date.setTime(parseInt(tabButton.getAttribute("data-date"))); - var scrollToTimeMs, scrollWidth = programGrid.scrollWidth; - if (scrollToTimeMs = scrollWidth ? programGrid.scrollLeft / scrollWidth * msPerDay : 0, previousButton) { - var previousDate = new Date; - previousDate.setTime(parseInt(previousButton.getAttribute("data-date"))), scrollToTimeMs += 60 * previousDate.getHours() * 60 * 1e3, scrollToTimeMs += 60 * previousDate.getMinutes() * 1e3 + + var previousButton = e.detail.previousIndex == null ? null : allTabButtons[parseInt(e.detail.previousIndex)]; + + var date = new Date(); + date.setTime(parseInt(tabButton.getAttribute('data-date'))); + + var scrollWidth = programGrid.scrollWidth; + var scrollToTimeMs; + if (scrollWidth) { + scrollToTimeMs = (programGrid.scrollLeft / scrollWidth) * msPerDay; + } else { + scrollToTimeMs = 0; } - var startTimeOfDayMs = 60 * date.getHours() * 60 * 1e3; - startTimeOfDayMs += 60 * date.getMinutes() * 1e3, changeDate(context, date, scrollToTimeMs, scrollToTimeMs, startTimeOfDayMs, !1) + + if (previousButton) { + + var previousDate = new Date(); + previousDate.setTime(parseInt(previousButton.getAttribute('data-date'))); + + scrollToTimeMs += (previousDate.getHours() * 60 * 60 * 1000); + scrollToTimeMs += (previousDate.getMinutes() * 60 * 1000); + } + + var startTimeOfDayMs = (date.getHours() * 60 * 60 * 1000); + startTimeOfDayMs += (date.getMinutes() * 60 * 1000); + + changeDate(context, date, scrollToTimeMs, scrollToTimeMs, startTimeOfDayMs, false); } - }), setScrollEvents(context, !0), itemShortcuts.on(context), events.trigger(self, "load"), events.on(serverNotifications, "TimerCreated", onTimerCreated), events.on(serverNotifications, "SeriesTimerCreated", onSeriesTimerCreated), events.on(serverNotifications, "TimerCancelled", onTimerCancelled), events.on(serverNotifications, "SeriesTimerCancelled", onSeriesTimerCancelled), self.refresh() - }) + }); + + setScrollEvents(context, true); + itemShortcuts.on(context); + + events.trigger(self, 'load'); + + events.on(serverNotifications, 'TimerCreated', onTimerCreated); + events.on(serverNotifications, 'SeriesTimerCreated', onSeriesTimerCreated); + events.on(serverNotifications, 'TimerCancelled', onTimerCancelled); + events.on(serverNotifications, 'SeriesTimerCancelled', onSeriesTimerCancelled); + + self.refresh(); + }); } - var isUpdatingProgramCellScroll = !1, - ProgramCellPrototype = Object.create(HTMLButtonElement.prototype); - return ProgramCellPrototype.detachedCallback = function() { - this.posLeft = null, this.posWidth = null, this.guideProgramName = null - }, document.registerElement("emby-programcell", { + + var ProgramCellPrototype = Object.create(HTMLButtonElement.prototype); + + ProgramCellPrototype.detachedCallback = function () { + this.posLeft = null; + this.posWidth = null; + this.guideProgramName = null; + }; + + document.registerElement('emby-programcell', { prototype: ProgramCellPrototype, - extends: "button" - }), Guide + extends: 'button' + }); + + return Guide; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/guide/programs.css b/src/bower_components/emby-webcomponents/guide/programs.css index 2d8738e01a..7a559e173f 100644 --- a/src/bower_components/emby-webcomponents/guide/programs.css +++ b/src/bower_components/emby-webcomponents/guide/programs.css @@ -1,19 +1,19 @@ .newTvProgram { - background: #38c; - color: #fff + background: #3388cc; + color: #fff; } .liveTvProgram { - background: #c33; - color: #fff + background: #cc3333; + color: #fff; } .premiereTvProgram { background: #EF6C00; - color: #fff + color: #fff; } .repeatTvProgram { background: #009688; - color: #fff -} \ No newline at end of file + color: #fff; +} diff --git a/src/bower_components/emby-webcomponents/headroom/headroom.css b/src/bower_components/emby-webcomponents/headroom/headroom.css index 642426881a..caac40a1b6 100644 --- a/src/bower_components/emby-webcomponents/headroom/headroom.css +++ b/src/bower_components/emby-webcomponents/headroom/headroom.css @@ -1,15 +1,11 @@ .headroom { - -webkit-transition: -webkit-transform 140ms linear; - -o-transition: transform 140ms linear; - transition: transform 140ms linear + transition: transform 140ms linear; } .headroom--pinned { - -webkit-transform: none; - transform: none + transform: none; } .headroom--unpinned:not(.headroomDisabled) { - -webkit-transform: translateY(-100%); - transform: translateY(-100%) + transform: translateY(-100%); } \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/headroom/headroom.js b/src/bower_components/emby-webcomponents/headroom/headroom.js index bd6573a663..9cb1cac04d 100644 --- a/src/bower_components/emby-webcomponents/headroom/headroom.js +++ b/src/bower_components/emby-webcomponents/headroom/headroom.js @@ -1,136 +1,349 @@ -define(["dom", "layoutManager", "browser", "css!./headroom"], function(dom, layoutManager, browser) { - "use strict"; +/*! + * headroom.js v0.7.0 - Give your page some headroom. Hide your header until you need it + * Copyright (c) 2014 Nick Williams - http://wicky.nillia.ms/headroom.js + * License: MIT + */ +define(['dom', 'layoutManager', 'browser', 'css!./headroom'], function (dom, layoutManager, browser) { + + 'use strict'; + + /* exported features */ + + var requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame; + + /** + * Handles debouncing of events via requestAnimationFrame + * @see http://www.html5rocks.com/en/tutorials/speed/animations/ + * @param {Function} callback The callback to handle whichever event + */ function Debouncer(callback) { - this.callback = callback, this.ticking = !1 + this.callback = callback; + this.ticking = false; } + Debouncer.prototype = { + constructor: Debouncer, + + /** + * dispatches the event to the supplied callback + * @private + */ + update: function () { + if (this.callback) { + this.callback(); + } + this.ticking = false; + }, + + /** + * Attach this as the event listeners + */ + handleEvent: function () { + if (!this.ticking) { + requestAnimationFrame(this.rafCallback || (this.rafCallback = this.update.bind(this))); + this.ticking = true; + } + } + }; function onHeadroomClearedExternally() { - this.state = null + this.state = null; } + /** + * UI enhancement for fixed headers. + * Hides header when scrolling down + * Shows header when scrolling up + * @constructor + * @param {DOMElement} elem the header element + * @param {Object} options options for the widget + */ function Headroom(elems, options) { - options = Object.assign(Headroom.options, options || {}), this.lastKnownScrollY = 0, this.elems = elems, this.scroller = options.scroller, this.debouncer = onScroll.bind(this), this.offset = options.offset, this.initialised = !1, this.initialClass = options.initialClass, this.unPinnedClass = options.unPinnedClass, this.pinnedClass = options.pinnedClass, this.state = "clear" + options = Object.assign(Headroom.options, options || {}); + + this.lastKnownScrollY = 0; + this.elems = elems; + + this.scroller = options.scroller; + + this.debouncer = onScroll.bind(this); + this.offset = options.offset; + this.initialised = false; + + this.initialClass = options.initialClass; + this.unPinnedClass = options.unPinnedClass; + this.pinnedClass = options.pinnedClass; + + this.state = 'clear'; } function onScroll() { - this.paused || requestAnimationFrame(this.rafCallback || (this.rafCallback = this.update.bind(this))) - } - var requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame; - return Debouncer.prototype = { - constructor: Debouncer, - update: function() { - this.callback && this.callback(), this.ticking = !1 - }, - handleEvent: function() { - this.ticking || (requestAnimationFrame(this.rafCallback || (this.rafCallback = this.update.bind(this))), this.ticking = !0) + + if (this.paused) { + return; } - }, Headroom.prototype = { + + requestAnimationFrame(this.rafCallback || (this.rafCallback = this.update.bind(this))); + } + + Headroom.prototype = { constructor: Headroom, - init: function() { + + /** + * Initialises the widget + */ + init: function () { + if (browser.supportsCssAnimation()) { - for (var i = 0, length = this.elems.length; i < length; i++) this.elems[i].classList.add(this.initialClass), this.elems[i].addEventListener("clearheadroom", onHeadroomClearedExternally.bind(this)); - this.attachEvent() + for (var i = 0, length = this.elems.length; i < length; i++) { + this.elems[i].classList.add(this.initialClass); + this.elems[i].addEventListener('clearheadroom', onHeadroomClearedExternally.bind(this)); + } + + this.attachEvent(); } - return this + + return this; }, - add: function(elem) { - browser.supportsCssAnimation() && (elem.classList.add(this.initialClass), elem.addEventListener("clearheadroom", onHeadroomClearedExternally.bind(this)), this.elems.push(elem)) + + add: function (elem) { + + if (browser.supportsCssAnimation()) { + elem.classList.add(this.initialClass); + elem.addEventListener('clearheadroom', onHeadroomClearedExternally.bind(this)); + this.elems.push(elem); + } }, - remove: function(elem) { - elem.classList.remove(this.unPinnedClass), elem.classList.remove(this.initialClass), elem.classList.remove(this.pinnedClass); - var i = this.elems.indexOf(elem); - 1 !== i && this.elems.splice(i, 1) + + remove: function (elem) { + + elem.classList.remove(this.unPinnedClass); + elem.classList.remove(this.initialClass); + elem.classList.remove(this.pinnedClass); + + var i = this.elems.indexOf(elem); + if (i !== -1) { + this.elems.splice(i, 1); + } }, - pause: function() { - this.paused = !0 + + pause: function () { + this.paused = true; }, - resume: function() { - this.paused = !1 + + resume: function () { + this.paused = false; }, - destroy: function() { - this.initialised = !1; + + /** + * Unattaches events and removes any classes that were added + */ + destroy: function () { + + this.initialised = false; + + for (var i = 0, length = this.elems.length; i < length; i++) { + + var classList = this.elems[i].classList; + + classList.remove(this.unPinnedClass); + classList.remove(this.initialClass); + classList.remove(this.pinnedClass); + } + + var scrollEventName = this.scroller.getScrollEventName ? this.scroller.getScrollEventName() : 'scroll'; + + dom.removeEventListener(this.scroller, scrollEventName, this.debouncer, { + capture: false, + passive: true + }); + }, + + /** + * Attaches the scroll event + * @private + */ + attachEvent: function () { + if (!this.initialised) { + this.lastKnownScrollY = this.getScrollY(); + this.initialised = true; + + var scrollEventName = this.scroller.getScrollEventName ? this.scroller.getScrollEventName() : 'scroll'; + + dom.addEventListener(this.scroller, scrollEventName, this.debouncer, { + capture: false, + passive: true + }); + + this.update(); + } + }, + + /** + * Unpins the header if it's currently pinned + */ + clear: function () { + + if (this.state === 'clear') { + return; + } + + this.state = 'clear'; + + var unpinnedClass = this.unPinnedClass; + var pinnedClass = this.pinnedClass; + for (var i = 0, length = this.elems.length; i < length; i++) { var classList = this.elems[i].classList; - classList.remove(this.unPinnedClass), classList.remove(this.initialClass), classList.remove(this.pinnedClass) - } - var scrollEventName = this.scroller.getScrollEventName ? this.scroller.getScrollEventName() : "scroll"; - dom.removeEventListener(this.scroller, scrollEventName, this.debouncer, { - capture: !1, - passive: !0 - }) - }, - attachEvent: function() { - if (!this.initialised) { - this.lastKnownScrollY = this.getScrollY(), this.initialised = !0; - var scrollEventName = this.scroller.getScrollEventName ? this.scroller.getScrollEventName() : "scroll"; - dom.addEventListener(this.scroller, scrollEventName, this.debouncer, { - capture: !1, - passive: !0 - }), this.update() + + classList.remove(unpinnedClass); + //classList.remove(pinnedClass); } }, - clear: function() { - if ("clear" !== this.state) { - this.state = "clear"; - for (var unpinnedClass = this.unPinnedClass, i = (this.pinnedClass, 0), length = this.elems.length; i < length; i++) { - this.elems[i].classList.remove(unpinnedClass) - } + + /** + * Unpins the header if it's currently pinned + */ + pin: function () { + + if (this.state === 'pin') { + return; + } + + this.state = 'pin'; + + var unpinnedClass = this.unPinnedClass; + var pinnedClass = this.pinnedClass; + + for (var i = 0, length = this.elems.length; i < length; i++) { + var classList = this.elems[i].classList; + + classList.remove(unpinnedClass); + classList.add(pinnedClass); } }, - pin: function() { - if ("pin" !== this.state) { - this.state = "pin"; - for (var unpinnedClass = this.unPinnedClass, pinnedClass = this.pinnedClass, i = 0, length = this.elems.length; i < length; i++) { - var classList = this.elems[i].classList; - classList.remove(unpinnedClass), classList.add(pinnedClass) - } + + /** + * Unpins the header if it's currently pinned + */ + unpin: function () { + + if (this.state === 'unpin') { + return; + } + + this.state = 'unpin'; + + var unpinnedClass = this.unPinnedClass; + var pinnedClass = this.pinnedClass; + + for (var i = 0, length = this.elems.length; i < length; i++) { + var classList = this.elems[i].classList; + + classList.add(unpinnedClass); + //classList.remove(pinnedClass); } }, - unpin: function() { - if ("unpin" !== this.state) { - this.state = "unpin"; - for (var unpinnedClass = this.unPinnedClass, i = (this.pinnedClass, 0), length = this.elems.length; i < length; i++) { - this.elems[i].classList.add(unpinnedClass) - } - } - }, - getScrollY: function() { + + /** + * Gets the Y scroll position + * @see https://developer.mozilla.org/en-US/docs/Web/API/Window.scrollY + * @return {Number} pixels the page has scrolled along the Y-axis + */ + getScrollY: function () { + var scroller = this.scroller; - if (scroller.getScrollPosition) return scroller.getScrollPosition(); + + if (scroller.getScrollPosition) { + return scroller.getScrollPosition(); + } + var pageYOffset = scroller.pageYOffset; - if (void 0 !== pageYOffset) return pageYOffset; + if (pageYOffset !== undefined) { + return pageYOffset; + } + var scrollTop = scroller.scrollTop; - return void 0 !== scrollTop ? scrollTop : (document.documentElement || document.body).scrollTop + if (scrollTop !== undefined) { + return scrollTop; + } + + return (document.documentElement || document.body).scrollTop; }, - shouldUnpin: function(currentScrollY) { + + /** + * determine if it is appropriate to unpin + * @param {int} currentScrollY the current y scroll position + * @return {bool} true if should unpin, false otherwise + */ + shouldUnpin: function (currentScrollY) { var scrollingDown = currentScrollY > this.lastKnownScrollY, pastOffset = currentScrollY >= this.offset; - return scrollingDown && pastOffset + + return scrollingDown && pastOffset; }, - shouldPin: function(currentScrollY) { + + /** + * determine if it is appropriate to pin + * @param {int} currentScrollY the current y scroll position + * @return {bool} true if should pin, false otherwise + */ + shouldPin: function (currentScrollY) { var scrollingUp = currentScrollY < this.lastKnownScrollY, pastOffset = currentScrollY <= this.offset; - return scrollingUp || pastOffset + + return scrollingUp || pastOffset; }, - update: function() { - if (!this.paused) { - var currentScrollY = this.getScrollY(), - lastKnownScrollY = this.lastKnownScrollY, - isTv = layoutManager.tv; - if (currentScrollY <= (isTv ? 120 : 10)) this.clear(); - else if (this.shouldUnpin(currentScrollY)) this.unpin(); - else if (this.shouldPin(currentScrollY)) { - var toleranceExceeded = Math.abs(currentScrollY - lastKnownScrollY) >= 14; - currentScrollY && isTv ? this.unpin() : toleranceExceeded && this.clear() - } - this.lastKnownScrollY = currentScrollY + + /** + * Handles updating the state of the widget + */ + update: function () { + + if (this.paused) { + return; } + + var currentScrollY = this.getScrollY(); + + var lastKnownScrollY = this.lastKnownScrollY; + + var isTv = layoutManager.tv; + + if (currentScrollY <= (isTv ? 120 : 10)) { + this.clear(); + } + else if (this.shouldUnpin(currentScrollY)) { + this.unpin(); + } + else if (this.shouldPin(currentScrollY)) { + + var toleranceExceeded = Math.abs(currentScrollY - lastKnownScrollY) >= 14; + + if (currentScrollY && isTv) { + this.unpin(); + } else if (toleranceExceeded) { + this.clear(); + } + } else if (isTv) { + //this.clear(); + } + + this.lastKnownScrollY = currentScrollY; } - }, Headroom.options = { + }; + /** + * Default options + * @type {Object} + */ + Headroom.options = { offset: 0, scroller: window, - initialClass: "headroom", - unPinnedClass: "headroom--unpinned", - pinnedClass: "headroom--pinned" - }, Headroom + initialClass: 'headroom', + unPinnedClass: 'headroom--unpinned', + pinnedClass: 'headroom--pinned' + }; + + return Headroom; + }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/homescreensettings/homescreensettings.js b/src/bower_components/emby-webcomponents/homescreensettings/homescreensettings.js index 8a34eb9418..67d21a6f09 100644 --- a/src/bower_components/emby-webcomponents/homescreensettings/homescreensettings.js +++ b/src/bower_components/emby-webcomponents/homescreensettings/homescreensettings.js @@ -1,233 +1,543 @@ -define(["require", "apphost", "layoutManager", "focusManager", "globalize", "loading", "connectionManager", "homeSections", "dom", "events", "listViewStyle", "emby-select", "emby-checkbox"], function(require, appHost, layoutManager, focusManager, globalize, loading, connectionManager, homeSections, dom, events) { +define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loading', 'connectionManager', 'homeSections', 'dom', 'events', 'listViewStyle', 'emby-select', 'emby-checkbox'], function (require, appHost, layoutManager, focusManager, globalize, loading, connectionManager, homeSections, dom, events) { "use strict"; + var numConfigurableSections = 7; + function renderViews(page, user, result) { - var folderHtml = ""; - folderHtml += '
', folderHtml += result.map(function(i) { - var currentHtml = "", - id = "chkGroupFolder" + i.Id, - isChecked = -1 !== user.Configuration.GroupedFolders.indexOf(i.Id), - checkedHtml = isChecked ? ' checked="checked"' : ""; - return currentHtml += "" - }).join(""), folderHtml += "
", page.querySelector(".folderGroupList").innerHTML = folderHtml + + var folderHtml = ''; + + folderHtml += '
'; + folderHtml += result.map(function (i) { + + var currentHtml = ''; + + var id = 'chkGroupFolder' + i.Id; + + var isChecked = user.Configuration.GroupedFolders.indexOf(i.Id) !== -1; + + var checkedHtml = isChecked ? ' checked="checked"' : ''; + + currentHtml += ''; + + return currentHtml; + + }).join(''); + + folderHtml += '
'; + + page.querySelector('.folderGroupList').innerHTML = folderHtml; } function getLandingScreenOptions(type) { + var list = []; - return "movies" === type ? (list.push({ - name: globalize.translate("sharedcomponents#Movies"), - value: "movies", - isDefault: !0 - }), list.push({ - name: globalize.translate("sharedcomponents#Suggestions"), - value: "suggestions" - }), list.push({ - name: globalize.translate("sharedcomponents#Favorites"), - value: "favorites" - }), list.push({ - name: globalize.translate("sharedcomponents#Collections"), - value: "collections" - })) : "tvshows" === type ? (list.push({ - name: globalize.translate("sharedcomponents#Shows"), - value: "shows", - isDefault: !0 - }), list.push({ - name: globalize.translate("sharedcomponents#Suggestions"), - value: "suggestions" - }), list.push({ - name: globalize.translate("sharedcomponents#Latest"), - value: "latest" - }), list.push({ - name: globalize.translate("sharedcomponents#Favorites"), - value: "favorites" - })) : "music" === type ? (list.push({ - name: globalize.translate("sharedcomponents#Suggestions"), - value: "suggestions", - isDefault: !0 - }), list.push({ - name: globalize.translate("sharedcomponents#Albums"), - value: "albums" - }), list.push({ - name: globalize.translate("sharedcomponents#HeaderAlbumArtists"), - value: "albumartists" - }), list.push({ - name: globalize.translate("sharedcomponents#Artists"), - value: "artists" - }), list.push({ - name: globalize.translate("sharedcomponents#Playlists"), - value: "playlists" - }), list.push({ - name: globalize.translate("sharedcomponents#Genres"), - value: "genres" - })) : "livetv" === type && (list.push({ - name: globalize.translate("sharedcomponents#Suggestions"), - value: "suggestions", - isDefault: !0 - }), list.push({ - name: globalize.translate("sharedcomponents#Guide"), - value: "guide" - })), list + + if (type === 'movies') { + + list.push({ + name: globalize.translate('sharedcomponents#Movies'), + value: 'movies', + isDefault: true + }); + + list.push({ + name: globalize.translate('sharedcomponents#Suggestions'), + value: 'suggestions' + }); + + list.push({ + name: globalize.translate('sharedcomponents#Favorites'), + value: 'favorites' + }); + list.push({ + name: globalize.translate('sharedcomponents#Collections'), + value: 'collections' + }); + } + else if (type === 'tvshows') { + + list.push({ + name: globalize.translate('sharedcomponents#Shows'), + value: 'shows', + isDefault: true + }); + list.push({ + name: globalize.translate('sharedcomponents#Suggestions'), + value: 'suggestions' + }); + + list.push({ + name: globalize.translate('sharedcomponents#Latest'), + value: 'latest' + }); + list.push({ + name: globalize.translate('sharedcomponents#Favorites'), + value: 'favorites' + }); + } + else if (type === 'music') { + + list.push({ + name: globalize.translate('sharedcomponents#Suggestions'), + value: 'suggestions', + isDefault: true + }); + + list.push({ + name: globalize.translate('sharedcomponents#Albums'), + value: 'albums' + }); + + list.push({ + name: globalize.translate('sharedcomponents#HeaderAlbumArtists'), + value: 'albumartists' + }); + + list.push({ + name: globalize.translate('sharedcomponents#Artists'), + value: 'artists' + }); + + list.push({ + name: globalize.translate('sharedcomponents#Playlists'), + value: 'playlists' + }); + + list.push({ + name: globalize.translate('sharedcomponents#Genres'), + value: 'genres' + }); + } + else if (type === 'livetv') { + + list.push({ + name: globalize.translate('sharedcomponents#Suggestions'), + value: 'suggestions', + isDefault: true + }); + list.push({ + name: globalize.translate('sharedcomponents#Guide'), + value: 'guide' + }); + } + + return list; } function getLandingScreenOptionsHtml(type, userValue) { - return getLandingScreenOptions(type).map(function(o) { - var selected = userValue === o.value || o.isDefault && !userValue, - selectedHtml = selected ? " selected" : ""; - return '" - }).join("") + + return getLandingScreenOptions(type).map(function (o) { + + var selected = userValue === o.value || (o.isDefault && !userValue); + var selectedHtml = selected ? ' selected' : ''; + var optionValue = o.isDefault ? '' : o.value; + + return ''; + }).join(''); } function renderViewOrder(context, user, result) { - var html = "", - index = 0; - html += result.Items.map(function(view) { - var currentHtml = ""; - return currentHtml += '
', currentHtml += '', currentHtml += '
', currentHtml += "
", currentHtml += view.Name, currentHtml += "
", currentHtml += "
", currentHtml += '', currentHtml += '', currentHtml += "
", index++, currentHtml - }).join(""), context.querySelector(".viewOrderList").innerHTML = html + + var html = ''; + + var index = 0; + + html += result.Items.map(function (view) { + + var currentHtml = ''; + + currentHtml += '
'; + + currentHtml += ''; + + currentHtml += '
'; + + currentHtml += '
'; + currentHtml += view.Name; + currentHtml += '
'; + + currentHtml += '
'; + + currentHtml += ''; + currentHtml += ''; + + currentHtml += '
'; + + index++; + return currentHtml; + + }).join(''); + + context.querySelector('.viewOrderList').innerHTML = html; } function updateHomeSectionValues(context, userSettings) { + for (var i = 1; i <= 7; i++) { - var select = context.querySelector("#selectHomeSection" + i), - defaultValue = homeSections.getDefaultSection(i - 1), - option = select.querySelector("option[value=" + defaultValue + "]") || select.querySelector('option[value=""]'), - userValue = userSettings.get("homesection" + (i - 1)); - option.value = "", select.value = userValue !== defaultValue && userValue ? userValue : "" + + var select = context.querySelector('#selectHomeSection' + i); + var defaultValue = homeSections.getDefaultSection(i - 1); + + var option = select.querySelector('option[value=' + defaultValue + ']') || select.querySelector('option[value=""]'); + + var userValue = userSettings.get('homesection' + (i - 1)); + + option.value = ''; + + if (userValue === defaultValue || !userValue) { + select.value = ''; + } else { + select.value = userValue; + } } - context.querySelector(".selectTVHomeScreen").value = userSettings.get("tvhome") || "" + + context.querySelector('.selectTVHomeScreen').value = userSettings.get('tvhome') || ''; } function getPerLibrarySettingsHtml(item, user, userSettings, apiClient) { - var isChecked, html = ""; - if ("Channel" !== item.Type && "boxsets" !== item.CollectionType && "playlists" !== item.CollectionType || (isChecked = -1 === (user.Configuration.MyMediaExcludes || []).indexOf(item.Id), html += "
", html += "", html += "
"), -1 === ["playlists", "livetv", "boxsets", "channels"].indexOf(item.CollectionType || "") && (isChecked = -1 === user.Configuration.LatestItemsExcludes.indexOf(item.Id), html += '"), html && (html = '
' + html + "
"), "movies" === item.CollectionType || "tvshows" === item.CollectionType || "music" === item.CollectionType || "livetv" === item.CollectionType) { - var idForLanding = "livetv" === item.CollectionType ? item.CollectionType : item.Id; - html += '
', html += '", html += "
" + + var html = ''; + + var isChecked; + + if (item.Type === 'Channel' || item.CollectionType === 'boxsets' || item.CollectionType === 'playlists') { + isChecked = (user.Configuration.MyMediaExcludes || []).indexOf(item.Id) === -1; + html += '
'; + html += ''; + html += '
'; } + + var excludeFromLatest = ['playlists', 'livetv', 'boxsets', 'channels']; + if (excludeFromLatest.indexOf(item.CollectionType || '') === -1) { + + isChecked = user.Configuration.LatestItemsExcludes.indexOf(item.Id) === -1; + html += ''; + } + if (html) { - var prefix = ""; - prefix += '
', prefix += '

', prefix += item.Name, prefix += "

", html = prefix + html, html += "
" + + html = '
' + html + '
'; } - return html + + if (item.CollectionType === 'movies' || item.CollectionType === 'tvshows' || item.CollectionType === 'music' || item.CollectionType === 'livetv') { + + var idForLanding = item.CollectionType === 'livetv' ? item.CollectionType : item.Id; + html += '
'; + html += ''; + html += '
'; + } + + if (html) { + + var prefix = ''; + prefix += '
'; + + prefix += '

'; + prefix += item.Name; + prefix += '

'; + + html = prefix + html; + html += '
'; + } + + + return html; } function renderPerLibrarySettings(context, user, userViews, userSettings, apiClient) { - for (var elem = context.querySelector(".perLibrarySettings"), html = "", i = 0, length = userViews.length; i < length; i++) html += getPerLibrarySettingsHtml(userViews[i], user, userSettings, apiClient); - elem.innerHTML = html + + var elem = context.querySelector('.perLibrarySettings'); + var html = ''; + + for (var i = 0, length = userViews.length; i < length; i++) { + + html += getPerLibrarySettingsHtml(userViews[i], user, userSettings, apiClient); + } + + elem.innerHTML = html; } function loadForm(context, user, userSettings, apiClient) { - context.querySelector(".chkHidePlayedFromLatest").checked = user.Configuration.HidePlayedInLatest || !1, updateHomeSectionValues(context, userSettings); - var promise1 = apiClient.getUserViews({ - IncludeHidden: !0 - }, user.Id), - promise2 = apiClient.getJSON(apiClient.getUrl("Users/" + user.Id + "/GroupingOptions")); - Promise.all([promise1, promise2]).then(function(responses) { - renderViewOrder(context, user, responses[0]), renderPerLibrarySettings(context, user, responses[0].Items, userSettings, apiClient), renderViews(context, user, responses[1]), loading.hide() - }) + + context.querySelector('.chkHidePlayedFromLatest').checked = user.Configuration.HidePlayedInLatest || false; + + updateHomeSectionValues(context, userSettings); + + var promise1 = apiClient.getUserViews({ IncludeHidden: true }, user.Id); + var promise2 = apiClient.getJSON(apiClient.getUrl("Users/" + user.Id + "/GroupingOptions")); + + Promise.all([promise1, promise2]).then(function (responses) { + + renderViewOrder(context, user, responses[0]); + + renderPerLibrarySettings(context, user, responses[0].Items, userSettings, apiClient); + + renderViews(context, user, responses[1]); + + loading.hide(); + }); + } + + function getSibling(elem, type, className) { + + var sibling = elem[type]; + + while (sibling != null) { + if (sibling.classList.contains(className)) { + break; + } + } + + if (sibling != null) { + if (!sibling.classList.contains(className)) { + sibling = null; + } + } + + return sibling; } function onSectionOrderListClick(e) { - var target = dom.parentWithClass(e.target, "btnViewItemMove"); + + var target = dom.parentWithClass(e.target, 'btnViewItemMove'); + if (target) { - var viewItem = dom.parentWithClass(target, "viewItem"); + var viewItem = dom.parentWithClass(target, 'viewItem'); + if (viewItem) { - dom.parentWithClass(viewItem, "paperList"); - if (target.classList.contains("btnViewItemDown")) { + var ul = dom.parentWithClass(viewItem, 'paperList'); + + if (target.classList.contains('btnViewItemDown')) { + var next = viewItem.nextSibling; - next && (viewItem.parentNode.removeChild(viewItem), next.parentNode.insertBefore(viewItem, next.nextSibling)) + + if (next) { + viewItem.parentNode.removeChild(viewItem); + next.parentNode.insertBefore(viewItem, next.nextSibling); + } + } else { + var prev = viewItem.previousSibling; - prev && (viewItem.parentNode.removeChild(viewItem), prev.parentNode.insertBefore(viewItem, prev)) + + if (prev) { + viewItem.parentNode.removeChild(viewItem); + prev.parentNode.insertBefore(viewItem, prev); + } } } } } function getCheckboxItems(selector, context, isChecked) { - for (var inputs = context.querySelectorAll(selector), list = [], i = 0, length = inputs.length; i < length; i++) inputs[i].checked === isChecked && list.push(inputs[i]); - return list + + var inputs = context.querySelectorAll(selector); + var list = []; + + for (var i = 0, length = inputs.length; i < length; i++) { + + if (inputs[i].checked === isChecked) { + list.push(inputs[i]); + } + + } + + return list; } function saveUser(context, user, userSettingsInstance, apiClient) { - user.Configuration.HidePlayedInLatest = context.querySelector(".chkHidePlayedFromLatest").checked, user.Configuration.LatestItemsExcludes = getCheckboxItems(".chkIncludeInLatest", context, !1).map(function(i) { - return i.getAttribute("data-folderid") - }), user.Configuration.MyMediaExcludes = getCheckboxItems(".chkIncludeInMyMedia", context, !1).map(function(i) { - return i.getAttribute("data-folderid") - }), user.Configuration.GroupedFolders = getCheckboxItems(".chkGroupFolder", context, !0).map(function(i) { - return i.getAttribute("data-folderid") + + user.Configuration.HidePlayedInLatest = context.querySelector('.chkHidePlayedFromLatest').checked; + + user.Configuration.LatestItemsExcludes = getCheckboxItems(".chkIncludeInLatest", context, false).map(function (i) { + + return i.getAttribute('data-folderid'); }); - var i, length, viewItems = context.querySelectorAll(".viewItem"), - orderedViews = []; - for (i = 0, length = viewItems.length; i < length; i++) orderedViews.push(viewItems[i].getAttribute("data-viewid")); - user.Configuration.OrderedViews = orderedViews, userSettingsInstance.set("tvhome", context.querySelector(".selectTVHomeScreen").value), userSettingsInstance.set("homesection0", context.querySelector("#selectHomeSection1").value), userSettingsInstance.set("homesection1", context.querySelector("#selectHomeSection2").value), userSettingsInstance.set("homesection2", context.querySelector("#selectHomeSection3").value), userSettingsInstance.set("homesection3", context.querySelector("#selectHomeSection4").value), userSettingsInstance.set("homesection4", context.querySelector("#selectHomeSection5").value), userSettingsInstance.set("homesection5", context.querySelector("#selectHomeSection6").value), userSettingsInstance.set("homesection6", context.querySelector("#selectHomeSection7").value); - var selectLandings = context.querySelectorAll(".selectLanding"); + + user.Configuration.MyMediaExcludes = getCheckboxItems(".chkIncludeInMyMedia", context, false).map(function (i) { + + return i.getAttribute('data-folderid'); + }); + + user.Configuration.GroupedFolders = getCheckboxItems(".chkGroupFolder", context, true).map(function (i) { + + return i.getAttribute('data-folderid'); + }); + + var viewItems = context.querySelectorAll('.viewItem'); + var orderedViews = []; + var i, length; + for (i = 0, length = viewItems.length; i < length; i++) { + orderedViews.push(viewItems[i].getAttribute('data-viewid')); + } + + user.Configuration.OrderedViews = orderedViews; + + userSettingsInstance.set('tvhome', context.querySelector('.selectTVHomeScreen').value); + + userSettingsInstance.set('homesection0', context.querySelector('#selectHomeSection1').value); + userSettingsInstance.set('homesection1', context.querySelector('#selectHomeSection2').value); + userSettingsInstance.set('homesection2', context.querySelector('#selectHomeSection3').value); + userSettingsInstance.set('homesection3', context.querySelector('#selectHomeSection4').value); + userSettingsInstance.set('homesection4', context.querySelector('#selectHomeSection5').value); + userSettingsInstance.set('homesection5', context.querySelector('#selectHomeSection6').value); + userSettingsInstance.set('homesection6', context.querySelector('#selectHomeSection7').value); + + var selectLandings = context.querySelectorAll('.selectLanding'); for (i = 0, length = selectLandings.length; i < length; i++) { var selectLanding = selectLandings[i]; - userSettingsInstance.set("landing-" + selectLanding.getAttribute("data-folderid"), selectLanding.value) + userSettingsInstance.set('landing-' + selectLanding.getAttribute('data-folderid'), selectLanding.value); } - return apiClient.updateUserConfiguration(user.Id, user.Configuration) + + return apiClient.updateUserConfiguration(user.Id, user.Configuration); } function save(instance, context, userId, userSettings, apiClient, enableSaveConfirmation) { - loading.show(), apiClient.getUser(userId).then(function(user) { - saveUser(context, user, userSettings, apiClient).then(function() { - loading.hide(), enableSaveConfirmation && require(["toast"], function(toast) { - toast(globalize.translate("sharedcomponents#SettingsSaved")) - }), events.trigger(instance, "saved") - }, function() { - loading.hide() - }) - }) + + loading.show(); + + apiClient.getUser(userId).then(function (user) { + + saveUser(context, user, userSettings, apiClient).then(function () { + + loading.hide(); + if (enableSaveConfirmation) { + require(['toast'], function (toast) { + toast(globalize.translate('sharedcomponents#SettingsSaved')); + }); + } + + events.trigger(instance, 'saved'); + + }, function () { + loading.hide(); + }); + }); } function onSubmit(e) { - var self = this, - apiClient = connectionManager.getApiClient(self.options.serverId), - userId = self.options.userId, - userSettings = self.options.userSettings; - return userSettings.setUserInfo(userId, apiClient).then(function() { + + var self = this; + var apiClient = connectionManager.getApiClient(self.options.serverId); + var userId = self.options.userId; + var userSettings = self.options.userSettings; + + userSettings.setUserInfo(userId, apiClient).then(function () { + var enableSaveConfirmation = self.options.enableSaveConfirmation; - save(self, self.options.element, userId, userSettings, apiClient, enableSaveConfirmation) - }), e && e.preventDefault(), !1 + save(self, self.options.element, userId, userSettings, apiClient, enableSaveConfirmation); + }); + + // Disable default form submission + if (e) { + e.preventDefault(); + } + return false; } function onChange(e) { - var chkIncludeInMyMedia = dom.parentWithClass(e.target, "chkIncludeInMyMedia"); - if (chkIncludeInMyMedia) { - var section = dom.parentWithClass(chkIncludeInMyMedia, "verticalSection"), - fldIncludeInLatest = section.querySelector(".fldIncludeInLatest"); - fldIncludeInLatest && (chkIncludeInMyMedia.checked ? fldIncludeInLatest.classList.remove("hide") : fldIncludeInLatest.classList.add("hide")) + + var chkIncludeInMyMedia = dom.parentWithClass(e.target, 'chkIncludeInMyMedia'); + if (!chkIncludeInMyMedia) { + return; + } + + var section = dom.parentWithClass(chkIncludeInMyMedia, 'verticalSection'); + var fldIncludeInLatest = section.querySelector('.fldIncludeInLatest'); + if (fldIncludeInLatest) { + if (chkIncludeInMyMedia.checked) { + fldIncludeInLatest.classList.remove('hide'); + } else { + fldIncludeInLatest.classList.add('hide'); + } } } function embed(options, self) { - require(["text!./homescreensettings.template.html"], function(template) { - for (var i = 1; i <= numConfigurableSections; i++) template = template.replace("{section" + i + "label}", globalize.translate("sharedcomponents#LabelHomeScreenSectionValue", i)); - options.element.innerHTML = globalize.translateDocument(template, "sharedcomponents"), options.element.querySelector(".viewOrderList").addEventListener("click", onSectionOrderListClick), options.element.querySelector("form").addEventListener("submit", onSubmit.bind(self)), options.element.addEventListener("change", onChange), options.enableSaveButton && options.element.querySelector(".btnSave").classList.remove("hide"), layoutManager.tv ? options.element.querySelector(".selectTVHomeScreenContainer").classList.remove("hide") : options.element.querySelector(".selectTVHomeScreenContainer").classList.add("hide"), self.loadData(options.autoFocus) - }) + + require(['text!./homescreensettings.template.html'], function (template) { + + for (var i = 1; i <= numConfigurableSections; i++) { + template = template.replace('{section' + i + 'label}', globalize.translate('sharedcomponents#LabelHomeScreenSectionValue', i)); + } + + options.element.innerHTML = globalize.translateDocument(template, 'sharedcomponents'); + + options.element.querySelector('.viewOrderList').addEventListener('click', onSectionOrderListClick); + options.element.querySelector('form').addEventListener('submit', onSubmit.bind(self)); + options.element.addEventListener('change', onChange); + + if (options.enableSaveButton) { + options.element.querySelector('.btnSave').classList.remove('hide'); + } + + if (layoutManager.tv) { + options.element.querySelector('.selectTVHomeScreenContainer').classList.remove('hide'); + } else { + options.element.querySelector('.selectTVHomeScreenContainer').classList.add('hide'); + } + + self.loadData(options.autoFocus); + }); } function HomeScreenSettings(options) { - this.options = options, embed(options, this) + + this.options = options; + + embed(options, this); } - var numConfigurableSections = 7; - return HomeScreenSettings.prototype.loadData = function(autoFocus) { - var self = this, - context = self.options.element; + + HomeScreenSettings.prototype.loadData = function (autoFocus) { + + var self = this; + var context = self.options.element; + loading.show(); - var userId = self.options.userId, - apiClient = connectionManager.getApiClient(self.options.serverId), - userSettings = self.options.userSettings; - apiClient.getUser(userId).then(function(user) { - userSettings.setUserInfo(userId, apiClient).then(function() { - self.dataLoaded = !0, loadForm(context, user, userSettings, apiClient), autoFocus && focusManager.autoFocus(context) - }) - }) - }, HomeScreenSettings.prototype.submit = function() { - onSubmit.call(this) - }, HomeScreenSettings.prototype.destroy = function() { - this.options = null - }, HomeScreenSettings + + var userId = self.options.userId; + var apiClient = connectionManager.getApiClient(self.options.serverId); + var userSettings = self.options.userSettings; + + apiClient.getUser(userId).then(function (user) { + + userSettings.setUserInfo(userId, apiClient).then(function () { + + self.dataLoaded = true; + + loadForm(context, user, userSettings, apiClient); + + if (autoFocus) { + focusManager.autoFocus(context); + } + }); + }); + }; + + HomeScreenSettings.prototype.submit = function () { + onSubmit.call(this); + }; + + HomeScreenSettings.prototype.destroy = function () { + + this.options = null; + }; + + return HomeScreenSettings; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/homescreensettings/homescreensettingsdialog.js b/src/bower_components/emby-webcomponents/homescreensettings/homescreensettingsdialog.js index a21c16ec81..3962ccf239 100644 --- a/src/bower_components/emby-webcomponents/homescreensettings/homescreensettingsdialog.js +++ b/src/bower_components/emby-webcomponents/homescreensettings/homescreensettingsdialog.js @@ -1,47 +1,88 @@ -define(["dialogHelper", "layoutManager", "globalize", "require", "events", "homescreenSettings", "paper-icon-button-light", "css!./../formdialog"], function(dialogHelper, layoutManager, globalize, require, events, HomescreenSettings) { - "use strict"; +define(['dialogHelper', 'layoutManager', 'globalize', 'require', 'events', 'homescreenSettings', 'paper-icon-button-light', 'css!./../formdialog'], function (dialogHelper, layoutManager, globalize, require, events, HomescreenSettings) { + 'use strict'; function centerFocus(elem, horiz, on) { - require(["scrollHelper"], function(scrollHelper) { - var fn = on ? "on" : "off"; - scrollHelper.centerFocus[fn](elem, horiz) - }) + require(['scrollHelper'], function (scrollHelper) { + var fn = on ? 'on' : 'off'; + scrollHelper.centerFocus[fn](elem, horiz); + }); } function show(options) { - return new Promise(function(resolve, reject) { - require(["text!./homescreensettingsdialog.template.html"], function(template) { + return new Promise(function (resolve, reject) { + + require(['text!./homescreensettingsdialog.template.html'], function (template) { + var dialogOptions = { - removeOnClose: !0, - scrollY: !1 + removeOnClose: true, + scrollY: false }; - layoutManager.tv ? dialogOptions.size = "fullscreen" : dialogOptions.size = "medium-tall"; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'medium-tall'; + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"); - var html = "", - submitted = !1; - html += globalize.translateDocument(template, "sharedcomponents"), dlg.innerHTML = html, layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !0); + + dlg.classList.add('formDialog'); + + var html = ''; + var submitted = false; + + html += globalize.translateDocument(template, 'sharedcomponents'); + + dlg.innerHTML = html; + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, true); + } + var homescreenSettingsInstance = new HomescreenSettings({ serverId: options.serverId, userId: options.userId, - element: dlg.querySelector(".settingsContent"), + element: dlg.querySelector('.settingsContent'), userSettings: options.userSettings, - enableSaveButton: !1, - enableSaveConfirmation: !1 + enableSaveButton: false, + enableSaveConfirmation: false }); - dialogHelper.open(dlg), dlg.addEventListener("close", function() { - layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !1), submitted ? resolve() : reject() - }), dlg.querySelector(".btnCancel").addEventListener("click", function(e) { - dialogHelper.close(dlg) - }), dlg.querySelector(".btnSave").addEventListener("click", function(e) { - submitted = !0, homescreenSettingsInstance.submit() - }), events.on(homescreenSettingsInstance, "saved", function() { - submitted = !0, dialogHelper.close(dlg) - }) - }) - }) + + dialogHelper.open(dlg); + + dlg.addEventListener('close', function () { + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, false); + } + + if (submitted) { + resolve(); + } else { + reject(); + } + }); + + dlg.querySelector('.btnCancel').addEventListener('click', function (e) { + + dialogHelper.close(dlg); + }); + + dlg.querySelector('.btnSave').addEventListener('click', function (e) { + + submitted = true; + homescreenSettingsInstance.submit(); + }); + + events.on(homescreenSettingsInstance, 'saved', function () { + submitted = true; + dialogHelper.close(dlg); + }); + }); + }); } + return { show: show - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/homesections/homesections.css b/src/bower_components/emby-webcomponents/homesections/homesections.css index fac9ac5b64..7bb561ee01 100644 --- a/src/bower_components/emby-webcomponents/homesections/homesections.css +++ b/src/bower_components/emby-webcomponents/homesections/homesections.css @@ -1,10 +1,11 @@ -.homeLibraryButton { +.homeLibraryButton { min-width: 18%; - margin: .5em !important + margin: .5em !important; } -@media all and (max-width:50em) { +@media all and (max-width: 50em) { + .homeLibraryButton { - width: 46% !important + width: 46% !important; } } \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/homesections/homesections.js b/src/bower_components/emby-webcomponents/homesections/homesections.js index bf464d2fc3..a92fa975ed 100644 --- a/src/bower_components/emby-webcomponents/homesections/homesections.js +++ b/src/bower_components/emby-webcomponents/homesections/homesections.js @@ -1,121 +1,226 @@ -define(["connectionManager", "cardBuilder", "registrationServices", "appSettings", "dom", "apphost", "layoutManager", "imageLoader", "globalize", "itemShortcuts", "itemHelper", "appRouter", "emby-button", "paper-icon-button-light", "emby-itemscontainer", "emby-scroller", "emby-linkbutton", "css!./homesections"], function(connectionManager, cardBuilder, registrationServices, appSettings, dom, appHost, layoutManager, imageLoader, globalize, itemShortcuts, itemHelper, appRouter) { - "use strict"; +define(['connectionManager', 'cardBuilder', 'registrationServices', 'appSettings', 'dom', 'apphost', 'layoutManager', 'imageLoader', 'globalize', 'itemShortcuts', 'itemHelper', 'appRouter', 'emby-button', 'paper-icon-button-light', 'emby-itemscontainer', 'emby-scroller', 'emby-linkbutton', 'css!./homesections'], function (connectionManager, cardBuilder, registrationServices, appSettings, dom, appHost, layoutManager, imageLoader, globalize, itemShortcuts, itemHelper, appRouter) { + 'use strict'; function getDefaultSection(index) { + switch (index) { + case 0: - return "smalllibrarytiles"; + return 'smalllibrarytiles'; case 1: - return "resume"; + return 'resume'; case 2: - return "resumeaudio"; + return 'resumeaudio'; case 3: - return "livetv"; + return 'livetv'; case 4: - return "nextup"; + return 'nextup'; case 5: - return "latestmedia"; + return 'latestmedia'; case 6: - return "none"; + return 'none'; default: - return "" + return ''; } } function getAllSectionsToShow(userSettings, sectionCount) { - for (var sections = [], i = 0, length = sectionCount; i < length; i++) { - var section = userSettings.get("homesection" + i) || getDefaultSection(i); - "folders" === section && (section = getDefaultSection(0)), sections.push(section) + + var sections = []; + + for (var i = 0, length = sectionCount; i < length; i++) { + + var section = userSettings.get('homesection' + i) || getDefaultSection(i); + + if (section === 'folders') { + section = getDefaultSection(0); + } + + sections.push(section); } - return sections + + return sections; } function loadSections(elem, apiClient, user, userSettings) { - return getUserViews(apiClient, user.Id).then(function(userViews) { - var i, length, html = ""; - for (i = 0, length = 7; i < length; i++) html += '
'; - elem.innerHTML = html, elem.classList.add("homeSectionsContainer"); - var promises = [], - sections = getAllSectionsToShow(userSettings, 7); - for (i = 0, length = sections.length; i < length; i++) promises.push(loadSection(elem, apiClient, user, userSettings, userViews, sections, i)); - return Promise.all(promises).then(function() { - html = ""; - var style = "margin-top:4em;"; - return layoutManager.tv && (style += "padding: 0 7.5%;"), html += '", elem.insertAdjacentHTML("beforeend", html), resume(elem, { - refresh: !0, - returnPromise: !1 - }) - }) - }) + + return getUserViews(apiClient, user.Id).then(function (userViews) { + + var i, length; + var sectionCount = 7; + + var html = ''; + for (i = 0, length = sectionCount; i < length; i++) { + + html += '
'; + } + + elem.innerHTML = html; + elem.classList.add('homeSectionsContainer'); + + var promises = []; + var sections = getAllSectionsToShow(userSettings, sectionCount); + + for (i = 0, length = sections.length; i < length; i++) { + + promises.push(loadSection(elem, apiClient, user, userSettings, userViews, sections, i)); + } + + return Promise.all(promises).then(function () { + + html = ''; + + var style = 'margin-top:4em;'; + + if (layoutManager.tv) { + style += 'padding: 0 7.5%;'; + } + + html += ''; + + elem.insertAdjacentHTML('beforeend', html); + + return resume(elem, { + refresh: true, + returnPromise: false + }); + }); + }); } function destroySections(elem) { - var i, length, elems = elem.querySelectorAll(".itemsContainer"); - for (i = 0, length = elems.length; i < length; i++) elems[i].fetchData = null, elems[i].parentContainer = null, elems[i].getItemsHtml = null; - elem.innerHTML = "" + + var elems = elem.querySelectorAll('.itemsContainer'); + var i, length; + + for (i = 0, length = elems.length; i < length; i++) { + + elems[i].fetchData = null; + elems[i].parentContainer = null; + elems[i].getItemsHtml = null; + } + + elem.innerHTML = ''; } function pause(elem) { - var i, length, elems = elem.querySelectorAll(".itemsContainer"); - for (i = 0, length = elems.length; i < length; i++) elems[i].pause() + + var elems = elem.querySelectorAll('.itemsContainer'); + var i, length; + for (i = 0, length = elems.length; i < length; i++) { + + elems[i].pause(); + } } function resume(elem, options) { - var i, length, elems = elem.querySelectorAll(".itemsContainer"), - promises = []; - for (i = 0, length = elems.length; i < length; i++) promises.push(elems[i].resume(options)); - var promise = Promise.all(promises).then(function() { - elem.querySelector(".customizeSection").classList.remove("hide") + + var elems = elem.querySelectorAll('.itemsContainer'); + var i, length; + var promises = []; + + for (i = 0, length = elems.length; i < length; i++) { + promises.push(elems[i].resume(options)); + } + + var promise = Promise.all(promises).then(function () { + elem.querySelector('.customizeSection').classList.remove('hide'); }); - if (!options || !1 !== options.returnPromise) return promise + + if (!options || options.returnPromise !== false) { + return promise; + } } function loadSection(page, apiClient, user, userSettings, userViews, allSections, index) { - var section = allSections[index], - userId = user.Id, - elem = page.querySelector(".section" + index); - if ("latestmedia" === section) loadRecentlyAdded(elem, apiClient, user, userViews); - else { - if ("librarytiles" === section || "smalllibrarytiles" === section || "smalllibrarytiles-automobile" === section || "librarytiles-automobile" === section) return loadLibraryTiles(elem, apiClient, user, userSettings, "smallBackdrop", userViews, allSections); - if ("librarybuttons" === section) return loadlibraryButtons(elem, apiClient, user, userSettings, userViews); - if ("resume" === section) loadResumeVideo(elem, apiClient, userId); - else if ("resumeaudio" === section) loadResumeAudio(elem, apiClient, userId); - else if ("activerecordings" === section) loadLatestLiveTvRecordings(elem, !0, apiClient, userId); - else { - if ("nextup" !== section) return "onnow" === section || "livetv" === section ? loadOnNow(elem, apiClient, user) : (elem.innerHTML = "", Promise.resolve()); - loadNextUp(elem, apiClient, userId) - } + + var section = allSections[index]; + var userId = user.Id; + + var elem = page.querySelector('.section' + index); + + if (section === 'latestmedia') { + loadRecentlyAdded(elem, apiClient, user, userViews); } - return Promise.resolve() + else if (section === 'librarytiles' || section === 'smalllibrarytiles' || section === 'smalllibrarytiles-automobile' || section === 'librarytiles-automobile') { + loadLibraryTiles(elem, apiClient, user, userSettings, 'smallBackdrop', userViews, allSections); + } + else if (section === 'librarybuttons') { + loadlibraryButtons(elem, apiClient, user, userSettings, userViews, allSections); + } + else if (section === 'resume') { + loadResumeVideo(elem, apiClient, userId); + } + else if (section === 'resumeaudio') { + loadResumeAudio(elem, apiClient, userId); + } + else if (section === 'activerecordings') { + loadLatestLiveTvRecordings(elem, true, apiClient, userId); + } + else if (section === 'nextup') { + loadNextUp(elem, apiClient, userId); + } + else if (section === 'onnow' || section === 'livetv') { + return loadOnNow(elem, apiClient, user); + } + else { + + elem.innerHTML = ''; + + return Promise.resolve(); + } + return Promise.resolve(); } function getUserViews(apiClient, userId) { - return apiClient.getUserViews({}, userId || apiClient.getCurrentUserId()).then(function(result) { - return result.Items - }) + + return apiClient.getUserViews({}, userId || apiClient.getCurrentUserId()).then(function (result) { + + return result.Items; + }); } function enableScrollX() { - return !0 + return true; } function getSquareShape() { - return enableScrollX() ? "overflowSquare" : "square" + return enableScrollX() ? 'overflowSquare' : 'square'; } function getThumbShape() { - return enableScrollX() ? "overflowBackdrop" : "backdrop" + return enableScrollX() ? 'overflowBackdrop' : 'backdrop'; } function getPortraitShape() { - return enableScrollX() ? "autooverflow" : "auto" + return enableScrollX() ? 'autooverflow' : 'auto'; } function getLibraryButtonsHtml(items) { + var html = ""; - html += '
', html += '
', html += '

' + globalize.translate("sharedcomponents#HeaderMyMedia") + "

", layoutManager.tv || (html += ''), html += "
", html += '
'; + + html += '
'; + html += '
'; + html += '

' + globalize.translate('sharedcomponents#HeaderMyMedia') + '

'; + + if (!layoutManager.tv) { + html += ''; + } + + html += '
'; + + html += '
'; + + // "My Library" backgrounds for (var i = 0, length = items.length; i < length; i++) { - var icon, item = items[i]; + + var item = items[i]; + + var icon; + switch (item.CollectionType) { case "movies": icon = "local_movies"; @@ -127,6 +232,8 @@ define(["connectionManager", "cardBuilder", "registrationServices", "appSettings icon = "photo"; break; case "livetv": + icon = "live_tv"; + break; case "tvshows": icon = "live_tv"; break; @@ -137,476 +244,830 @@ define(["connectionManager", "cardBuilder", "registrationServices", "appSettings icon = "local_movies"; break; case "homevideos": + icon = "video_library"; + break; case "musicvideos": icon = "video_library"; break; case "books": + icon = "folder"; + break; case "channels": + icon = "folder"; + break; case "playlists": + icon = "folder"; + break; default: - icon = "folder" + icon = "folder"; + break; } - html += '' + icon + "" + item.Name + "" + + html += '' + icon + '' + item.Name + ''; } - return html += "
", html += "
" + + html += '
'; + html += '
'; + + return html; } function loadlibraryButtons(elem, apiClient, user, userSettings, userViews) { - return Promise.all([getAppInfo(apiClient), getDownloadsSectionHtml(apiClient, user, userSettings)]).then(function(responses) { - var infoHtml = responses[0], - downloadsHtml = responses[1]; - elem.classList.remove("verticalSection"); - var html = getLibraryButtonsHtml(userViews); - elem.innerHTML = html + downloadsHtml + infoHtml, bindHomeScreenSettingsIcon(elem, apiClient, user.Id, userSettings), infoHtml && bindAppInfoEvents(elem), imageLoader.lazyChildren(elem) - }) - } - - function bindAppInfoEvents(elem) { - elem.querySelector(".appInfoSection").addEventListener("click", function(e) { - dom.parentWithClass(e.target, "card") && registrationServices.showPremiereInfo() - }) + elem.classList.remove('verticalSection'); + var html = getLibraryButtonsHtml(userViews); + + elem.innerHTML = html; + bindHomeScreenSettingsIcon(elem, apiClient, user.Id, userSettings); + imageLoader.lazyChildren(elem); } + /** + * Returns a random integer between min (inclusive) and max (inclusive) + * Using Math.round() will give you a non-uniform distribution! + */ function getRandomInt(min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min - } - - function getAppInfo(apiClient) { - var cacheKey = "lastappinfopresent5", - lastDatePresented = parseInt(appSettings.get(cacheKey) || "0"); - return lastDatePresented ? (new Date).getTime() - lastDatePresented < 1728e5 ? Promise.resolve("") : registrationServices.validateFeature("dvr", { - showDialog: !1, - viewOnly: !0 - }).then(function() { - return appSettings.set(cacheKey, (new Date).getTime()), "" - }, function() { - appSettings.set(cacheKey, (new Date).getTime()); - var infos = [getPremiereInfo]; - return appHost.supports("otherapppromotions") && infos.push(getTheaterInfo), infos[getRandomInt(0, infos.length - 1)]() - }) : (appSettings.set(cacheKey, (new Date).getTime()), Promise.resolve("")) - } - - function getCard(img, shape) { - shape = shape || "backdropCard"; - var html = '
'; - return html += '
', html += '
', html += "
", html += "
" - } - - function getTheaterInfo() { - var html = ""; - html += '
', html += '
', html += '

Discover Jellyfin Theater

', html += '', html += "
"; - return html += '
', html += '

A beautiful app for your TV and large screen tablet. Jellyfin Theater runs on Windows, Xbox One, Raspberry Pi, Samsung Smart TVs, Sony PS4, Web Browsers, and more.

', html += '
', html += getCard("https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/apps/theater1.png"), html += getCard("https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/apps/theater2.png"), html += getCard("https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/apps/theater3.png"), html += "
", html += "
", html += "
" - } - - function getPremiereInfo() { - var html = ""; - return html += '
', html += '
', html += '

Discover Jellyfin Premiere

', html += '', html += "
", html += '
', html += '

Enjoy Jellyfin DVR, get free access to Jellyfin apps, and more.

', html += '
', html += getCard("https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/apps/theater1.png"), html += getCard("https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/apps/theater2.png"), html += getCard("https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/apps/theater3.png"), html += "
", html += "
", html += "
" + return Math.floor(Math.random() * (max - min + 1)) + min; } function getFetchLatestItemsFn(serverId, parentId, collectionType) { - return function() { - var apiClient = connectionManager.getApiClient(serverId), - limit = 16; - enableScrollX() ? "music" === collectionType && (limit = 30) : limit = "tvshows" === collectionType ? 5 : "music" === collectionType ? 9 : 8; + + return function () { + + var apiClient = connectionManager.getApiClient(serverId); + + var limit = 16; + + if (enableScrollX()) { + + if (collectionType === 'music') { + limit = 30; + } + } + else { + + if (collectionType === 'tvshows') { + limit = 5; + } else if (collectionType === 'music') { + limit = 9; + } else { + limit = 8; + } + } + var options = { + Limit: limit, Fields: "PrimaryImageAspectRatio,BasicSyncInfo", ImageTypeLimit: 1, EnableImageTypes: "Primary,Backdrop,Thumb", ParentId: parentId }; - return apiClient.getLatestItems(options) - } + + return apiClient.getLatestItems(options); + }; } function getLatestItemsHtmlFn(itemType, viewType) { - return function(items) { - var shape = "Channel" === itemType || "movies" === viewType ? getPortraitShape() : "music" === viewType ? getSquareShape() : getThumbShape(); + + return function (items) { + + var shape = itemType === 'Channel' || viewType === 'movies' ? + getPortraitShape() : + viewType === 'music' ? + getSquareShape() : + getThumbShape(); + + var cardLayout = false; + return cardBuilder.getCardsHtml({ items: items, shape: shape, - preferThumb: "movies" !== viewType && "Channel" !== itemType && "music" !== viewType ? "auto" : null, - showUnplayedIndicator: !1, - showChildCountIndicator: !0, - context: "home", - overlayText: !1, - centerText: !0, - overlayPlayButton: "photos" !== viewType, - allowBottomPadding: !enableScrollX() && !0, - cardLayout: !1, - showTitle: "photos" !== viewType, - showYear: "movies" === viewType || "tvshows" === viewType || !viewType, - showParentTitle: "music" === viewType || "tvshows" === viewType || !viewType || !1, + preferThumb: viewType !== 'movies' && itemType !== 'Channel' && viewType !== 'music' ? 'auto' : null, + showUnplayedIndicator: false, + showChildCountIndicator: true, + context: 'home', + overlayText: false, + centerText: !cardLayout, + overlayPlayButton: viewType !== 'photos', + allowBottomPadding: !enableScrollX() && !cardLayout, + cardLayout: cardLayout, + showTitle: viewType !== 'photos', + showYear: viewType === 'movies' || viewType === 'tvshows' || !viewType, + showParentTitle: viewType === 'music' || viewType === 'tvshows' || !viewType || (cardLayout && (viewType === 'tvshows')), lines: 2 - }) - } + }); + }; } function renderLatestSection(elem, apiClient, user, parent) { - var html = ""; - html += '
', layoutManager.tv ? html += '

' + globalize.translate("sharedcomponents#LatestFromLibrary", parent.Name) + "

" : (html += '', html += '

', html += globalize.translate("sharedcomponents#LatestFromLibrary", parent.Name), html += "

", html += '', html += "
"), html += "
", enableScrollX() ? html += '
' : html += '
', enableScrollX() && (html += "
"), html += "
", elem.innerHTML = html; - var itemsContainer = elem.querySelector(".itemsContainer"); - itemsContainer.fetchData = getFetchLatestItemsFn(apiClient.serverId(), parent.Id, parent.CollectionType), itemsContainer.getItemsHtml = getLatestItemsHtmlFn(parent.Type, parent.CollectionType), itemsContainer.parentContainer = elem + + var html = ''; + html += '
'; + if (!layoutManager.tv) { + + html += ''; + html += '

'; + html += globalize.translate('sharedcomponents#LatestFromLibrary', parent.Name); + html += '

'; + html += ''; + html += '
'; + + } else { + html += '

' + globalize.translate('sharedcomponents#LatestFromLibrary', parent.Name) + '

'; + } + html += '
'; + + if (enableScrollX()) { + html += '
'; + } else { + html += '
'; + } + + if (enableScrollX()) { + html += '
'; + } + html += '
'; + + elem.innerHTML = html; + + var itemsContainer = elem.querySelector('.itemsContainer'); + itemsContainer.fetchData = getFetchLatestItemsFn(apiClient.serverId(), parent.Id, parent.CollectionType); + itemsContainer.getItemsHtml = getLatestItemsHtmlFn(parent.Type, parent.CollectionType); + itemsContainer.parentContainer = elem; + } function loadRecentlyAdded(elem, apiClient, user, userViews) { - elem.classList.remove("verticalSection"); - for (var excludeViewTypes = ["playlists", "livetv", "boxsets", "channels"], i = 0, length = userViews.length; i < length; i++) { + + elem.classList.remove('verticalSection'); + + var excludeViewTypes = ['playlists', 'livetv', 'boxsets', 'channels']; + + for (var i = 0, length = userViews.length; i < length; i++) { + var item = userViews[i]; - if (-1 === user.Configuration.LatestItemsExcludes.indexOf(item.Id) && -1 === excludeViewTypes.indexOf(item.CollectionType || [])) { - var frag = document.createElement("div"); - frag.classList.add("verticalSection"), frag.classList.add("hide"), elem.appendChild(frag), renderLatestSection(frag, apiClient, user, item) + + if (user.Configuration.LatestItemsExcludes.indexOf(item.Id) !== -1) { + continue; } + + if (excludeViewTypes.indexOf(item.CollectionType || []) !== -1) { + continue; + } + + var frag = document.createElement('div'); + frag.classList.add('verticalSection'); + frag.classList.add('hide'); + elem.appendChild(frag); + + renderLatestSection(frag, apiClient, user, item); } } function getRequirePromise(deps) { - return new Promise(function(resolve, reject) { - require(deps, resolve) - }) + + return new Promise(function (resolve, reject) { + + require(deps, resolve); + }); } function showHomeScreenSettings(elem, options) { - return getRequirePromise(["homescreenSettingsDialog"]).then(function(homescreenSettingsDialog) { - return homescreenSettingsDialog.show(options).then(function() { - dom.parentWithClass(elem, "homeSectionsContainer").dispatchEvent(new CustomEvent("settingschange", { - cancelable: !1 - })) - }) - }) + return getRequirePromise(['homescreenSettingsDialog']).then(function (homescreenSettingsDialog) { + + return homescreenSettingsDialog.show(options).then(function () { + + dom.parentWithClass(elem, 'homeSectionsContainer').dispatchEvent(new CustomEvent('settingschange', { + cancelable: false + })); + }); + }); } function bindHomeScreenSettingsIcon(elem, apiClient, userId, userSettings) { - var btnHomeScreenSettings = elem.querySelector(".btnHomeScreenSettings"); - btnHomeScreenSettings && btnHomeScreenSettings.addEventListener("click", function() { + + var btnHomeScreenSettings = elem.querySelector('.btnHomeScreenSettings'); + if (!btnHomeScreenSettings) { + return; + } + + btnHomeScreenSettings.addEventListener('click', function () { showHomeScreenSettings(elem, { serverId: apiClient.serverId(), userId: userId, userSettings: userSettings - }) - }) - } - function getDownloadsSectionHtml(apiClient, user, userSettings) { - return appHost.supports("sync") && user.Policy.EnableContentDownloading ? (apiClient.getLatestOfflineItems ? apiClient.getLatestOfflineItems({ - Limit: 20, - Filters: "IsNotFolder" - }) : Promise.resolve([])).then(function(items) { - var html = ""; - return html += '
', html += '
', layoutManager.tv ? html += '

' + globalize.translate("sharedcomponents#HeaderMyDownloads") + "

" : (html += '', html += '

', html += globalize.translate("sharedcomponents#HeaderMyDownloads"), html += "

", html += '', html += "
", html += ''), html += "
", html += '
', html += cardBuilder.getCardsHtml({ - items: items, - preferThumb: "auto", - shape: "autooverflow", - overlayText: !1, - showTitle: !0, - showParentTitle: !0, - lazy: !0, - showDetailsMenu: !0, - overlayPlayButton: !0, - context: "home", - centerText: !0, - allowBottomPadding: !1, - cardLayout: !1, - showYear: !0, - lines: 2 - }), html += "
", html += "
" - }) : Promise.resolve("") + }); + }); } function loadLibraryTiles(elem, apiClient, user, userSettings, shape, userViews, allSections) { - elem.classList.remove("verticalSection"); - var html = "", - scrollX = !layoutManager.desktop; - return userViews.length && (html += '
', html += '
', html += '

' + globalize.translate("sharedcomponents#HeaderMyMedia") + "

", layoutManager.tv || (html += ''), html += "
", html += scrollX ? '
' : '
', html += cardBuilder.getCardsHtml({ - items: userViews, - shape: scrollX ? "overflowSmallBackdrop" : shape, - showTitle: !0, - centerText: !0, - overlayText: !1, - lazy: !0, - transition: !1, - allowBottomPadding: !scrollX - }), scrollX && (html += "
"), html += "
", html += "
"), Promise.all([getAppInfo(apiClient), getDownloadsSectionHtml(apiClient, user, userSettings)]).then(function(responses) { - var infoHtml = responses[0], - downloadsHtml = responses[1]; - elem.innerHTML = html + downloadsHtml + infoHtml, bindHomeScreenSettingsIcon(elem, apiClient, user.Id, userSettings), infoHtml && bindAppInfoEvents(elem), imageLoader.lazyChildren(elem) - }) + + elem.classList.remove('verticalSection'); + + var html = ''; + + var scrollX = !layoutManager.desktop; + + if (userViews.length) { + + html += '
'; + + html += '
'; + html += '

' + globalize.translate('sharedcomponents#HeaderMyMedia') + '

'; + + if (!layoutManager.tv) { + html += ''; + } + + html += '
'; + + if (scrollX) { + html += '
'; + } else { + html += '
'; + } + + html += cardBuilder.getCardsHtml({ + items: userViews, + shape: scrollX ? 'overflowSmallBackdrop' : shape, + showTitle: true, + centerText: true, + overlayText: false, + lazy: true, + transition: false, + allowBottomPadding: !scrollX + }); + + if (scrollX) { + html += '
'; + } + html += '
'; + html += '
'; + } + + elem.innerHTML = html; + bindHomeScreenSettingsIcon(elem, apiClient, user.Id, userSettings); + imageLoader.lazyChildren(elem); } function getContinueWatchingFetchFn(serverId) { - return function() { - var limit, apiClient = connectionManager.getApiClient(serverId), - screenWidth = dom.getWindowSize().innerWidth; - enableScrollX() ? limit = 12 : (limit = screenWidth >= 1920 ? 8 : screenWidth >= 1600 ? 8 : screenWidth >= 1200 ? 9 : 6, limit = Math.min(limit, 5)); + + return function () { + + var apiClient = connectionManager.getApiClient(serverId); + + var screenWidth = dom.getWindowSize().innerWidth; + + var limit; + + if (enableScrollX()) { + + limit = 12; + + } else { + + limit = screenWidth >= 1920 ? 8 : (screenWidth >= 1600 ? 8 : (screenWidth >= 1200 ? 9 : 6)); + limit = Math.min(limit, 5); + } + var options = { + Limit: limit, - Recursive: !0, + Recursive: true, Fields: "PrimaryImageAspectRatio,BasicSyncInfo", ImageTypeLimit: 1, EnableImageTypes: "Primary,Backdrop,Thumb", - EnableTotalRecordCount: !1, - MediaTypes: "Video" + EnableTotalRecordCount: false, + MediaTypes: 'Video' }; - return apiClient.getResumableItems(apiClient.getCurrentUserId(), options) - } + + return apiClient.getResumableItems(apiClient.getCurrentUserId(), options); + }; } function getContinueWatchingItemsHtml(items) { + + var cardLayout = false; + return cardBuilder.getCardsHtml({ items: items, - preferThumb: !0, + preferThumb: true, shape: getThumbShape(), - overlayText: !1, - showTitle: !0, - showParentTitle: !0, - lazy: !0, - showDetailsMenu: !0, - overlayPlayButton: !0, - context: "home", - centerText: !0, - allowBottomPadding: !1, - cardLayout: !1, - showYear: !0, + overlayText: false, + showTitle: true, + showParentTitle: true, + lazy: true, + showDetailsMenu: true, + overlayPlayButton: true, + context: 'home', + centerText: !cardLayout, + allowBottomPadding: false, + cardLayout: cardLayout, + showYear: true, lines: 2 - }) + }); } function loadResumeVideo(elem, apiClient, userId) { - var html = ""; - html += '

' + globalize.translate("sharedcomponents#HeaderContinueWatching") + "

", enableScrollX() ? html += '
' : html += '
', enableScrollX() && (html += "
"), html += "
", elem.classList.add("hide"), elem.innerHTML = html; - var itemsContainer = elem.querySelector(".itemsContainer"); - itemsContainer.fetchData = getContinueWatchingFetchFn(apiClient.serverId()), itemsContainer.getItemsHtml = getContinueWatchingItemsHtml, itemsContainer.parentContainer = elem + + var html = ''; + html += '

' + globalize.translate('sharedcomponents#HeaderContinueWatching') + '

'; + + if (enableScrollX()) { + html += '
'; + } else { + html += '
'; + } + + if (enableScrollX()) { + html += '
'; + } + html += '
'; + + elem.classList.add('hide'); + elem.innerHTML = html; + + var itemsContainer = elem.querySelector('.itemsContainer'); + itemsContainer.fetchData = getContinueWatchingFetchFn(apiClient.serverId()); + itemsContainer.getItemsHtml = getContinueWatchingItemsHtml; + itemsContainer.parentContainer = elem; } function getContinueListeningFetchFn(serverId) { - return function() { - var limit, apiClient = connectionManager.getApiClient(serverId), - screenWidth = dom.getWindowSize().innerWidth; - enableScrollX() ? limit = 12 : (limit = screenWidth >= 1920 ? 8 : screenWidth >= 1600 ? 8 : screenWidth >= 1200 ? 9 : 6, limit = Math.min(limit, 5)); + + return function () { + + var apiClient = connectionManager.getApiClient(serverId); + + var screenWidth = dom.getWindowSize().innerWidth; + + var limit; + + if (enableScrollX()) { + + limit = 12; + + } else { + + limit = screenWidth >= 1920 ? 8 : (screenWidth >= 1600 ? 8 : (screenWidth >= 1200 ? 9 : 6)); + limit = Math.min(limit, 5); + } + var options = { + Limit: limit, - Recursive: !0, + Recursive: true, Fields: "PrimaryImageAspectRatio,BasicSyncInfo", ImageTypeLimit: 1, EnableImageTypes: "Primary,Backdrop,Thumb", - EnableTotalRecordCount: !1, - MediaTypes: "Audio" + EnableTotalRecordCount: false, + MediaTypes: 'Audio' }; - return apiClient.getResumableItems(apiClient.getCurrentUserId(), options) - } + + return apiClient.getResumableItems(apiClient.getCurrentUserId(), options); + }; } function getContinueListeningItemsHtml(items) { + + var cardLayout = false; + return cardBuilder.getCardsHtml({ items: items, - preferThumb: !0, + preferThumb: true, shape: getThumbShape(), - overlayText: !1, - showTitle: !0, - showParentTitle: !0, - lazy: !0, - showDetailsMenu: !0, - overlayPlayButton: !0, - context: "home", - centerText: !0, - allowBottomPadding: !1, - cardLayout: !1, - showYear: !0, + overlayText: false, + showTitle: true, + showParentTitle: true, + lazy: true, + showDetailsMenu: true, + overlayPlayButton: true, + context: 'home', + centerText: !cardLayout, + allowBottomPadding: false, + cardLayout: cardLayout, + showYear: true, lines: 2 - }) + }); } function loadResumeAudio(elem, apiClient, userId) { - var html = ""; - html += '

' + globalize.translate("sharedcomponents#HeaderContinueWatching") + "

", enableScrollX() ? html += '
' : html += '
', enableScrollX() && (html += "
"), html += "
", elem.classList.add("hide"), elem.innerHTML = html; - var itemsContainer = elem.querySelector(".itemsContainer"); - itemsContainer.fetchData = getContinueListeningFetchFn(apiClient.serverId()), itemsContainer.getItemsHtml = getContinueListeningItemsHtml, itemsContainer.parentContainer = elem + + var html = ''; + html += '

' + globalize.translate('sharedcomponents#HeaderContinueWatching') + '

'; + + if (enableScrollX()) { + html += '
'; + } else { + html += '
'; + } + + if (enableScrollX()) { + html += '
'; + } + html += '
'; + + elem.classList.add('hide'); + elem.innerHTML = html; + + var itemsContainer = elem.querySelector('.itemsContainer'); + itemsContainer.fetchData = getContinueListeningFetchFn(apiClient.serverId()); + itemsContainer.getItemsHtml = getContinueListeningItemsHtml; + itemsContainer.parentContainer = elem; } function bindUnlockClick(elem) { - var btnUnlock = elem.querySelector(".btnUnlock"); - btnUnlock && btnUnlock.addEventListener("click", function(e) { - registrationServices.validateFeature("livetv", { - viewOnly: !0 - }).then(function() { - dom.parentWithClass(elem, "homeSectionsContainer").dispatchEvent(new CustomEvent("settingschange", { - cancelable: !1 - })) - }) - }) - } - function getOnNowFetchFn(serverId) { - return function() { - var apiClient = connectionManager.getApiClient(serverId); - return apiClient.getLiveTvRecommendedPrograms({ - userId: apiClient.getCurrentUserId(), - IsAiring: !0, - limit: 24, - ImageTypeLimit: 1, - EnableImageTypes: "Primary,Thumb,Backdrop", - EnableTotalRecordCount: !1, - Fields: "ChannelInfo,PrimaryImageAspectRatio" - }) + var btnUnlock = elem.querySelector('.btnUnlock'); + if (btnUnlock) { + btnUnlock.addEventListener('click', function (e) { + + registrationServices.validateFeature('livetv', { + + viewOnly: true + + }).then(function () { + + dom.parentWithClass(elem, 'homeSectionsContainer').dispatchEvent(new CustomEvent('settingschange', { + cancelable: false + })); + }); + }); } } + function getOnNowFetchFn(serverId) { + + return function () { + + var apiClient = connectionManager.getApiClient(serverId); + + return apiClient.getLiveTvRecommendedPrograms({ + + userId: apiClient.getCurrentUserId(), + IsAiring: true, + limit: 24, + ImageTypeLimit: 1, + EnableImageTypes: "Primary,Thumb,Backdrop", + EnableTotalRecordCount: false, + Fields: "ChannelInfo,PrimaryImageAspectRatio" + + }); + }; + } + function getOnNowItemsHtml(items) { + + var cardLayout = false; + return cardBuilder.getCardsHtml({ items: items, - preferThumb: "auto", - inheritThumb: !1, - shape: enableScrollX() ? "autooverflow" : "auto", - showParentTitleOrTitle: !0, - showTitle: !0, - centerText: !0, - coverImage: !0, - overlayText: !1, + preferThumb: 'auto', + inheritThumb: false, + shape: (enableScrollX() ? 'autooverflow' : 'auto'), + showParentTitleOrTitle: true, + showTitle: true, + centerText: true, + coverImage: true, + overlayText: false, allowBottomPadding: !enableScrollX(), - showAirTime: !0, - showChannelName: !1, - showAirDateTime: !1, - showAirEndTime: !0, + showAirTime: true, + showChannelName: false, + showAirDateTime: false, + showAirEndTime: true, defaultShape: getThumbShape(), lines: 3, - overlayPlayButton: !0 - }) + overlayPlayButton: true + }); } function loadOnNow(elem, apiClient, user) { - if (!user.Policy.EnableLiveTvAccess) return Promise.resolve(); + + if (!user.Policy.EnableLiveTvAccess) { + return Promise.resolve(); + } + var promises = []; - promises.push(registrationServices.validateFeature("livetv", { - viewOnly: !0, - showDialog: !1 - }).then(function() { - return !0 - }, function() { - return !1 - })); - user.Id; - return promises.push(apiClient.getLiveTvRecommendedPrograms({ + + promises.push(registrationServices.validateFeature('livetv', + { + viewOnly: true, + showDialog: false + }).then(function () { + return true; + }, function () { + return false; + })); + + var userId = user.Id; + + promises.push(apiClient.getLiveTvRecommendedPrograms({ + userId: apiClient.getCurrentUserId(), - IsAiring: !0, + IsAiring: true, limit: 1, ImageTypeLimit: 1, EnableImageTypes: "Primary,Thumb,Backdrop", - EnableTotalRecordCount: !1, + EnableTotalRecordCount: false, Fields: "ChannelInfo,PrimaryImageAspectRatio" - })), Promise.all(promises).then(function(responses) { - var registered = responses[0], - result = responses[1], - html = ""; + + })); + + return Promise.all(promises).then(function (responses) { + + var registered = responses[0]; + var result = responses[1]; + var html = ''; + if (result.Items.length && registered) { - elem.classList.remove("padded-left"), elem.classList.remove("padded-right"), elem.classList.remove("padded-bottom"), elem.classList.remove("verticalSection"), html += '
', html += '
', html += '

' + globalize.translate("sharedcomponents#LiveTV") + "

", html += "
", enableScrollX() ? (html += '
', html += '", html += '
', html += '
', layoutManager.tv ? html += '

' + globalize.translate("sharedcomponents#HeaderOnNow") + "

" : (html += '', html += '

', html += globalize.translate("sharedcomponents#HeaderOnNow"), html += "

", html += '', html += "
"), html += "
", enableScrollX() ? html += '
' : html += '
', enableScrollX() && (html += "
"), html += "
", html += "
", elem.innerHTML = html; - var itemsContainer = elem.querySelector(".itemsContainer"); - itemsContainer.parentContainer = elem, itemsContainer.fetchData = getOnNowFetchFn(apiClient.serverId()), itemsContainer.getItemsHtml = getOnNowItemsHtml - } else result.Items.length && !registered && (elem.classList.add("padded-left"), elem.classList.add("padded-right"), elem.classList.add("padded-bottom"), html += '

' + globalize.translate("sharedcomponents#LiveTvRequiresUnlock") + "

", html += '", elem.innerHTML = html); - bindUnlockClick(elem) - }) + section: 'dvrschedule' + + }) + '" class="raised">' + globalize.translate('sharedcomponents#Schedule') + ''; + + html += '
'; + + if (enableScrollX()) { + html += '
'; + } + + html += '
'; + html += '
'; + + html += '
'; + html += '
'; + + if (!layoutManager.tv) { + + html += ''; + html += '

'; + html += globalize.translate('sharedcomponents#HeaderOnNow'); + html += '

'; + html += ''; + html += '
'; + + } else { + html += '

' + globalize.translate('sharedcomponents#HeaderOnNow') + '

'; + } + html += '
'; + + if (enableScrollX()) { + html += '
'; + } else { + html += '
'; + } + + if (enableScrollX()) { + html += '
'; + } + + html += '
'; + html += '
'; + + elem.innerHTML = html; + + var itemsContainer = elem.querySelector('.itemsContainer'); + itemsContainer.parentContainer = elem; + itemsContainer.fetchData = getOnNowFetchFn(apiClient.serverId()); + itemsContainer.getItemsHtml = getOnNowItemsHtml; + + } else if (result.Items.length && !registered) { + + elem.classList.add('padded-left'); + elem.classList.add('padded-right'); + elem.classList.add('padded-bottom'); + + html += '

' + globalize.translate('sharedcomponents#LiveTvRequiresUnlock') + '

'; + html += ''; + + elem.innerHTML = html; + } + + bindUnlockClick(elem); + }); } function getNextUpFetchFn(serverId) { - return function() { + + return function () { + var apiClient = connectionManager.getApiClient(serverId); + return apiClient.getNextUpEpisodes({ + Limit: enableScrollX() ? 24 : 15, Fields: "PrimaryImageAspectRatio,SeriesInfo,DateCreated,BasicSyncInfo", UserId: apiClient.getCurrentUserId(), ImageTypeLimit: 1, EnableImageTypes: "Primary,Backdrop,Banner,Thumb", - EnableTotalRecordCount: !1 - }) - } + EnableTotalRecordCount: false + }); + }; } function getNextUpItemsHtml(items) { + + var cardLayout = false; + return cardBuilder.getCardsHtml({ items: items, - preferThumb: !0, + preferThumb: true, shape: getThumbShape(), - overlayText: !1, - showTitle: !0, - showParentTitle: !0, - lazy: !0, - overlayPlayButton: !0, - context: "home", - centerText: !0, + overlayText: false, + showTitle: true, + showParentTitle: true, + lazy: true, + overlayPlayButton: true, + context: 'home', + centerText: !cardLayout, allowBottomPadding: !enableScrollX(), - cardLayout: !1 - }) + cardLayout: cardLayout + }); } function loadNextUp(elem, apiClient, userId) { - var html = ""; - html += '
', layoutManager.tv ? html += '

' + globalize.translate("sharedcomponents#HeaderNextUp") + "

" : (html += '', html += '

', html += globalize.translate("sharedcomponents#HeaderNextUp"), html += "

", html += '', html += "
"), html += "
", enableScrollX() ? html += '
' : html += '
', enableScrollX() && (html += "
"), html += "
", elem.classList.add("hide"), elem.innerHTML = html; - var itemsContainer = elem.querySelector(".itemsContainer"); - itemsContainer.fetchData = getNextUpFetchFn(apiClient.serverId()), itemsContainer.getItemsHtml = getNextUpItemsHtml, itemsContainer.parentContainer = elem + + var html = ''; + html += '
'; + if (!layoutManager.tv) { + + html += ''; + html += '

'; + html += globalize.translate('sharedcomponents#HeaderNextUp'); + html += '

'; + html += ''; + html += '
'; + + } else { + html += '

' + globalize.translate('sharedcomponents#HeaderNextUp') + '

'; + } + html += '
'; + + if (enableScrollX()) { + html += '
'; + } else { + html += '
'; + } + + if (enableScrollX()) { + html += '
'; + } + + html += '
'; + + elem.classList.add('hide'); + elem.innerHTML = html; + + var itemsContainer = elem.querySelector('.itemsContainer'); + itemsContainer.fetchData = getNextUpFetchFn(apiClient.serverId()); + itemsContainer.getItemsHtml = getNextUpItemsHtml; + itemsContainer.parentContainer = elem; } function getLatestRecordingsFetchFn(serverId, activeRecordingsOnly) { - return function() { + + return function () { + var apiClient = connectionManager.getApiClient(serverId); + return apiClient.getLiveTvRecordings({ + userId: apiClient.getCurrentUserId(), Limit: enableScrollX() ? 12 : 5, Fields: "PrimaryImageAspectRatio,BasicSyncInfo", - EnableTotalRecordCount: !1, - IsLibraryItem: !!activeRecordingsOnly && null, - IsInProgress: !!activeRecordingsOnly || null - }) - } + EnableTotalRecordCount: false, + IsLibraryItem: activeRecordingsOnly ? null : false, + IsInProgress: activeRecordingsOnly ? true : null + + }); + }; } function getLatestRecordingItemsHtml(activeRecordingsOnly) { - return function(items) { + + return function (items) { + var cardLayout = false; + return cardBuilder.getCardsHtml({ items: items, - shape: enableScrollX() ? "autooverflow" : "auto", - showTitle: !0, - showParentTitle: !0, - coverImage: !0, - lazy: !0, - showDetailsMenu: !0, - centerText: !0, - overlayText: !1, - showYear: !0, + shape: enableScrollX() ? 'autooverflow' : 'auto', + showTitle: true, + showParentTitle: true, + coverImage: true, + lazy: true, + showDetailsMenu: true, + centerText: true, + overlayText: false, + showYear: true, lines: 2, overlayPlayButton: !activeRecordingsOnly, allowBottomPadding: !enableScrollX(), - preferThumb: !0, - cardLayout: !1, + preferThumb: true, + cardLayout: false, overlayMoreButton: activeRecordingsOnly, - action: activeRecordingsOnly ? "none" : null, + action: activeRecordingsOnly ? 'none' : null, centerPlayButton: activeRecordingsOnly - }) - } + }); + }; } function loadLatestLiveTvRecordings(elem, activeRecordingsOnly, apiClient, userId) { - var title = activeRecordingsOnly ? globalize.translate("sharedcomponents#HeaderActiveRecordings") : globalize.translate("sharedcomponents#HeaderLatestRecordings"), - html = ""; - html += '
', html += '

' + title + "

", layoutManager.tv, html += "
", enableScrollX() ? html += '
' : html += '
', enableScrollX() && (html += "
"), html += "
", elem.classList.add("hide"), elem.innerHTML = html; - var itemsContainer = elem.querySelector(".itemsContainer"); - itemsContainer.fetchData = getLatestRecordingsFetchFn(apiClient.serverId(), activeRecordingsOnly), itemsContainer.getItemsHtml = getLatestRecordingItemsHtml(activeRecordingsOnly), itemsContainer.parentContainer = elem + + var title = activeRecordingsOnly ? + globalize.translate('sharedcomponents#HeaderActiveRecordings') : + globalize.translate('sharedcomponents#HeaderLatestRecordings'); + + var html = ''; + + html += '
'; + html += '

' + title + '

'; + if (!layoutManager.tv) { + //html += ''; + //html += ''; + } + html += '
'; + + if (enableScrollX()) { + html += '
'; + } else { + html += '
'; + } + + if (enableScrollX()) { + html += '
'; + } + + html += '
'; + + elem.classList.add('hide'); + elem.innerHTML = html; + + var itemsContainer = elem.querySelector('.itemsContainer'); + itemsContainer.fetchData = getLatestRecordingsFetchFn(apiClient.serverId(), activeRecordingsOnly); + itemsContainer.getItemsHtml = getLatestRecordingItemsHtml(activeRecordingsOnly); + itemsContainer.parentContainer = elem; } + return { loadLibraryTiles: loadLibraryTiles, getDefaultSection: getDefaultSection, @@ -614,5 +1075,5 @@ define(["connectionManager", "cardBuilder", "registrationServices", "appSettings destroySections: destroySections, pause: pause, resume: resume - } -}); + }; +}); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/htmlaudioplayer/plugin.js b/src/bower_components/emby-webcomponents/htmlaudioplayer/plugin.js index e36ac4d469..daf22790a6 100644 --- a/src/bower_components/emby-webcomponents/htmlaudioplayer/plugin.js +++ b/src/bower_components/emby-webcomponents/htmlaudioplayer/plugin.js @@ -1,218 +1,515 @@ -define(["events", "browser", "require", "apphost", "appSettings", "htmlMediaHelper"], function(events, browser, require, appHost, appSettings, htmlMediaHelper) { +define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelper'], function (events, browser, require, appHost, appSettings, htmlMediaHelper) { "use strict"; function getDefaultProfile() { - return new Promise(function(resolve, reject) { - require(["browserdeviceprofile"], function(profileBuilder) { - resolve(profileBuilder({})) - }) - }) + + return new Promise(function (resolve, reject) { + + require(['browserdeviceprofile'], function (profileBuilder) { + + resolve(profileBuilder({})); + }); + }); } + var fadeTimeout; function fade(instance, elem, startingVolume) { - instance._isFadingOut = !0; - var newVolume = Math.max(0, startingVolume - .15); - return console.log("fading volume to " + newVolume), elem.volume = newVolume, newVolume <= 0 ? (instance._isFadingOut = !1, Promise.resolve()) : new Promise(function(resolve, reject) { - cancelFadeTimeout(), fadeTimeout = setTimeout(function() { - fade(instance, elem, newVolume).then(resolve, reject) - }, 100) - }) + + instance._isFadingOut = true; + + // Need to record the starting volume on each pass rather than querying elem.volume + // This is due to iOS safari not allowing volume changes and always returning the system volume value + + var newVolume = Math.max(0, startingVolume - 0.15); + console.log('fading volume to ' + newVolume); + elem.volume = newVolume; + + if (newVolume <= 0) { + + instance._isFadingOut = false; + return Promise.resolve(); + } + + return new Promise(function (resolve, reject) { + + cancelFadeTimeout(); + + fadeTimeout = setTimeout(function () { + + fade(instance, elem, newVolume).then(resolve, reject); + }, 100); + }); } function cancelFadeTimeout() { var timeout = fadeTimeout; - timeout && (clearTimeout(timeout), fadeTimeout = null) + if (timeout) { + clearTimeout(timeout); + fadeTimeout = null; + } } function supportsFade() { - return !browser.tv + + if (browser.tv) { + // Not working on tizen. + // We could possibly enable on other tv's, but all smart tv browsers tend to be pretty primitive + return false; + } + + return true; } function requireHlsPlayer(callback) { - require(["hlsjs"], function(hls) { - window.Hls = hls, callback() - }) + require(['hlsjs'], function (hls) { + window.Hls = hls; + callback(); + }); } function enableHlsPlayer(url, item, mediaSource, mediaType) { - return htmlMediaHelper.enableHlsJsPlayer(mediaSource.RunTimeTicks, mediaType) ? -1 !== url.indexOf(".m3u8") ? Promise.resolve() : new Promise(function(resolve, reject) { - require(["fetchHelper"], function(fetchHelper) { + + if (!htmlMediaHelper.enableHlsJsPlayer(mediaSource.RunTimeTicks, mediaType)) { + + return Promise.reject(); + } + + if (url.indexOf('.m3u8') !== -1) { + return Promise.resolve(); + } + + // issue head request to get content type + return new Promise(function (resolve, reject) { + + require(['fetchHelper'], function (fetchHelper) { fetchHelper.ajax({ url: url, - type: "HEAD" - }).then(function(response) { - "application/x-mpegurl" === (response.headers.get("Content-Type") || "").toLowerCase() ? resolve() : reject() - }, reject) - }) - }) : Promise.reject() + type: 'HEAD' + }).then(function (response) { + + var contentType = (response.headers.get('Content-Type') || '').toLowerCase(); + if (contentType === 'application/x-mpegurl') { + resolve(); + } else { + reject(); + } + + }, reject); + }); + }); } function HtmlAudioPlayer() { + + var self = this; + + self.name = 'Html Audio Player'; + self.type = 'mediaplayer'; + self.id = 'htmlaudioplayer'; + + // Let any players created by plugins take priority + self.priority = 1; + + self.play = function (options) { + + self._started = false; + self._timeUpdated = false; + + self._currentTime = null; + + var elem = createMediaElement(options); + + return setCurrentSrc(elem, options); + }; + function setCurrentSrc(elem, options) { - elem.removeEventListener("error", onError), unBindEvents(elem), bindEvents(elem); + + elem.removeEventListener('error', onError); + + unBindEvents(elem); + bindEvents(elem); + var val = options.url; - console.log("playing url: " + val); - var seconds = (options.playerStartPositionTicks || 0) / 1e7; - seconds && (val += "#t=" + seconds), htmlMediaHelper.destroyHlsPlayer(self), self._currentPlayOptions = options; + console.log('playing url: ' + val); + + // Convert to seconds + var seconds = (options.playerStartPositionTicks || 0) / 10000000; + if (seconds) { + val += '#t=' + seconds; + } + + htmlMediaHelper.destroyHlsPlayer(self); + + self._currentPlayOptions = options; + var crossOrigin = htmlMediaHelper.getCrossOriginValue(options.mediaSource); - return crossOrigin && (elem.crossOrigin = crossOrigin), enableHlsPlayer(val, options.item, options.mediaSource, "Audio").then(function() { - return new Promise(function(resolve, reject) { - requireHlsPlayer(function() { + if (crossOrigin) { + elem.crossOrigin = crossOrigin; + } + + return enableHlsPlayer(val, options.item, options.mediaSource, 'Audio').then(function () { + + return new Promise(function (resolve, reject) { + + requireHlsPlayer(function () { var hls = new Hls({ - manifestLoadingTimeOut: 2e4 + manifestLoadingTimeOut: 20000 + //appendErrorMaxRetry: 6, + //debug: true }); - hls.loadSource(val), hls.attachMedia(elem), htmlMediaHelper.bindEventsToHlsPlayer(self, hls, elem, onError, resolve, reject), self._hlsPlayer = hls, self._currentSrc = val - }) - }) - }, function() { - return elem.autoplay = !0, htmlMediaHelper.applySrc(elem, val, options).then(function() { - return self._currentSrc = val, htmlMediaHelper.playWithPromise(elem, onError) - }) - }) + hls.loadSource(val); + hls.attachMedia(elem); + + htmlMediaHelper.bindEventsToHlsPlayer(self, hls, elem, onError, resolve, reject); + + self._hlsPlayer = hls; + + self._currentSrc = val; + }); + }); + + + }, function () { + + elem.autoplay = true; + + return htmlMediaHelper.applySrc(elem, val, options).then(function () { + + self._currentSrc = val; + + return htmlMediaHelper.playWithPromise(elem, onError); + }); + }); } function bindEvents(elem) { - elem.addEventListener("timeupdate", onTimeUpdate), elem.addEventListener("ended", onEnded), elem.addEventListener("volumechange", onVolumeChange), elem.addEventListener("pause", onPause), elem.addEventListener("playing", onPlaying), elem.addEventListener("play", onPlay) + elem.addEventListener('timeupdate', onTimeUpdate); + elem.addEventListener('ended', onEnded); + elem.addEventListener('volumechange', onVolumeChange); + elem.addEventListener('pause', onPause); + elem.addEventListener('playing', onPlaying); + elem.addEventListener('play', onPlay); } function unBindEvents(elem) { - elem.removeEventListener("timeupdate", onTimeUpdate), elem.removeEventListener("ended", onEnded), elem.removeEventListener("volumechange", onVolumeChange), elem.removeEventListener("pause", onPause), elem.removeEventListener("playing", onPlaying), elem.removeEventListener("play", onPlay) + elem.removeEventListener('timeupdate', onTimeUpdate); + elem.removeEventListener('ended', onEnded); + elem.removeEventListener('volumechange', onVolumeChange); + elem.removeEventListener('pause', onPause); + elem.removeEventListener('playing', onPlaying); + elem.removeEventListener('play', onPlay); } - function createMediaElement() { + self.stop = function (destroyPlayer) { + + cancelFadeTimeout(); + var elem = self._mediaElement; - return elem || (elem = document.querySelector(".mediaPlayerAudio"), elem || (elem = document.createElement("audio"), elem.classList.add("mediaPlayerAudio"), elem.classList.add("hide"), document.body.appendChild(elem)), elem.volume = htmlMediaHelper.getSavedVolume(), self._mediaElement = elem, elem) + var src = self._currentSrc; + + if (elem && src) { + + if (!destroyPlayer || !supportsFade()) { + + elem.pause(); + + htmlMediaHelper.onEndedInternal(self, elem, onError); + + if (destroyPlayer) { + self.destroy(); + } + return Promise.resolve(); + } + + var originalVolume = elem.volume; + + return fade(self, elem, elem.volume).then(function () { + + elem.pause(); + elem.volume = originalVolume; + + htmlMediaHelper.onEndedInternal(self, elem, onError); + + if (destroyPlayer) { + self.destroy(); + } + }); + } + return Promise.resolve(); + }; + + self.destroy = function () { + unBindEvents(self._mediaElement); + }; + + function createMediaElement() { + + var elem = self._mediaElement; + + if (elem) { + return elem; + } + + elem = document.querySelector('.mediaPlayerAudio'); + + if (!elem) { + elem = document.createElement('audio'); + elem.classList.add('mediaPlayerAudio'); + elem.classList.add('hide'); + + document.body.appendChild(elem); + } + + elem.volume = htmlMediaHelper.getSavedVolume(); + + self._mediaElement = elem; + + return elem; } function onEnded() { - htmlMediaHelper.onEndedInternal(self, this, onError) + + htmlMediaHelper.onEndedInternal(self, this, onError); } function onTimeUpdate() { + + // Get the player position + the transcoding offset var time = this.currentTime; - self._isFadingOut || (self._currentTime = time, events.trigger(self, "timeupdate")) + + // Don't trigger events after user stop + if (!self._isFadingOut) { + self._currentTime = time; + events.trigger(self, 'timeupdate'); + } } function onVolumeChange() { - self._isFadingOut || (htmlMediaHelper.saveVolume(this.volume), events.trigger(self, "volumechange")) + + if (!self._isFadingOut) { + htmlMediaHelper.saveVolume(this.volume); + events.trigger(self, 'volumechange'); + } } function onPlaying(e) { - self._started || (self._started = !0, this.removeAttribute("controls"), htmlMediaHelper.seekOnPlaybackStart(self, e.target, self._currentPlayOptions.playerStartPositionTicks)), events.trigger(self, "playing") + + if (!self._started) { + self._started = true; + this.removeAttribute('controls'); + + htmlMediaHelper.seekOnPlaybackStart(self, e.target, self._currentPlayOptions.playerStartPositionTicks); + } + events.trigger(self, 'playing'); } function onPlay(e) { - events.trigger(self, "unpause") + + events.trigger(self, 'unpause'); } function onPause() { - events.trigger(self, "pause") + events.trigger(self, 'pause'); } function onError() { - var errorCode = this.error ? this.error.code || 0 : 0, - errorMessage = this.error ? this.error.message || "" : ""; - console.log("Media element error: " + errorCode.toString() + " " + errorMessage); + + var errorCode = this.error ? (this.error.code || 0) : 0; + var errorMessage = this.error ? (this.error.message || '') : ''; + console.log('Media element error: ' + errorCode.toString() + ' ' + errorMessage); + var type; + switch (errorCode) { case 1: + // MEDIA_ERR_ABORTED + // This will trigger when changing media while something is playing return; case 2: - type = "network"; + // MEDIA_ERR_NETWORK + type = 'network'; break; case 3: - if (self._hlsPlayer) return void htmlMediaHelper.handleHlsJsMediaError(self); - type = "mediadecodeerror"; + // MEDIA_ERR_DECODE + if (self._hlsPlayer) { + htmlMediaHelper.handleHlsJsMediaError(self); + return; + } else { + type = 'mediadecodeerror'; + } break; case 4: - type = "medianotsupported"; + // MEDIA_ERR_SRC_NOT_SUPPORTED + type = 'medianotsupported'; break; default: - return + // seeing cases where Edge is firing error events with no error code + // example is start playing something, then immediately change src to something else + return; } - htmlMediaHelper.onErrorInternal(self, type) - } - var self = this; - self.name = "Html Audio Player", self.type = "mediaplayer", self.id = "htmlaudioplayer", self.priority = 1, self.play = function(options) { - return self._started = !1, self._timeUpdated = !1, self._currentTime = null, setCurrentSrc(createMediaElement(), options) - }, self.stop = function(destroyPlayer) { - cancelFadeTimeout(); - var elem = self._mediaElement, - src = self._currentSrc; - if (elem && src) { - if (!destroyPlayer || !supportsFade()) return elem.pause(), htmlMediaHelper.onEndedInternal(self, elem, onError), destroyPlayer && self.destroy(), Promise.resolve(); - var originalVolume = elem.volume; - return fade(self, elem, elem.volume).then(function() { - elem.pause(), elem.volume = originalVolume, htmlMediaHelper.onEndedInternal(self, elem, onError), destroyPlayer && self.destroy() - }) - } - return Promise.resolve() - }, self.destroy = function() { - unBindEvents(self._mediaElement) + + htmlMediaHelper.onErrorInternal(self, type); } } - var fadeTimeout; - return HtmlAudioPlayer.prototype.currentSrc = function() { - return this._currentSrc - }, HtmlAudioPlayer.prototype.canPlayMediaType = function(mediaType) { - return "audio" === (mediaType || "").toLowerCase() - }, HtmlAudioPlayer.prototype.getDeviceProfile = function(item) { - return appHost.getDeviceProfile ? appHost.getDeviceProfile(item) : getDefaultProfile() - }, HtmlAudioPlayer.prototype.currentTime = function(val) { + + HtmlAudioPlayer.prototype.currentSrc = function () { + return this._currentSrc; + }; + + HtmlAudioPlayer.prototype.canPlayMediaType = function (mediaType) { + + return (mediaType || '').toLowerCase() === 'audio'; + }; + + HtmlAudioPlayer.prototype.getDeviceProfile = function (item) { + + if (appHost.getDeviceProfile) { + return appHost.getDeviceProfile(item); + } + + return getDefaultProfile(); + }; + + // Save this for when playback stops, because querying the time at that point might return 0 + HtmlAudioPlayer.prototype.currentTime = function (val) { + var mediaElement = this._mediaElement; if (mediaElement) { - if (null != val) return void(mediaElement.currentTime = val / 1e3); + if (val != null) { + mediaElement.currentTime = val / 1000; + return; + } + var currentTime = this._currentTime; - return currentTime ? 1e3 * currentTime : 1e3 * (mediaElement.currentTime || 0) + if (currentTime) { + return currentTime * 1000; + } + + return (mediaElement.currentTime || 0) * 1000; } - }, HtmlAudioPlayer.prototype.duration = function(val) { + }; + + HtmlAudioPlayer.prototype.duration = function (val) { + var mediaElement = this._mediaElement; if (mediaElement) { var duration = mediaElement.duration; - if (htmlMediaHelper.isValidDuration(duration)) return 1e3 * duration + if (htmlMediaHelper.isValidDuration(duration)) { + return duration * 1000; + } } - return null - }, HtmlAudioPlayer.prototype.seekable = function() { + + return null; + }; + + HtmlAudioPlayer.prototype.seekable = function () { var mediaElement = this._mediaElement; if (mediaElement) { + var seekable = mediaElement.seekable; if (seekable && seekable.length) { - var start = seekable.start(0), - end = seekable.end(0); - return htmlMediaHelper.isValidDuration(start) || (start = 0), htmlMediaHelper.isValidDuration(end) || (end = 0), end - start > 0 + + var start = seekable.start(0); + var end = seekable.end(0); + + if (!htmlMediaHelper.isValidDuration(start)) { + start = 0; + } + if (!htmlMediaHelper.isValidDuration(end)) { + end = 0; + } + + return (end - start) > 0; } - return !1 + + return false; } - }, HtmlAudioPlayer.prototype.getBufferedRanges = function() { + }; + + HtmlAudioPlayer.prototype.getBufferedRanges = function () { var mediaElement = this._mediaElement; - return mediaElement ? htmlMediaHelper.getBufferedRanges(this, mediaElement) : [] - }, HtmlAudioPlayer.prototype.pause = function() { + if (mediaElement) { + + return htmlMediaHelper.getBufferedRanges(this, mediaElement); + } + + return []; + }; + + HtmlAudioPlayer.prototype.pause = function () { var mediaElement = this._mediaElement; - mediaElement && mediaElement.pause() - }, HtmlAudioPlayer.prototype.resume = function() { + if (mediaElement) { + mediaElement.pause(); + } + }; + + // This is a retry after error + HtmlAudioPlayer.prototype.resume = function () { var mediaElement = this._mediaElement; - mediaElement && mediaElement.play() - }, HtmlAudioPlayer.prototype.unpause = function() { + if (mediaElement) { + mediaElement.play(); + } + }; + + HtmlAudioPlayer.prototype.unpause = function () { var mediaElement = this._mediaElement; - mediaElement && mediaElement.play() - }, HtmlAudioPlayer.prototype.paused = function() { + if (mediaElement) { + mediaElement.play(); + } + }; + + HtmlAudioPlayer.prototype.paused = function () { + var mediaElement = this._mediaElement; - return !!mediaElement && mediaElement.paused - }, HtmlAudioPlayer.prototype.setVolume = function(val) { + if (mediaElement) { + return mediaElement.paused; + } + + return false; + }; + + HtmlAudioPlayer.prototype.setVolume = function (val) { var mediaElement = this._mediaElement; - mediaElement && (mediaElement.volume = val / 100) - }, HtmlAudioPlayer.prototype.getVolume = function() { + if (mediaElement) { + mediaElement.volume = val / 100; + } + }; + + HtmlAudioPlayer.prototype.getVolume = function () { var mediaElement = this._mediaElement; - if (mediaElement) return Math.min(Math.round(100 * mediaElement.volume), 100) - }, HtmlAudioPlayer.prototype.volumeUp = function() { - this.setVolume(Math.min(this.getVolume() + 2, 100)) - }, HtmlAudioPlayer.prototype.volumeDown = function() { - this.setVolume(Math.max(this.getVolume() - 2, 0)) - }, HtmlAudioPlayer.prototype.setMute = function(mute) { + if (mediaElement) { + + return Math.min(Math.round(mediaElement.volume * 100), 100); + } + }; + + HtmlAudioPlayer.prototype.volumeUp = function () { + this.setVolume(Math.min(this.getVolume() + 2, 100)); + }; + + HtmlAudioPlayer.prototype.volumeDown = function () { + this.setVolume(Math.max(this.getVolume() - 2, 0)); + }; + + HtmlAudioPlayer.prototype.setMute = function (mute) { + var mediaElement = this._mediaElement; - mediaElement && (mediaElement.muted = mute) - }, HtmlAudioPlayer.prototype.isMuted = function() { + if (mediaElement) { + mediaElement.muted = mute; + } + }; + + HtmlAudioPlayer.prototype.isMuted = function () { var mediaElement = this._mediaElement; - return !!mediaElement && mediaElement.muted - }, HtmlAudioPlayer.prototype.destroy = function() {}, HtmlAudioPlayer + if (mediaElement) { + return mediaElement.muted; + } + return false; + }; + + HtmlAudioPlayer.prototype.destroy = function () { + + }; + + return HtmlAudioPlayer; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/htmlvideoplayer/htmlmediahelper.js b/src/bower_components/emby-webcomponents/htmlvideoplayer/htmlmediahelper.js index 68bc1ecded..bba0ec2daf 100644 --- a/src/bower_components/emby-webcomponents/htmlvideoplayer/htmlmediahelper.js +++ b/src/bower_components/emby-webcomponents/htmlvideoplayer/htmlmediahelper.js @@ -1,113 +1,254 @@ -define(["appSettings", "browser", "events"], function(appSettings, browser, events) { - "use strict"; +define(['appSettings', 'browser', 'events'], function (appSettings, browser, events) { + 'use strict'; function getSavedVolume() { - return appSettings.get("volume") || 1 + return appSettings.get("volume") || 1; } function saveVolume(value) { - value && appSettings.set("volume", value) + if (value) { + appSettings.set("volume", value); + } } function getCrossOriginValue(mediaSource) { - return mediaSource.IsRemote ? null : "anonymous" + + if (mediaSource.IsRemote) { + return null; + } + + return 'anonymous'; } function canPlayNativeHls() { - var media = document.createElement("video"); - return !(!media.canPlayType("application/x-mpegURL").replace(/no/, "") && !media.canPlayType("application/vnd.apple.mpegURL").replace(/no/, "")) + var media = document.createElement('video'); + + if (media.canPlayType('application/x-mpegURL').replace(/no/, '') || + media.canPlayType('application/vnd.apple.mpegURL').replace(/no/, '')) { + return true; + } + + return false; } function enableHlsShakaPlayer(item, mediaSource, mediaType) { - if (window.MediaSource && MediaSource.isTypeSupported) { + + if (!!window.MediaSource && !!MediaSource.isTypeSupported) { + if (canPlayNativeHls()) { - if (browser.edge && "Video" === mediaType) return !0; - mediaSource.RunTimeTicks + + if (browser.edge && mediaType === 'Video') { + return true; + } + + // simple playback should use the native support + if (mediaSource.RunTimeTicks) { + //if (!browser.edge) { + //return false; + //} + } + + //return false; } - return !0 + + return true; } - return !1 + + return false; } function enableHlsJsPlayer(runTimeTicks, mediaType) { - if (null == window.MediaSource) return !1; - if (browser.iOS) return !1; - if (browser.tizen || browser.web0s) return !1; - if (canPlayNativeHls()) { - if (browser.android && "Audio" === mediaType) return !0; - if (browser.edge, runTimeTicks) return !1 + + if (window.MediaSource == null) { + return false; } - return !0 + + // hls.js is only in beta. needs more testing. + if (browser.iOS) { + return false; + } + + // The native players on these devices support seeking live streams, no need to use hls.js here + if (browser.tizen || browser.web0s) { + return false; + } + + if (canPlayNativeHls()) { + + // Having trouble with chrome's native support and transcoded music + if (browser.android && mediaType === 'Audio') { + return true; + } + + if (browser.edge && mediaType === 'Video') { + //return true; + } + + // simple playback should use the native support + if (runTimeTicks) { + //if (!browser.edge) { + return false; + //} + } + + //return false; + } + + return true; } + var recoverDecodingErrorDate, recoverSwapAudioCodecDate; function handleHlsJsMediaError(instance, reject) { + var hlsPlayer = instance._hlsPlayer; - if (hlsPlayer) { - var now = Date.now(); - window.performance && window.performance.now && (now = performance.now()), !recoverDecodingErrorDate || now - recoverDecodingErrorDate > 3e3 ? (recoverDecodingErrorDate = now, console.log("try to recover media Error ..."), hlsPlayer.recoverMediaError()) : !recoverSwapAudioCodecDate || now - recoverSwapAudioCodecDate > 3e3 ? (recoverSwapAudioCodecDate = now, console.log("try to swap Audio Codec and recover media Error ..."), hlsPlayer.swapAudioCodec(), hlsPlayer.recoverMediaError()) : (console.error("cannot recover, last media error recovery failed ..."), reject ? reject() : onErrorInternal(instance, "mediadecodeerror")) + + if (!hlsPlayer) { + return; + } + + var now = Date.now(); + + if (window.performance && window.performance.now) { + now = performance.now(); + } + + if (!recoverDecodingErrorDate || (now - recoverDecodingErrorDate) > 3000) { + recoverDecodingErrorDate = now; + console.log('try to recover media Error ...'); + hlsPlayer.recoverMediaError(); + } else { + if (!recoverSwapAudioCodecDate || (now - recoverSwapAudioCodecDate) > 3000) { + recoverSwapAudioCodecDate = now; + console.log('try to swap Audio Codec and recover media Error ...'); + hlsPlayer.swapAudioCodec(); + hlsPlayer.recoverMediaError(); + } else { + console.error('cannot recover, last media error recovery failed ...'); + + if (reject) { + reject(); + } else { + onErrorInternal(instance, 'mediadecodeerror'); + } + } } } function onErrorInternal(instance, type) { - instance.destroyCustomTrack && instance.destroyCustomTrack(instance._mediaElement), events.trigger(instance, "error", [{ - type: type - }]) + + // Needed for video + if (instance.destroyCustomTrack) { + instance.destroyCustomTrack(instance._mediaElement); + } + + events.trigger(instance, 'error', [ + { + type: type + }]); } function isValidDuration(duration) { - return !(!duration || isNaN(duration) || duration === Number.POSITIVE_INFINITY || duration === Number.NEGATIVE_INFINITY) + if (duration && !isNaN(duration) && duration !== Number.POSITIVE_INFINITY && duration !== Number.NEGATIVE_INFINITY) { + return true; + } + + return false; } - function setCurrentTimeIfNeeded(element, seconds, allowance) { - Math.abs((element.currentTime || 0) - seconds) >= allowance && (element.currentTime = seconds) + function setCurrentTimeIfNeeded(element, seconds) { + + if (Math.abs(element.currentTime || 0, seconds) <= 1) { + element.currentTime = seconds; + } } function seekOnPlaybackStart(instance, element, ticks) { - var seconds = (ticks || 0) / 1e7; + + var seconds = (ticks || 0) / 10000000; + if (seconds) { - (instance.currentSrc() || "").toLowerCase(); - setCurrentTimeIfNeeded(element, seconds, 5), setTimeout(function() { - setCurrentTimeIfNeeded(element, seconds, 10) - }, 2500) + var src = (instance.currentSrc() || '').toLowerCase(); + + // Appending #t=xxx to the query string doesn't seem to work with HLS + // For plain video files, not all browsers support it either + var delay = browser.safari ? 2500 : 0; + if (delay) { + setTimeout(function () { + setCurrentTimeIfNeeded(element, seconds); + }, delay); + } else { + setCurrentTimeIfNeeded(element, seconds); + } } } function applySrc(elem, src, options) { - return window.Windows && options.mediaSource && options.mediaSource.IsLocal ? Windows.Storage.StorageFile.getFileFromPathAsync(options.url).then(function(file) { - var playlist = new Windows.Media.Playback.MediaPlaybackList, - source1 = Windows.Media.Core.MediaSource.createFromStorageFile(file), - startTime = (options.playerStartPositionTicks || 0) / 1e4; - return playlist.items.append(new Windows.Media.Playback.MediaPlaybackItem(source1, startTime)), elem.src = URL.createObjectURL(playlist, { - oneTimeOnly: !0 - }), Promise.resolve() - }) : (elem.src = src, Promise.resolve()) + + if (window.Windows && options.mediaSource && options.mediaSource.IsLocal) { + + return Windows.Storage.StorageFile.getFileFromPathAsync(options.url).then(function (file) { + + var playlist = new Windows.Media.Playback.MediaPlaybackList(); + + var source1 = Windows.Media.Core.MediaSource.createFromStorageFile(file); + var startTime = (options.playerStartPositionTicks || 0) / 10000; + playlist.items.append(new Windows.Media.Playback.MediaPlaybackItem(source1, startTime)); + elem.src = URL.createObjectURL(playlist, { oneTimeOnly: true }); + return Promise.resolve(); + }); + + } else { + + elem.src = src; + } + + return Promise.resolve(); } function onSuccessfulPlay(elem, onErrorFn) { - elem.addEventListener("error", onErrorFn) + + elem.addEventListener('error', onErrorFn); } function playWithPromise(elem, onErrorFn) { + try { var promise = elem.play(); - return promise && promise.then ? promise.catch(function(e) { - var errorName = (e.name || "").toLowerCase(); - return "notallowederror" === errorName || "aborterror" === errorName ? (onSuccessfulPlay(elem, onErrorFn), Promise.resolve()) : Promise.reject() - }) : (onSuccessfulPlay(elem, onErrorFn), Promise.resolve()) + if (promise && promise.then) { + // Chrome now returns a promise + return promise.catch(function (e) { + + var errorName = (e.name || '').toLowerCase(); + // safari uses aborterror + if (errorName === 'notallowederror' || + errorName === 'aborterror') { + // swallow this error because the user can still click the play button on the video element + onSuccessfulPlay(elem, onErrorFn); + return Promise.resolve(); + } + return Promise.reject(); + }); + } else { + onSuccessfulPlay(elem, onErrorFn); + return Promise.resolve(); + } } catch (err) { - return console.log("error calling video.play: " + err), Promise.reject() + console.log('error calling video.play: ' + err); + return Promise.reject(); } } function destroyCastPlayer(instance) { + var player = instance._castPlayer; if (player) { try { - player.unload() + player.unload(); } catch (err) { - console.log(err) + console.log(err); } - instance._castPlayer = null + + instance._castPlayer = null; } } @@ -115,11 +256,12 @@ define(["appSettings", "browser", "events"], function(appSettings, browser, even var player = instance._shakaPlayer; if (player) { try { - player.destroy() + player.destroy(); } catch (err) { - console.log(err) + console.log(err); } - instance._shakaPlayer = null + + instance._shakaPlayer = null; } } @@ -127,11 +269,12 @@ define(["appSettings", "browser", "events"], function(appSettings, browser, even var player = instance._hlsPlayer; if (player) { try { - player.destroy() + player.destroy(); } catch (err) { - console.log(err) + console.log(err); } - instance._hlsPlayer = null + + instance._hlsPlayer = null; } } @@ -139,63 +282,170 @@ define(["appSettings", "browser", "events"], function(appSettings, browser, even var player = instance._flvPlayer; if (player) { try { - player.unload(), player.detachMediaElement(), player.destroy() + player.unload(); + player.detachMediaElement(); + player.destroy(); } catch (err) { - console.log(err) + console.log(err); } - instance._flvPlayer = null + + instance._flvPlayer = null; } } function bindEventsToHlsPlayer(instance, hls, elem, onErrorFn, resolve, reject) { - hls.on(Hls.Events.MANIFEST_PARSED, function() { - playWithPromise(elem, onErrorFn).then(resolve, function() { - reject && (reject(), reject = null) - }) - }), hls.on(Hls.Events.ERROR, function(event, data) { - switch (console.log("HLS Error: Type: " + data.type + " Details: " + (data.details || "") + " Fatal: " + (data.fatal || !1)), data.type) { + + hls.on(Hls.Events.MANIFEST_PARSED, function () { + playWithPromise(elem, onErrorFn).then(resolve, function () { + + if (reject) { + reject(); + reject = null; + } + }); + }); + + hls.on(Hls.Events.ERROR, function (event, data) { + + console.log('HLS Error: Type: ' + data.type + ' Details: ' + (data.details || '') + ' Fatal: ' + (data.fatal || false)); + + switch (data.type) { case Hls.ErrorTypes.NETWORK_ERROR: - if (data.response && data.response.code && data.response.code >= 400) return console.log("hls.js response error code: " + data.response.code), hls.destroy(), void(reject ? (reject("servererror"), reject = null) : onErrorInternal(instance, "servererror")) - } - if (data.fatal) switch (data.type) { - case Hls.ErrorTypes.NETWORK_ERROR: - data.response && 0 === data.response.code ? (console.log("hls.js response error code: " + data.response.code), hls.destroy(), reject ? (reject("network"), reject = null) : onErrorInternal(instance, "network")) : (console.log("fatal network error encountered, try to recover"), hls.startLoad()); - break; - case Hls.ErrorTypes.MEDIA_ERROR: - console.log("fatal media error encountered, try to recover"); - var currentReject = reject; - reject = null, handleHlsJsMediaError(instance, currentReject); + // try to recover network error + if (data.response && data.response.code && data.response.code >= 400) { + + console.log('hls.js response error code: ' + data.response.code); + + // Trigger failure differently depending on whether this is prior to start of playback, or after + hls.destroy(); + + if (reject) { + reject('servererror'); + reject = null; + } else { + onErrorInternal(instance, 'servererror'); + } + + return; + + } + break; default: - console.log("Cannot recover from hls error - destroy and trigger error"), hls.destroy(), reject ? (reject(), reject = null) : onErrorInternal(instance, "mediadecodeerror") + break; } - }) + + if (data.fatal) { + switch (data.type) { + case Hls.ErrorTypes.NETWORK_ERROR: + + if (data.response && data.response.code === 0) { + + // This could be a CORS error related to access control response headers + + console.log('hls.js response error code: ' + data.response.code); + + // Trigger failure differently depending on whether this is prior to start of playback, or after + hls.destroy(); + + if (reject) { + reject('network'); + reject = null; + } else { + onErrorInternal(instance, 'network'); + } + } + + else { + console.log("fatal network error encountered, try to recover"); + hls.startLoad(); + } + + break; + case Hls.ErrorTypes.MEDIA_ERROR: + console.log("fatal media error encountered, try to recover"); + var currentReject = reject; + reject = null; + handleHlsJsMediaError(instance, currentReject); + break; + default: + + console.log('Cannot recover from hls error - destroy and trigger error'); + // cannot recover + // Trigger failure differently depending on whether this is prior to start of playback, or after + hls.destroy(); + + if (reject) { + reject(); + reject = null; + } else { + onErrorInternal(instance, 'mediadecodeerror'); + } + break; + } + } + }); } function onEndedInternal(instance, elem, onErrorFn) { - elem.removeEventListener("error", onErrorFn), elem.src = "", elem.innerHTML = "", elem.removeAttribute("src"), destroyHlsPlayer(instance), destroyFlvPlayer(instance), destroyShakaPlayer(instance), destroyCastPlayer(instance); + + elem.removeEventListener('error', onErrorFn); + + elem.src = ''; + elem.innerHTML = ''; + elem.removeAttribute("src"); + + destroyHlsPlayer(instance); + destroyFlvPlayer(instance); + destroyShakaPlayer(instance); + destroyCastPlayer(instance); + var stopInfo = { src: instance._currentSrc }; - events.trigger(instance, "stopped", [stopInfo]), instance._currentTime = null, instance._currentSrc = null, instance._currentPlayOptions = null + + events.trigger(instance, 'stopped', [stopInfo]); + + instance._currentTime = null; + instance._currentSrc = null; + instance._currentPlayOptions = null; } function getBufferedRanges(instance, elem) { - var offset, ranges = [], - seekable = elem.buffered || [], - currentPlayOptions = instance._currentPlayOptions; - currentPlayOptions && (offset = currentPlayOptions.transcodingOffsetTicks), offset = offset || 0; - for (var i = 0, length = seekable.length; i < length; i++) { - var start = seekable.start(i), - end = seekable.end(i); - isValidDuration(start) || (start = 0), isValidDuration(end) ? ranges.push({ - start: 1e7 * start + offset, - end: 1e7 * end + offset - }) : end = 0 + + var ranges = []; + var seekable = elem.buffered || []; + + var offset; + var currentPlayOptions = instance._currentPlayOptions; + if (currentPlayOptions) { + offset = currentPlayOptions.transcodingOffsetTicks; } - return ranges + + offset = offset || 0; + + for (var i = 0, length = seekable.length; i < length; i++) { + + var start = seekable.start(i); + var end = seekable.end(i); + + if (!isValidDuration(start)) { + start = 0; + } + if (!isValidDuration(end)) { + end = 0; + continue; + } + + ranges.push({ + start: (start * 10000000) + offset, + end: (end * 10000000) + offset + }); + } + + return ranges; } - var recoverDecodingErrorDate, recoverSwapAudioCodecDate; + return { getSavedVolume: getSavedVolume, saveVolume: saveVolume, @@ -214,5 +464,5 @@ define(["appSettings", "browser", "events"], function(appSettings, browser, even onEndedInternal: onEndedInternal, getCrossOriginValue: getCrossOriginValue, getBufferedRanges: getBufferedRanges - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/htmlvideoplayer/plugin.js b/src/bower_components/emby-webcomponents/htmlvideoplayer/plugin.js index 35313d6782..16381d4462 100644 --- a/src/bower_components/emby-webcomponents/htmlvideoplayer/plugin.js +++ b/src/bower_components/emby-webcomponents/htmlvideoplayer/plugin.js @@ -1,699 +1,1852 @@ -define(["browser", "require", "events", "apphost", "loading", "dom", "playbackManager", "appRouter", "appSettings", "connectionManager", "htmlMediaHelper", "itemHelper"], function(browser, require, events, appHost, loading, dom, playbackManager, appRouter, appSettings, connectionManager, htmlMediaHelper, itemHelper) { +define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackManager', 'appRouter', 'appSettings', 'connectionManager', 'htmlMediaHelper', 'itemHelper'], function (browser, require, events, appHost, loading, dom, playbackManager, appRouter, appSettings, connectionManager, htmlMediaHelper, itemHelper) { "use strict"; + var mediaManager; + function tryRemoveElement(elem) { var parentNode = elem.parentNode; - if (parentNode) try { - parentNode.removeChild(elem) - } catch (err) { - console.log("Error removing dialog element: " + err) + if (parentNode) { + + // Seeing crashes in edge webview + try { + parentNode.removeChild(elem); + } catch (err) { + console.log('Error removing dialog element: ' + err); + } } } + var _supportsTextTracks; + function supportsTextTracks() { + + if (_supportsTextTracks == null) { + _supportsTextTracks = document.createElement('video').textTracks != null; + } + + // For now, until ready + return _supportsTextTracks; + } + function enableNativeTrackSupport(currentSrc, track) { - if (track && "Embed" === track.DeliveryMethod) return !0; - if (browser.firefox && -1 !== (currentSrc || "").toLowerCase().indexOf(".m3u8")) return !1; - if (browser.chromecast && -1 !== (currentSrc || "").toLowerCase().indexOf(".m3u8")) return !1; - if (browser.ps4) return !1; - if (browser.web0s) return !1; - if (browser.edge) return !1; - if (browser.iOS && (browser.iosVersion || 10) < 10) return !1; + if (track) { - var format = (track.Codec || "").toLowerCase(); - if ("ssa" === format || "ass" === format) return !1 + if (track.DeliveryMethod === 'Embed') { + return true; + } } - return !0 + + if (browser.firefox) { + if ((currentSrc || '').toLowerCase().indexOf('.m3u8') !== -1) { + return false; + } + } + + // subs getting blocked due to CORS + if (browser.chromecast) { + if ((currentSrc || '').toLowerCase().indexOf('.m3u8') !== -1) { + return false; + } + } + + if (browser.ps4) { + return false; + } + + if (browser.web0s) { + return false; + } + + // Edge is randomly not rendering subtitles + if (browser.edge) { + return false; + } + + if (browser.iOS) { + // works in the browser but not the native app + if ((browser.iosVersion || 10) < 10) { + return false; + } + } + + if (track) { + var format = (track.Codec || '').toLowerCase(); + if (format === 'ssa' || format === 'ass') { + // libjass is needed here + return false; + } + } + + return true; } function requireHlsPlayer(callback) { - require(["hlsjs"], function(hls) { - window.Hls = hls, callback() - }) + require(['hlsjs'], function (hls) { + window.Hls = hls; + callback(); + }); } function getMediaStreamAudioTracks(mediaSource) { - return mediaSource.MediaStreams.filter(function(s) { - return "Audio" === s.Type - }) + + return mediaSource.MediaStreams.filter(function (s) { + return s.Type === 'Audio'; + }); } function getMediaStreamTextTracks(mediaSource) { - return mediaSource.MediaStreams.filter(function(s) { - return "Subtitle" === s.Type - }) + + return mediaSource.MediaStreams.filter(function (s) { + return s.Type === 'Subtitle'; + }); } function zoomIn(elem) { - return new Promise(function(resolve, reject) { - elem.style.animation = "htmlvideoplayer-zoomin 240ms ease-in normal", dom.addEventListener(elem, dom.whichAnimationEvent(), resolve, { - once: !0 - }) - }) + + return new Promise(function (resolve, reject) { + + var duration = 240; + elem.style.animation = 'htmlvideoplayer-zoomin ' + duration + 'ms ease-in normal'; + dom.addEventListener(elem, dom.whichAnimationEvent(), resolve, { + once: true + }); + }); } function normalizeTrackEventText(text) { - return text.replace(/\\N/gi, "\n") + return text.replace(/\\N/gi, '\n'); } function setTracks(elem, tracks, item, mediaSource) { - elem.innerHTML = getTracksHtml(tracks, item, mediaSource) + + elem.innerHTML = getTracksHtml(tracks, item, mediaSource); } function getTextTrackUrl(track, item, format) { - if (itemHelper.isLocalItem(item) && track.Path) return track.Path; + + if (itemHelper.isLocalItem(item) && track.Path) { + return track.Path; + } + var url = playbackManager.getSubtitleUrl(track, item.ServerId); - return format && (url = url.replace(".vtt", format)), url + if (format) { + url = url.replace('.vtt', format); + } + + return url; } function getTracksHtml(tracks, item, mediaSource) { - return tracks.map(function(t) { - if ("External" !== t.DeliveryMethod) return ""; - var defaultAttribute = mediaSource.DefaultSubtitleStreamIndex === t.Index ? " default" : "", - language = t.Language || "und", - label = t.Language || "und"; - return '" - }).join("") + return tracks.map(function (t) { + + if (t.DeliveryMethod !== 'External') { + return ''; + } + + var defaultAttribute = mediaSource.DefaultSubtitleStreamIndex === t.Index ? ' default' : ''; + + var language = t.Language || 'und'; + var label = t.Language || 'und'; + return ''; + + }).join(''); } function getDefaultProfile() { - return new Promise(function(resolve, reject) { - require(["browserdeviceprofile"], function(profileBuilder) { - resolve(profileBuilder({})) - }) - }) + + return new Promise(function (resolve, reject) { + + require(['browserdeviceprofile'], function (profileBuilder) { + + resolve(profileBuilder({})); + }); + }); } function HtmlVideoPlayer() { - function updateVideoUrl(streamInfo) { - var isHls = -1 !== streamInfo.url.toLowerCase().indexOf(".m3u8"), - mediaSource = streamInfo.mediaSource, - item = streamInfo.item; - if (mediaSource && item && !mediaSource.RunTimeTicks && isHls && "Transcode" === streamInfo.playMethod && (browser.iOS || browser.osx)) { - var hlsPlaylistUrl = streamInfo.url.replace("master.m3u8", "live.m3u8"); - return loading.show(), console.log("prefetching hls playlist: " + hlsPlaylistUrl), connectionManager.getApiClient(item.ServerId).ajax({ - type: "GET", - url: hlsPlaylistUrl - }).then(function() { - return console.log("completed prefetching hls playlist: " + hlsPlaylistUrl), loading.hide(), streamInfo.url = hlsPlaylistUrl, Promise.resolve() - }, function() { - return console.log("error prefetching hls playlist: " + hlsPlaylistUrl), loading.hide(), Promise.resolve() - }) - } - return Promise.resolve() + + if (browser.edgeUwp) { + this.name = 'Windows Video Player'; + } else { + this.name = 'Html Video Player'; } + this.type = 'mediaplayer'; + this.id = 'htmlvideoplayer'; + + // Let any players created by plugins take priority + this.priority = 1; + + var videoDialog; + + var winJsPlaybackItem; + + var subtitleTrackIndexToSetOnPlaying; + var audioTrackIndexToSetOnPlaying; + + var lastCustomTrackMs = 0; + var currentClock; + var currentAssRenderer; + var customTrackIndex = -1; + + var videoSubtitlesElem; + var currentTrackEvents; + + var self = this; + + self.currentSrc = function () { + return self._currentSrc; + }; + + function updateVideoUrl(streamInfo) { + + var isHls = streamInfo.url.toLowerCase().indexOf('.m3u8') !== -1; + + var mediaSource = streamInfo.mediaSource; + var item = streamInfo.item; + + // Huge hack alert. Safari doesn't seem to like if the segments aren't available right away when playback starts + // This will start the transcoding process before actually feeding the video url into the player + // Edit: Also seeing stalls from hls.js + if (mediaSource && item && !mediaSource.RunTimeTicks && isHls && streamInfo.playMethod === 'Transcode' && (browser.iOS || browser.osx)) { + + var hlsPlaylistUrl = streamInfo.url.replace('master.m3u8', 'live.m3u8'); + + loading.show(); + + console.log('prefetching hls playlist: ' + hlsPlaylistUrl); + + return connectionManager.getApiClient(item.ServerId).ajax({ + + type: 'GET', + url: hlsPlaylistUrl + + }).then(function () { + + console.log('completed prefetching hls playlist: ' + hlsPlaylistUrl); + + loading.hide(); + streamInfo.url = hlsPlaylistUrl; + + return Promise.resolve(); + + }, function () { + + console.log('error prefetching hls playlist: ' + hlsPlaylistUrl); + + loading.hide(); + return Promise.resolve(); + }); + + } else { + return Promise.resolve(); + } + } + + self.play = function (options) { + + if (browser.msie) { + if (options.playMethod === 'Transcode' && !window.MediaSource) { + alert('Playback of this content is not supported in Internet Explorer. For a better experience, try a modern browser such as Microsoft Edge, Google Chrome, Firefox or Opera.'); + return Promise.reject(); + } + } + + self._started = false; + self._timeUpdated = false; + + self._currentTime = null; + + return createMediaElement(options).then(function (elem) { + + return updateVideoUrl(options, options.mediaSource).then(function () { + return setCurrentSrc(elem, options); + }); + }); + }; + function setSrcWithFlvJs(instance, elem, options, url) { - return new Promise(function(resolve, reject) { - require(["flvjs"], function(flvjs) { + + return new Promise(function (resolve, reject) { + + require(['flvjs'], function (flvjs) { + var flvPlayer = flvjs.createPlayer({ - type: "flv", + type: 'flv', url: url - }, { - seekType: "range", - lazyLoad: !1 - }); - flvPlayer.attachMediaElement(elem), flvPlayer.load(), flvPlayer.play().then(resolve, reject), instance._flvPlayer = flvPlayer, self._currentSrc = url - }) - }) + }, + { + seekType: 'range', + lazyLoad: false + }); + + flvPlayer.attachMediaElement(elem); + flvPlayer.load(); + + flvPlayer.play().then(resolve, reject); + instance._flvPlayer = flvPlayer; + + // This is needed in setCurrentTrackElement + self._currentSrc = url; + }); + }); } function setSrcWithHlsJs(instance, elem, options, url) { - return new Promise(function(resolve, reject) { - requireHlsPlayer(function() { + + return new Promise(function (resolve, reject) { + + requireHlsPlayer(function () { + var hls = new Hls({ - manifestLoadingTimeOut: 2e4 + manifestLoadingTimeOut: 20000 + //appendErrorMaxRetry: 6, + //debug: true }); - hls.loadSource(url), hls.attachMedia(elem), htmlMediaHelper.bindEventsToHlsPlayer(self, hls, elem, onError, resolve, reject), self._hlsPlayer = hls, self._currentSrc = url - }) - }) + hls.loadSource(url); + hls.attachMedia(elem); + + htmlMediaHelper.bindEventsToHlsPlayer(self, hls, elem, onError, resolve, reject); + + self._hlsPlayer = hls; + + // This is needed in setCurrentTrackElement + self._currentSrc = url; + }); + }); + } + + function onShakaError(event) { + + var error = event.detail; + console.error('Error code', error.code, 'object', error); + } + + function setSrcWithShakaPlayer(instance, elem, options, url) { + + return new Promise(function (resolve, reject) { + + require(['shaka'], function () { + + var player = new shaka.Player(elem); + + //player.configure({ + // abr: { + // enabled: false + // }, + // streaming: { + + // failureCallback: function () { + // alert(2); + // } + // } + //}); + + //shaka.log.setLevel(6); + + // Listen for error events. + player.addEventListener('error', onShakaError); + + // Try to load a manifest. + // This is an asynchronous process. + player.load(url).then(function () { + + // This runs if the asynchronous load is successful. + resolve(); + + }, reject); + + self._shakaPlayer = player; + + // This is needed in setCurrentTrackElement + self._currentSrc = url; + }); + }); } function setCurrentSrcChromecast(instance, elem, options, url) { - elem.autoplay = !0; - var lrd = new cast.receiver.MediaManager.LoadRequestData; - lrd.currentTime = (options.playerStartPositionTicks || 0) / 1e7, lrd.autoplay = !0, lrd.media = new cast.receiver.media.MediaInformation, lrd.media.contentId = url, lrd.media.contentType = options.mimeType, lrd.media.streamType = cast.receiver.media.StreamType.OTHER, lrd.media.customData = options, console.log("loading media url into mediaManager"); + + elem.autoplay = true; + + var lrd = new cast.receiver.MediaManager.LoadRequestData(); + lrd.currentTime = (options.playerStartPositionTicks || 0) / 10000000; + lrd.autoplay = true; + lrd.media = new cast.receiver.media.MediaInformation(); + + lrd.media.contentId = url; + lrd.media.contentType = options.mimeType; + lrd.media.streamType = cast.receiver.media.StreamType.OTHER; + lrd.media.customData = options; + + console.log('loading media url into mediaManager'); + try { - return mediaManager.load(lrd), self._currentSrc = url, Promise.resolve() + mediaManager.load(lrd); + // This is needed in setCurrentTrackElement + self._currentSrc = url; + + return Promise.resolve(); } catch (err) { - return console.log("mediaManager error: " + err), Promise.reject() + + console.log('mediaManager error: ' + err); + return Promise.reject(); } } + // Adapted from : https://github.com/googlecast/CastReferencePlayer/blob/master/player.js function onMediaManagerLoadMedia(event) { - self._castPlayer && self._castPlayer.unload(), self._castPlayer = null; - var protocol, data = event.data, - media = event.data.media || {}, - url = media.contentId, - contentType = media.contentType.toLowerCase(), - mediaElement = (media.customData, self._mediaElement), - host = new cast.player.api.Host({ - url: url, - mediaElement: mediaElement - }); - protocol = cast.player.api.CreateHlsStreamingProtocol(host), console.log("loading playback url: " + url), console.log("contentType: " + contentType), host.onError = function(errorCode) { - console.log("Fatal Error - " + errorCode) - }, mediaElement.autoplay = !1, self._castPlayer = new cast.player.api.Player(host), self._castPlayer.load(protocol, data.currentTime || 0), self._castPlayer.playWhenHaveEnoughData() + + if (self._castPlayer) { + self._castPlayer.unload(); // Must unload before starting again. + } + self._castPlayer = null; + + var data = event.data; + + var media = event.data.media || {}; + var url = media.contentId; + var contentType = media.contentType.toLowerCase(); + var options = media.customData; + + var protocol; + var ext = 'm3u8'; + + var mediaElement = self._mediaElement; + + var host = new cast.player.api.Host({ + 'url': url, + 'mediaElement': mediaElement + }); + + if (ext === 'm3u8' || + contentType === 'application/x-mpegurl' || + contentType === 'application/vnd.apple.mpegurl') { + protocol = cast.player.api.CreateHlsStreamingProtocol(host); + } else if (ext === 'mpd' || + contentType === 'application/dash+xml') { + protocol = cast.player.api.CreateDashStreamingProtocol(host); + } else if (url.indexOf('.ism') > -1 || + contentType === 'application/vnd.ms-sstr+xml') { + protocol = cast.player.api.CreateSmoothStreamingProtocol(host); + } + + console.log('loading playback url: ' + url); + console.log('contentType: ' + contentType); + + host.onError = function (errorCode) { + console.log("Fatal Error - " + errorCode); + }; + + mediaElement.autoplay = false; + + self._castPlayer = new cast.player.api.Player(host); + + self._castPlayer.load(protocol, data.currentTime || 0); + + self._castPlayer.playWhenHaveEnoughData(); } function initMediaManager() { - mediaManager.defaultOnLoad = mediaManager.onLoad.bind(mediaManager), mediaManager.onLoad = onMediaManagerLoadMedia.bind(self), mediaManager.defaultOnStop = mediaManager.onStop.bind(mediaManager), mediaManager.onStop = function(event) { - playbackManager.stop(), mediaManager.defaultOnStop(event) - } + + mediaManager.defaultOnLoad = mediaManager.onLoad.bind(mediaManager); + mediaManager.onLoad = onMediaManagerLoadMedia.bind(self); + + //mediaManager.defaultOnPlay = mediaManager.onPlay.bind(mediaManager); + //mediaManager.onPlay = function (event) { + // // TODO ??? + // mediaManager.defaultOnPlay(event); + //}; + + mediaManager.defaultOnStop = mediaManager.onStop.bind(mediaManager); + mediaManager.onStop = function (event) { + playbackManager.stop(); + mediaManager.defaultOnStop(event); + }; } function setCurrentSrc(elem, options) { - elem.removeEventListener("error", onError); + + elem.removeEventListener('error', onError); + var val = options.url; - console.log("playing url: " + val); - var seconds = (options.playerStartPositionTicks || 0) / 1e7; - seconds && (val += "#t=" + seconds), htmlMediaHelper.destroyHlsPlayer(self), htmlMediaHelper.destroyFlvPlayer(self), htmlMediaHelper.destroyCastPlayer(self); - var tracks = getMediaStreamTextTracks(options.mediaSource); - if (null != (subtitleTrackIndexToSetOnPlaying = null == options.mediaSource.DefaultSubtitleStreamIndex ? -1 : options.mediaSource.DefaultSubtitleStreamIndex) && subtitleTrackIndexToSetOnPlaying >= 0) { - var initialSubtitleStream = options.mediaSource.MediaStreams[subtitleTrackIndexToSetOnPlaying]; - initialSubtitleStream && "Encode" !== initialSubtitleStream.DeliveryMethod || (subtitleTrackIndexToSetOnPlaying = -1) + console.log('playing url: ' + val); + + // Convert to seconds + var seconds = (options.playerStartPositionTicks || 0) / 10000000; + if (seconds) { + val += '#t=' + seconds; } - audioTrackIndexToSetOnPlaying = "Transcode" === options.playMethod ? null : options.mediaSource.DefaultAudioStreamIndex, self._currentPlayOptions = options; + + htmlMediaHelper.destroyHlsPlayer(self); + htmlMediaHelper.destroyFlvPlayer(self); + htmlMediaHelper.destroyCastPlayer(self); + + var tracks = getMediaStreamTextTracks(options.mediaSource); + + subtitleTrackIndexToSetOnPlaying = options.mediaSource.DefaultSubtitleStreamIndex == null ? -1 : options.mediaSource.DefaultSubtitleStreamIndex; + if (subtitleTrackIndexToSetOnPlaying != null && subtitleTrackIndexToSetOnPlaying >= 0) { + var initialSubtitleStream = options.mediaSource.MediaStreams[subtitleTrackIndexToSetOnPlaying]; + if (!initialSubtitleStream || initialSubtitleStream.DeliveryMethod === 'Encode') { + subtitleTrackIndexToSetOnPlaying = -1; + } + } + + audioTrackIndexToSetOnPlaying = options.playMethod === 'Transcode' ? null : options.mediaSource.DefaultAudioStreamIndex; + + self._currentPlayOptions = options; + var crossOrigin = htmlMediaHelper.getCrossOriginValue(options.mediaSource); - return crossOrigin && (elem.crossOrigin = crossOrigin), browser.chromecast && -1 !== val.indexOf(".m3u8") && options.mediaSource.RunTimeTicks ? (setTracks(elem, tracks, options.item, options.mediaSource), setCurrentSrcChromecast(self, elem, options, val)) : htmlMediaHelper.enableHlsJsPlayer(options.mediaSource.RunTimeTicks, "Video") && -1 !== val.indexOf(".m3u8") ? (setTracks(elem, tracks, options.item, options.mediaSource), setSrcWithHlsJs(self, elem, options, val)) : "Transcode" !== options.playMethod && "flv" === options.mediaSource.Container ? (setTracks(elem, tracks, options.item, options.mediaSource), setSrcWithFlvJs(self, elem, options, val)) : (elem.autoplay = !0, htmlMediaHelper.applySrc(elem, val, options).then(function() { - return setTracks(elem, tracks, options.item, options.mediaSource), self._currentSrc = val, htmlMediaHelper.playWithPromise(elem, onError) - })) + if (crossOrigin) { + elem.crossOrigin = crossOrigin; + } + + /*if (htmlMediaHelper.enableHlsShakaPlayer(options.item, options.mediaSource, 'Video') && val.indexOf('.m3u8') !== -1) { + + setTracks(elem, tracks, options.item, options.mediaSource); + + return setSrcWithShakaPlayer(self, elem, options, val); + + } else*/ if (browser.chromecast && val.indexOf('.m3u8') !== -1 && options.mediaSource.RunTimeTicks) { + + setTracks(elem, tracks, options.item, options.mediaSource); + return setCurrentSrcChromecast(self, elem, options, val); + } + + else if (htmlMediaHelper.enableHlsJsPlayer(options.mediaSource.RunTimeTicks, 'Video') && val.indexOf('.m3u8') !== -1) { + + setTracks(elem, tracks, options.item, options.mediaSource); + + return setSrcWithHlsJs(self, elem, options, val); + + } else if (options.playMethod !== 'Transcode' && options.mediaSource.Container === 'flv') { + + setTracks(elem, tracks, options.item, options.mediaSource); + + return setSrcWithFlvJs(self, elem, options, val); + + } else { + + elem.autoplay = true; + + return htmlMediaHelper.applySrc(elem, val, options).then(function () { + + setTracks(elem, tracks, options.item, options.mediaSource); + + self._currentSrc = val; + + return htmlMediaHelper.playWithPromise(elem, onError); + }); + } } + self.setSubtitleStreamIndex = function (index) { + + setCurrentTrackElement(index); + }; + function isAudioStreamSupported(stream, deviceProfile) { - var codec = (stream.Codec || "").toLowerCase(); - return !codec || (!deviceProfile || (deviceProfile.DirectPlayProfiles || []).filter(function(p) { - return "Video" === p.Type && (!p.AudioCodec || -1 !== p.AudioCodec.toLowerCase().indexOf(codec)) - }).length > 0) + + var codec = (stream.Codec || '').toLowerCase(); + + if (!codec) { + return true; + } + + if (!deviceProfile) { + // This should never happen + return true; + } + + var profiles = deviceProfile.DirectPlayProfiles || []; + + return profiles.filter(function (p) { + + + if (p.Type === 'Video') { + + if (!p.AudioCodec) { + return true; + } + + return p.AudioCodec.toLowerCase().indexOf(codec) !== -1; + } + + return false; + + }).length > 0; } function getSupportedAudioStreams() { var profile = self._lastProfile; - return getMediaStreamAudioTracks(self._currentPlayOptions.mediaSource).filter(function(stream) { - return isAudioStreamSupported(stream, profile) - }) + + return getMediaStreamAudioTracks(self._currentPlayOptions.mediaSource).filter(function (stream) { + return isAudioStreamSupported(stream, profile); + }); } + self.setAudioStreamIndex = function (index) { + + var streams = getSupportedAudioStreams(); + + if (streams.length < 2) { + // If there's only one supported stream then trust that the player will handle it on it's own + return; + } + + var audioIndex = -1; + var i, length, stream; + + for (i = 0, length = streams.length; i < length; i++) { + stream = streams[i]; + + audioIndex++; + + if (stream.Index === index) { + break; + } + } + + if (audioIndex === -1) { + return; + } + + var elem = self._mediaElement; + if (!elem) { + return; + } + + // https://msdn.microsoft.com/en-us/library/hh772507(v=vs.85).aspx + + var elemAudioTracks = elem.audioTracks || []; + console.log('found ' + elemAudioTracks.length + ' audio tracks'); + + for (i = 0, length = elemAudioTracks.length; i < length; i++) { + + if (audioIndex === i) { + console.log('setting audio track ' + i + ' to enabled'); + elemAudioTracks[i].enabled = true; + } else { + console.log('setting audio track ' + i + ' to disabled'); + elemAudioTracks[i].enabled = false; + } + } + + setTimeout(function () { + elem.currentTime = elem.currentTime; + }, 100); + }; + + self.stop = function (destroyPlayer) { + + var elem = self._mediaElement; + var src = self._currentSrc; + + if (elem) { + + if (src) { + elem.pause(); + } + + htmlMediaHelper.onEndedInternal(self, elem, onError); + + if (destroyPlayer) { + self.destroy(); + } + } + + destroyCustomTrack(elem); + + return Promise.resolve(); + }; + + self.destroy = function () { + + htmlMediaHelper.destroyHlsPlayer(self); + htmlMediaHelper.destroyFlvPlayer(self); + + appRouter.setTransparency('none'); + + var videoElement = self._mediaElement; + + if (videoElement) { + + self._mediaElement = null; + + destroyCustomTrack(videoElement); + + videoElement.removeEventListener('timeupdate', onTimeUpdate); + videoElement.removeEventListener('ended', onEnded); + videoElement.removeEventListener('volumechange', onVolumeChange); + videoElement.removeEventListener('pause', onPause); + videoElement.removeEventListener('playing', onPlaying); + videoElement.removeEventListener('play', onPlay); + videoElement.removeEventListener('click', onClick); + videoElement.removeEventListener('dblclick', onDblClick); + + videoElement.parentNode.removeChild(videoElement); + } + + var dlg = videoDialog; + if (dlg) { + + videoDialog = null; + + dlg.parentNode.removeChild(dlg); + } + }; + function onEnded() { - destroyCustomTrack(this), htmlMediaHelper.onEndedInternal(self, this, onError) + + destroyCustomTrack(this); + htmlMediaHelper.onEndedInternal(self, this, onError); } function onTimeUpdate(e) { + + // Get the player position + the transcoding offset var time = this.currentTime; - time && !self._timeUpdated && (self._timeUpdated = !0, ensureValidVideo(this)), self._currentTime = time; - var currentPlayOptions = self._currentPlayOptions; - if (currentPlayOptions) { - var timeMs = 1e3 * time; - timeMs += (currentPlayOptions.transcodingOffsetTicks || 0) / 1e4, updateSubtitleText(timeMs) + + if (time && !self._timeUpdated) { + self._timeUpdated = true; + ensureValidVideo(this); } - events.trigger(self, "timeupdate") + + self._currentTime = time; + + var currentPlayOptions = self._currentPlayOptions; + // Not sure yet how this is coming up null since we never null it out, but it is causing app crashes + if (currentPlayOptions) { + var timeMs = time * 1000; + timeMs += ((currentPlayOptions.transcodingOffsetTicks || 0) / 10000); + updateSubtitleText(timeMs); + } + + events.trigger(self, 'timeupdate'); } function onVolumeChange() { - htmlMediaHelper.saveVolume(this.volume), events.trigger(self, "volumechange") + + htmlMediaHelper.saveVolume(this.volume); + events.trigger(self, 'volumechange'); } function onNavigatedToOsd() { - var dlg = videoDialog; - dlg && (dlg.classList.remove("videoPlayerContainer-withBackdrop"), dlg.classList.remove("videoPlayerContainer-onTop"), onStartedAndNavigatedToOsd()) - } + var dlg = videoDialog; + if (dlg) { + dlg.classList.remove('videoPlayerContainer-withBackdrop'); + dlg.classList.remove('videoPlayerContainer-onTop'); + + onStartedAndNavigatedToOsd(); + } + } function onStartedAndNavigatedToOsd() { - setCurrentTrackElement(subtitleTrackIndexToSetOnPlaying), null != audioTrackIndexToSetOnPlaying && self.canSetAudioStreamIndex() && self.setAudioStreamIndex(audioTrackIndexToSetOnPlaying) + + // If this causes a failure during navigation we end up in an awkward UI state + setCurrentTrackElement(subtitleTrackIndexToSetOnPlaying); + + if (audioTrackIndexToSetOnPlaying != null && self.canSetAudioStreamIndex()) { + self.setAudioStreamIndex(audioTrackIndexToSetOnPlaying); + } } function onPlaying(e) { - self._started || (self._started = !0, this.removeAttribute("controls"), loading.hide(), htmlMediaHelper.seekOnPlaybackStart(self, e.target, self._currentPlayOptions.playerStartPositionTicks), self._currentPlayOptions.fullscreen ? appRouter.showVideoOsd().then(onNavigatedToOsd) : (appRouter.setTransparency("backdrop"), videoDialog.classList.remove("videoPlayerContainer-withBackdrop"), videoDialog.classList.remove("videoPlayerContainer-onTop"), onStartedAndNavigatedToOsd())), events.trigger(self, "playing") + + if (!self._started) { + self._started = true; + this.removeAttribute('controls'); + + loading.hide(); + + htmlMediaHelper.seekOnPlaybackStart(self, e.target, self._currentPlayOptions.playerStartPositionTicks); + + if (self._currentPlayOptions.fullscreen) { + + appRouter.showVideoOsd().then(onNavigatedToOsd); + + } else { + appRouter.setTransparency('backdrop'); + videoDialog.classList.remove('videoPlayerContainer-withBackdrop'); + videoDialog.classList.remove('videoPlayerContainer-onTop'); + + onStartedAndNavigatedToOsd(); + } + } + events.trigger(self, 'playing'); } function onPlay(e) { - events.trigger(self, "unpause") + + events.trigger(self, 'unpause'); } function ensureValidVideo(elem) { - if (elem === self._mediaElement && 0 === elem.videoWidth && 0 === elem.videoHeight) { - var mediaSource = (self._currentPlayOptions || {}).mediaSource; - if (!mediaSource || mediaSource.RunTimeTicks) return void htmlMediaHelper.onErrorInternal(self, "mediadecodeerror") + if (elem !== self._mediaElement) { + return; } + + if (elem.videoWidth === 0 && elem.videoHeight === 0) { + + var mediaSource = (self._currentPlayOptions || {}).mediaSource; + + // Only trigger this if there is media info + // Avoid triggering in situations where it might not actually have a video stream (audio only live tv channel) + if (!mediaSource || mediaSource.RunTimeTicks) { + htmlMediaHelper.onErrorInternal(self, 'mediadecodeerror'); + return; + } + } + + //if (elem.audioTracks && !elem.audioTracks.length) { + // htmlMediaHelper.onErrorInternal(self, 'mediadecodeerror'); + //} } function onClick() { - events.trigger(self, "click") + events.trigger(self, 'click'); } function onDblClick() { - events.trigger(self, "dblclick") + events.trigger(self, 'dblclick'); } function onPause() { - events.trigger(self, "pause") + events.trigger(self, 'pause'); } function onError() { - var errorCode = this.error ? this.error.code || 0 : 0, - errorMessage = this.error ? this.error.message || "" : ""; - console.log("Media element error: " + errorCode.toString() + " " + errorMessage); + + var errorCode = this.error ? (this.error.code || 0) : 0; + var errorMessage = this.error ? (this.error.message || '') : ''; + console.log('Media element error: ' + errorCode.toString() + ' ' + errorMessage); + var type; + switch (errorCode) { case 1: + // MEDIA_ERR_ABORTED + // This will trigger when changing media while something is playing return; case 2: - type = "network"; + // MEDIA_ERR_NETWORK + type = 'network'; break; case 3: - if (self._hlsPlayer) return void htmlMediaHelper.handleHlsJsMediaError(self); - type = "mediadecodeerror"; + // MEDIA_ERR_DECODE + if (self._hlsPlayer) { + htmlMediaHelper.handleHlsJsMediaError(self); + return; + } else { + type = 'mediadecodeerror'; + } break; case 4: - type = "medianotsupported"; + // MEDIA_ERR_SRC_NOT_SUPPORTED + type = 'medianotsupported'; break; default: - return + // seeing cases where Edge is firing error events with no error code + // example is start playing something, then immediately change src to something else + return; } - htmlMediaHelper.onErrorInternal(self, type) + + htmlMediaHelper.onErrorInternal(self, type); } function destroyCustomTrack(videoElement) { - if (self._resizeObserver && (self._resizeObserver.disconnect(), self._resizeObserver = null), videoSubtitlesElem) { - var subtitlesContainer = videoSubtitlesElem.parentNode; - subtitlesContainer && tryRemoveElement(subtitlesContainer), videoSubtitlesElem = null + + if (self._resizeObserver) { + self._resizeObserver.disconnect(); + self._resizeObserver = null; } - if (currentTrackEvents = null, videoElement) - for (var allTracks = videoElement.textTracks || [], i = 0; i < allTracks.length; i++) { - var currentTrack = allTracks[i]; - 1 !== currentTrack.label.indexOf("manualTrack") && (currentTrack.mode = "disabled") + + if (videoSubtitlesElem) { + var subtitlesContainer = videoSubtitlesElem.parentNode; + if (subtitlesContainer) { + tryRemoveElement(subtitlesContainer); } - customTrackIndex = -1, currentClock = null, self._currentAspectRatio = null; + videoSubtitlesElem = null; + } + + currentTrackEvents = null; + + if (videoElement) { + var allTracks = videoElement.textTracks || []; // get list of tracks + for (var i = 0; i < allTracks.length; i++) { + + var currentTrack = allTracks[i]; + + if (currentTrack.label.indexOf('manualTrack') !== -1) { + currentTrack.mode = 'disabled'; + } + } + } + + customTrackIndex = -1; + currentClock = null; + self._currentAspectRatio = null; + var renderer = currentAssRenderer; - renderer && renderer.setEnabled(!1), currentAssRenderer = null + if (renderer) { + renderer.setEnabled(false); + } + currentAssRenderer = null; } + self.destroyCustomTrack = destroyCustomTrack; + function fetchSubtitlesUwp(track, item) { - return Windows.Storage.StorageFile.getFileFromPathAsync(track.Path).then(function(storageFile) { - return Windows.Storage.FileIO.readTextAsync(storageFile).then(function(text) { - return JSON.parse(text) - }) - }) + + return Windows.Storage.StorageFile.getFileFromPathAsync(track.Path).then(function (storageFile) { + + return Windows.Storage.FileIO.readTextAsync(storageFile).then(function (text) { + return JSON.parse(text); + }); + }); + } function fetchSubtitles(track, item) { - return window.Windows && itemHelper.isLocalItem(item) ? fetchSubtitlesUwp(track, item) : new Promise(function(resolve, reject) { - var xhr = new XMLHttpRequest, - url = getTextTrackUrl(track, item, ".js"); - xhr.open("GET", url, !0), xhr.onload = function(e) { - resolve(JSON.parse(this.response)) - }, xhr.onerror = reject, xhr.send() - }) + + if (window.Windows && itemHelper.isLocalItem(item)) { + return fetchSubtitlesUwp(track, item); + } + + return new Promise(function (resolve, reject) { + + var xhr = new XMLHttpRequest(); + + var url = getTextTrackUrl(track, item, '.js'); + + xhr.open('GET', url, true); + + xhr.onload = function (e) { + resolve(JSON.parse(this.response)); + }; + + xhr.onerror = reject; + + xhr.send(); + }); } function setTrackForCustomDisplay(videoElement, track) { - if (!track) return void destroyCustomTrack(videoElement); - if (customTrackIndex !== track.Index) { - var item = self._currentPlayOptions.item; - destroyCustomTrack(videoElement), customTrackIndex = track.Index, renderTracksEvents(videoElement, track, item), lastCustomTrackMs = 0 + + if (!track) { + destroyCustomTrack(videoElement); + return; } + + // if already playing thids track, skip + if (customTrackIndex === track.Index) { + return; + } + + var item = self._currentPlayOptions.item; + + destroyCustomTrack(videoElement); + customTrackIndex = track.Index; + renderTracksEvents(videoElement, track, item); + lastCustomTrackMs = 0; } function renderWithLibjass(videoElement, track, item) { + var rendererSettings = {}; - browser.ps4 ? rendererSettings.enableSvg = !1 : (browser.edge || browser.msie) && (rendererSettings.enableSvg = !1), rendererSettings.enableSvg = !1, require(["libjass", "ResizeObserver"], function(libjass, ResizeObserver) { - libjass.ASS.fromUrl(getTextTrackUrl(track, item)).then(function(ass) { - var clock = new libjass.renderers.ManualClock; + + if (browser.ps4) { + // Text outlines are not rendering very well + rendererSettings.enableSvg = false; + } + else if (browser.edge || browser.msie) { + // svg not rendering at all + rendererSettings.enableSvg = false; + } + + // probably safer to just disable everywhere + rendererSettings.enableSvg = false; + + require(['libjass', 'ResizeObserver'], function (libjass, ResizeObserver) { + + libjass.ASS.fromUrl(getTextTrackUrl(track, item)).then(function (ass) { + + var clock = new libjass.renderers.ManualClock(); currentClock = clock; + + // Create a DefaultRenderer using the video element and the ASS object var renderer = new libjass.renderers.WebRenderer(ass, clock, videoElement.parentNode, rendererSettings); - currentAssRenderer = renderer, renderer.addEventListener("ready", function() { + + currentAssRenderer = renderer; + + renderer.addEventListener("ready", function () { try { - renderer.resize(videoElement.offsetWidth, videoElement.offsetHeight, 0, 0), self._resizeObserver || (self._resizeObserver = new ResizeObserver(onVideoResize, {}), self._resizeObserver.observe(videoElement)) - } catch (ex) {} - }) - }, function() { - htmlMediaHelper.onErrorInternal(self, "mediadecodeerror") - }) - }) + renderer.resize(videoElement.offsetWidth, videoElement.offsetHeight, 0, 0); + + if (!self._resizeObserver) { + self._resizeObserver = new ResizeObserver(onVideoResize, {}); + self._resizeObserver.observe(videoElement); + } + //clock.pause(); + } catch (ex) { + //alert(ex); + } + }); + }, function () { + htmlMediaHelper.onErrorInternal(self, 'mediadecodeerror'); + }); + }); } function onVideoResize() { - browser.iOS ? setTimeout(resetVideoRendererSize, 500) : resetVideoRendererSize() + + if (browser.iOS) { + + // with wkwebview, the new sizes will be delayed for about 500ms + setTimeout(resetVideoRendererSize, 500); + } else { + resetVideoRendererSize(); + } } function resetVideoRendererSize() { var renderer = currentAssRenderer; if (renderer) { - var videoElement = self._mediaElement, - width = videoElement.offsetWidth, - height = videoElement.offsetHeight; - console.log("videoElement resized: " + width + "x" + height), renderer.resize(width, height, 0, 0) + var videoElement = self._mediaElement; + var width = videoElement.offsetWidth; + var height = videoElement.offsetHeight; + console.log('videoElement resized: ' + width + 'x' + height); + renderer.resize(width, height, 0, 0); } } function requiresCustomSubtitlesElement() { - if (browser.ps4) return !0; - if (browser.firefox || browser.web0s) return !0; - if (browser.edge) return !0; + + // after a system update, ps4 isn't showing anything when creating a track element dynamically + // going to have to do it ourselves + if (browser.ps4) { + return true; + } + + // This is unfortunate, but we're unable to remove the textTrack that gets added via addTextTrack + if (browser.firefox || browser.web0s) { + return true; + } + + if (browser.edge) { + return true; + } + if (browser.iOS) { var userAgent = navigator.userAgent.toLowerCase(); - if ((-1 !== userAgent.indexOf("os 9") || -1 !== userAgent.indexOf("os 8")) && -1 === userAgent.indexOf("safari")) return !0 + // works in the browser but not the native app + if ((userAgent.indexOf('os 9') !== -1 || userAgent.indexOf('os 8') !== -1) && userAgent.indexOf('safari') === -1) { + return true; + } } - return !1 + + return false; } function renderSubtitlesWithCustomElement(videoElement, track, item) { - fetchSubtitles(track, item).then(function(data) { + + fetchSubtitles(track, item).then(function (data) { if (!videoSubtitlesElem) { - var subtitlesContainer = document.createElement("div"); - subtitlesContainer.classList.add("videoSubtitles"), subtitlesContainer.innerHTML = '
', videoSubtitlesElem = subtitlesContainer.querySelector(".videoSubtitlesInner"), setSubtitleAppearance(subtitlesContainer, videoSubtitlesElem), videoElement.parentNode.appendChild(subtitlesContainer), currentTrackEvents = data.TrackEvents + var subtitlesContainer = document.createElement('div'); + subtitlesContainer.classList.add('videoSubtitles'); + subtitlesContainer.innerHTML = '
'; + videoSubtitlesElem = subtitlesContainer.querySelector('.videoSubtitlesInner'); + setSubtitleAppearance(subtitlesContainer, videoSubtitlesElem); + videoElement.parentNode.appendChild(subtitlesContainer); + currentTrackEvents = data.TrackEvents; } - }) + }); } function setSubtitleAppearance(elem, innerElem) { - require(["userSettings", "subtitleAppearanceHelper"], function(userSettings, subtitleAppearanceHelper) { + + require(['userSettings', 'subtitleAppearanceHelper'], function (userSettings, subtitleAppearanceHelper) { + subtitleAppearanceHelper.applyStyles({ + text: innerElem, window: elem - }, userSettings.getSubtitleAppearanceSettings()) - }) + + }, userSettings.getSubtitleAppearanceSettings()); + }); } function getCueCss(appearance, selector) { - var html = selector + "::cue {"; - return html += appearance.text.map(function(s) { - return s.name + ":" + s.value + "!important;" - }).join(""), html += "}" + + var html = selector + '::cue {'; + + html += appearance.text.map(function (s) { + + return s.name + ':' + s.value + '!important;'; + + }).join(''); + + html += '}'; + + return html; } function setCueAppearance() { - require(["userSettings", "subtitleAppearanceHelper"], function(userSettings, subtitleAppearanceHelper) { - var elementId = self.id + "-cuestyle", - styleElem = document.querySelector("#" + elementId); - styleElem || (styleElem = document.createElement("style"), styleElem.id = elementId, styleElem.type = "text/css", document.getElementsByTagName("head")[0].appendChild(styleElem)), styleElem.innerHTML = getCueCss(subtitleAppearanceHelper.getStyles(userSettings.getSubtitleAppearanceSettings(), !0), ".htmlvideoplayer") - }) + + require(['userSettings', 'subtitleAppearanceHelper'], function (userSettings, subtitleAppearanceHelper) { + + var elementId = self.id + '-cuestyle'; + + var styleElem = document.querySelector('#' + elementId); + if (!styleElem) { + styleElem = document.createElement('style'); + styleElem.id = elementId; + styleElem.type = 'text/css'; + document.getElementsByTagName('head')[0].appendChild(styleElem); + } + + styleElem.innerHTML = getCueCss(subtitleAppearanceHelper.getStyles(userSettings.getSubtitleAppearanceSettings(), true), '.htmlvideoplayer'); + }); } function renderTracksEvents(videoElement, track, item) { + if (!itemHelper.isLocalItem(item) || track.IsExternal) { - var format = (track.Codec || "").toLowerCase(); - if ("ssa" === format || "ass" === format) return void renderWithLibjass(videoElement, track, item); - if (requiresCustomSubtitlesElement()) return void renderSubtitlesWithCustomElement(videoElement, track, item) + var format = (track.Codec || '').toLowerCase(); + if (format === 'ssa' || format === 'ass') { + // libjass is needed here + renderWithLibjass(videoElement, track, item); + return; + } + + if (requiresCustomSubtitlesElement()) { + renderSubtitlesWithCustomElement(videoElement, track, item); + return; + } } - for (var trackElement = null, expectedId = "manualTrack" + track.Index, allTracks = videoElement.textTracks, i = 0; i < allTracks.length; i++) { + + var trackElement = null; + var expectedId = 'manualTrack' + track.Index; + + var allTracks = videoElement.textTracks; // get list of tracks + for (var i = 0; i < allTracks.length; i++) { + var currentTrack = allTracks[i]; + if (currentTrack.label === expectedId) { trackElement = currentTrack; - break + break; + } else { + currentTrack.mode = 'disabled'; } - currentTrack.mode = "disabled" } - trackElement ? trackElement.mode = "showing" : (trackElement = videoElement.addTextTrack("subtitles", "manualTrack" + track.Index, track.Language || "und"), fetchSubtitles(track, item).then(function(data) { - console.log("downloaded " + data.TrackEvents.length + " track events"), data.TrackEvents.forEach(function(trackEvent) { - var trackCueObject = window.VTTCue || window.TextTrackCue, - cue = new trackCueObject(trackEvent.StartPositionTicks / 1e7, trackEvent.EndPositionTicks / 1e7, normalizeTrackEventText(trackEvent.Text)); - trackElement.addCue(cue) - }), trackElement.mode = "showing" - })) + + if (!trackElement) { + trackElement = videoElement.addTextTrack('subtitles', 'manualTrack' + track.Index, track.Language || 'und'); + + // download the track json + fetchSubtitles(track, item).then(function (data) { + + // show in ui + console.log('downloaded ' + data.TrackEvents.length + ' track events'); + // add some cues to show the text + // in safari, the cues need to be added before setting the track mode to showing + data.TrackEvents.forEach(function (trackEvent) { + + var trackCueObject = window.VTTCue || window.TextTrackCue; + var cue = new trackCueObject(trackEvent.StartPositionTicks / 10000000, trackEvent.EndPositionTicks / 10000000, normalizeTrackEventText(trackEvent.Text)); + + trackElement.addCue(cue); + }); + trackElement.mode = 'showing'; + }); + } else { + trackElement.mode = 'showing'; + } } function updateSubtitleText(timeMs) { + var clock = currentClock; - if (clock) try { - clock.seek(timeMs / 1e3) - } catch (err) { - console.log("Error in libjass: " + err) - } else { - var trackEvents = currentTrackEvents, - subtitleTextElement = videoSubtitlesElem; - if (trackEvents && subtitleTextElement) { - for (var selectedTrackEvent, ticks = 1e4 * timeMs, i = 0; i < trackEvents.length; i++) { - var currentTrackEvent = trackEvents[i]; - if (currentTrackEvent.StartPositionTicks <= ticks && currentTrackEvent.EndPositionTicks >= ticks) { - selectedTrackEvent = currentTrackEvent; - break - } + if (clock) { + try { + clock.seek(timeMs / 1000); + } catch (err) { + console.log('Error in libjass: ' + err); + } + return; + } + + var trackEvents = currentTrackEvents; + var subtitleTextElement = videoSubtitlesElem; + + if (trackEvents && subtitleTextElement) { + var ticks = timeMs * 10000; + var selectedTrackEvent; + for (var i = 0; i < trackEvents.length; i++) { + + var currentTrackEvent = trackEvents[i]; + if (currentTrackEvent.StartPositionTicks <= ticks && currentTrackEvent.EndPositionTicks >= ticks) { + selectedTrackEvent = currentTrackEvent; + break; } - selectedTrackEvent && selectedTrackEvent.Text ? (subtitleTextElement.innerHTML = normalizeTrackEventText(selectedTrackEvent.Text), subtitleTextElement.classList.remove("hide")) : subtitleTextElement.classList.add("hide") + } + + if (selectedTrackEvent && selectedTrackEvent.Text) { + + subtitleTextElement.innerHTML = normalizeTrackEventText(selectedTrackEvent.Text); + subtitleTextElement.classList.remove('hide'); + + } else { + subtitleTextElement.classList.add('hide'); } } } function setCurrentTrackElement(streamIndex) { - console.log("Setting new text track index to: " + streamIndex); - var mediaStreamTextTracks = getMediaStreamTextTracks(self._currentPlayOptions.mediaSource), - track = -1 === streamIndex ? null : mediaStreamTextTracks.filter(function(t) { - return t.Index === streamIndex - })[0]; - enableNativeTrackSupport(self._currentSrc, track) ? (setTrackForCustomDisplay(self._mediaElement, null), -1 !== streamIndex && setCueAppearance()) : (setTrackForCustomDisplay(self._mediaElement, track), streamIndex = -1, track = null); - for (var expectedId = "textTrack" + streamIndex, trackIndex = -1 !== streamIndex && track ? mediaStreamTextTracks.indexOf(track) : -1, modes = ["disabled", "showing", "hidden"], allTracks = self._mediaElement.textTracks, i = 0; i < allTracks.length; i++) { - var currentTrack = allTracks[i]; - console.log("currentTrack id: " + currentTrack.id); - var mode; - if (console.log("expectedId: " + expectedId + "--currentTrack.Id:" + currentTrack.id), browser.msie || browser.edge) mode = trackIndex === i ? 1 : 0; - else { - if (-1 !== currentTrack.label.indexOf("manualTrack")) continue; - mode = currentTrack.id === expectedId ? 1 : 0 + + console.log('Setting new text track index to: ' + streamIndex); + + var mediaStreamTextTracks = getMediaStreamTextTracks(self._currentPlayOptions.mediaSource); + + var track = streamIndex === -1 ? null : mediaStreamTextTracks.filter(function (t) { + return t.Index === streamIndex; + })[0]; + + if (enableNativeTrackSupport(self._currentSrc, track)) { + + setTrackForCustomDisplay(self._mediaElement, null); + + if (streamIndex !== -1) { + setCueAppearance(); } - console.log("Setting track " + i + " mode to: " + mode), currentTrack.mode = modes[mode] + + } else { + setTrackForCustomDisplay(self._mediaElement, track); + + // null these out to disable the player's native display (handled below) + streamIndex = -1; + track = null; + } + + var expectedId = 'textTrack' + streamIndex; + var trackIndex = streamIndex === -1 || !track ? -1 : mediaStreamTextTracks.indexOf(track); + var modes = ['disabled', 'showing', 'hidden']; + + var allTracks = self._mediaElement.textTracks; // get list of tracks + for (var i = 0; i < allTracks.length; i++) { + + var currentTrack = allTracks[i]; + + console.log('currentTrack id: ' + currentTrack.id); + + var mode; + + console.log('expectedId: ' + expectedId + '--currentTrack.Id:' + currentTrack.id); + + // IE doesn't support track id + if (browser.msie || browser.edge) { + if (trackIndex === i) { + mode = 1; // show this track + } else { + mode = 0; // hide all other tracks + } + } else { + + if (currentTrack.label.indexOf('manualTrack') !== -1) { + continue; + } + if (currentTrack.id === expectedId) { + mode = 1; // show this track + } else { + mode = 0; // hide all other tracks + } + } + + console.log('Setting track ' + i + ' mode to: ' + mode); + + currentTrack.mode = modes[mode]; + } + } + + function updateTextStreamUrls(startPositionTicks) { + + if (!supportsTextTracks()) { + return; + } + + var allTracks = self._mediaElement.textTracks; // get list of tracks + var i; + var track; + + for (i = 0; i < allTracks.length; i++) { + + track = allTracks[i]; + + // This throws an error in IE, but is fine in chrome + // In IE it's not necessary anyway because changing the src seems to be enough + try { + while (track.cues.length) { + track.removeCue(track.cues[0]); + } + } catch (e) { + console.log('Error removing cue from textTrack'); + } + } + + var tracks = self._mediaElement.querySelectorAll('track'); + for (i = 0; i < tracks.length; i++) { + + track = tracks[i]; + + track.src = replaceQueryString(track.src, 'startPositionTicks', startPositionTicks); } } function createMediaElement(options) { - return (browser.tv || browser.iOS || browser.mobile) && (options.backdropUrl = null), new Promise(function(resolve, reject) { - var dlg = document.querySelector(".videoPlayerContainer"); - dlg ? (options.backdropUrl && (dlg.classList.add("videoPlayerContainer-withBackdrop"), dlg.style.backgroundImage = "url('" + options.backdropUrl + "')"), resolve(dlg.querySelector("video"))) : require(["css!./style"], function() { - loading.show(); - var dlg = document.createElement("div"); - dlg.classList.add("videoPlayerContainer"), options.backdropUrl && (dlg.classList.add("videoPlayerContainer-withBackdrop"), dlg.style.backgroundImage = "url('" + options.backdropUrl + "')"), options.fullscreen && dlg.classList.add("videoPlayerContainer-onTop"); - var html = "", - cssClass = "htmlvideoplayer"; - browser.chromecast || (cssClass += " htmlvideoplayer-moveupsubtitles"), appHost.supports("htmlvideoautoplay") ? html += '
"), html += "
", html += "
", html += "" + + var tagName = layoutManager.tv ? 'button' : 'div'; + var enableFooterButtons = !layoutManager.tv; + + var html = ''; + + var cssClass = "card scalableCard imageEditorCard"; + var cardBoxCssClass = 'cardBox visualCardBox'; + + var shape = 'backdrop'; + if (imageType === "Backdrop" || imageType === "Art" || imageType === "Thumb" || imageType === "Logo") { + shape = 'backdrop'; + } + else if (imageType === "Banner") { + shape = 'banner'; + } + else if (imageType === "Disc") { + shape = 'square'; + } + else { + + if (currentItemType === "Episode") { + shape = 'backdrop'; + } + else if (currentItemType === "MusicAlbum" || currentItemType === "MusicArtist") { + shape = 'square'; + } + else { + shape = 'portrait'; + } + } + + cssClass += ' ' + shape + 'Card ' + shape + 'Card-scalable'; + if (tagName === 'button') { + cssClass += ' btnImageCard'; + + if (layoutManager.tv && !browser.slow) { + cardBoxCssClass += ' cardBox-focustransform'; + } + + if (layoutManager.tv) { + cardBoxCssClass += ' card-focuscontent cardBox-withfocuscontent'; + } + + html += ''; + html += '
'; + } + + html += '
'; + // end footer + + html += '
'; + //html += '
'; + + html += ''; + + return html; } function initEditor(page, apiClient) { - page.querySelector("#selectBrowsableImageType").addEventListener("change", function() { - browsableImageType = this.value, browsableImageStartIndex = 0, selectedProvider = null, reloadBrowsableImages(page, apiClient) - }), page.querySelector("#selectImageProvider").addEventListener("change", function() { - browsableImageStartIndex = 0, selectedProvider = this.value, reloadBrowsableImages(page, apiClient) - }), page.querySelector("#chkAllLanguages").addEventListener("change", function() { - browsableImageStartIndex = 0, reloadBrowsableImages(page, apiClient) - }), page.addEventListener("click", function(e) { - var btnDownloadRemoteImage = parentWithClass(e.target, "btnDownloadRemoteImage"); + + page.querySelector('#selectBrowsableImageType').addEventListener('change', function () { + browsableImageType = this.value; + browsableImageStartIndex = 0; + selectedProvider = null; + + reloadBrowsableImages(page, apiClient); + }); + + page.querySelector('#selectImageProvider').addEventListener('change', function () { + + browsableImageStartIndex = 0; + selectedProvider = this.value; + + reloadBrowsableImages(page, apiClient); + }); + + page.querySelector('#chkAllLanguages').addEventListener('change', function () { + + browsableImageStartIndex = 0; + + reloadBrowsableImages(page, apiClient); + }); + + page.addEventListener('click', function (e) { + + var btnDownloadRemoteImage = parentWithClass(e.target, 'btnDownloadRemoteImage'); if (btnDownloadRemoteImage) { - var card = parentWithClass(btnDownloadRemoteImage, "card"); - return void downloadRemoteImage(page, apiClient, card.getAttribute("data-imageurl"), card.getAttribute("data-imagetype"), card.getAttribute("data-imageprovider")) + var card = parentWithClass(btnDownloadRemoteImage, 'card'); + downloadRemoteImage(page, apiClient, card.getAttribute('data-imageurl'), card.getAttribute('data-imagetype'), card.getAttribute('data-imageprovider')); + return; } - var btnImageCard = parentWithClass(e.target, "btnImageCard"); - btnImageCard && downloadRemoteImage(page, apiClient, btnImageCard.getAttribute("data-imageurl"), btnImageCard.getAttribute("data-imagetype"), btnImageCard.getAttribute("data-imageprovider")) - }) + + var btnImageCard = parentWithClass(e.target, 'btnImageCard'); + if (btnImageCard) { + downloadRemoteImage(page, apiClient, btnImageCard.getAttribute('data-imageurl'), btnImageCard.getAttribute('data-imagetype'), btnImageCard.getAttribute('data-imageprovider')); + } + }); } function showEditor(itemId, serverId, itemType) { - loading.show(), require(["text!./imagedownloader.template.html"], function(template) { + + loading.show(); + + require(['text!./imagedownloader.template.html'], function (template) { + var apiClient = connectionManager.getApiClient(serverId); - currentItemId = itemId, currentItemType = itemType; + + currentItemId = itemId; + currentItemType = itemType; + var dialogOptions = { - removeOnClose: !0 + removeOnClose: true }; - layoutManager.tv ? dialogOptions.size = "fullscreen" : dialogOptions.size = "fullscreen-border"; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'fullscreen-border'; + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.innerHTML = globalize.translateDocument(template, "sharedcomponents"), layoutManager.tv && scrollHelper.centerFocus.on(dlg, !1), dlg.addEventListener("close", onDialogClosed), dialogHelper.open(dlg); - var editorContent = dlg.querySelector(".formDialogContent"); - initEditor(editorContent, apiClient), dlg.querySelector(".btnCancel").addEventListener("click", function() { - dialogHelper.close(dlg) - }), reloadBrowsableImages(editorContent, apiClient) - }) + + dlg.innerHTML = globalize.translateDocument(template, 'sharedcomponents'); + + if (layoutManager.tv) { + scrollHelper.centerFocus.on(dlg, false); + } + + // Has to be assigned a z-index after the call to .open() + dlg.addEventListener('close', onDialogClosed); + + dialogHelper.open(dlg); + + var editorContent = dlg.querySelector('.formDialogContent'); + initEditor(editorContent, apiClient); + + dlg.querySelector('.btnCancel').addEventListener('click', function () { + + dialogHelper.close(dlg); + }); + + reloadBrowsableImages(editorContent, apiClient); + }); } function onDialogClosed() { + var dlg = this; - layoutManager.tv && scrollHelper.centerFocus.off(dlg, !1), loading.hide(), hasChanges ? currentResolve() : currentReject() - } - var currentItemId, currentItemType, currentResolve, currentReject, selectedProvider, hasChanges = !1, - browsableImagePageSize = browser.slow ? 6 : 30, - browsableImageStartIndex = 0, - browsableImageType = "Primary"; - return { - show: function(itemId, serverId, itemType, imageType) { - return new Promise(function(resolve, reject) { - currentResolve = resolve, currentReject = reject, hasChanges = !1, browsableImageStartIndex = 0, browsableImageType = imageType || "Primary", selectedProvider = null, showEditor(itemId, serverId, itemType) - }) + + if (layoutManager.tv) { + scrollHelper.centerFocus.off(dlg, false); + } + + loading.hide(); + if (hasChanges) { + currentResolve(); + } else { + currentReject(); } } + + return { + show: function (itemId, serverId, itemType, imageType) { + + return new Promise(function (resolve, reject) { + + currentResolve = resolve; + currentReject = reject; + hasChanges = false; + browsableImageStartIndex = 0; + browsableImageType = imageType || 'Primary'; + selectedProvider = null; + + showEditor(itemId, serverId, itemType); + }); + } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/imageeditor/imageeditor.css b/src/bower_components/emby-webcomponents/imageeditor/imageeditor.css index 46c5ecd743..724b9ffb0f 100644 --- a/src/bower_components/emby-webcomponents/imageeditor/imageeditor.css +++ b/src/bower_components/emby-webcomponents/imageeditor/imageeditor.css @@ -1,13 +1,9 @@ -.imageEditor-buttons { - display: -webkit-box; - display: -webkit-flex; +.imageEditor-buttons { display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - margin: 1em 0 + margin: 1em 0 1em; } .first-imageEditor-buttons { - margin-top: 2em + margin-top: 2em; } \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/imageeditor/imageeditor.js b/src/bower_components/emby-webcomponents/imageeditor/imageeditor.js index 98f527901e..339e7a0f32 100644 --- a/src/bower_components/emby-webcomponents/imageeditor/imageeditor.js +++ b/src/bower_components/emby-webcomponents/imageeditor/imageeditor.js @@ -1,220 +1,513 @@ -define(["dialogHelper", "connectionManager", "loading", "dom", "layoutManager", "focusManager", "globalize", "scrollHelper", "imageLoader", "require", "browser", "apphost", "cardStyle", "formDialogStyle", "emby-button", "paper-icon-button-light", "css!./imageeditor"], function(dialogHelper, connectionManager, loading, dom, layoutManager, focusManager, globalize, scrollHelper, imageLoader, require, browser, appHost) { - "use strict"; +define(['dialogHelper', 'connectionManager', 'loading', 'dom', 'layoutManager', 'focusManager', 'globalize', 'scrollHelper', 'imageLoader', 'require', 'browser', 'apphost', 'cardStyle', 'formDialogStyle', 'emby-button', 'paper-icon-button-light', 'css!./imageeditor'], function (dialogHelper, connectionManager, loading, dom, layoutManager, focusManager, globalize, scrollHelper, imageLoader, require, browser, appHost) { + 'use strict'; + + var currentItem; + var hasChanges = false; function getBaseRemoteOptions() { + var options = {}; - return options.itemId = currentItem.Id, options + + options.itemId = currentItem.Id; + + return options; } function reload(page, item, focusContext) { + loading.show(); + var apiClient; - item ? (apiClient = connectionManager.getApiClient(item.ServerId), reloadItem(page, item, apiClient, focusContext)) : (apiClient = connectionManager.getApiClient(currentItem.ServerId), apiClient.getItem(apiClient.getCurrentUserId(), currentItem.Id).then(function(item) { - reloadItem(page, item, apiClient, focusContext) - })) + + if (item) { + apiClient = connectionManager.getApiClient(item.ServerId); + reloadItem(page, item, apiClient, focusContext); + } + else { + + apiClient = connectionManager.getApiClient(currentItem.ServerId); + apiClient.getItem(apiClient.getCurrentUserId(), currentItem.Id).then(function (item) { + reloadItem(page, item, apiClient, focusContext); + }); + } } function addListeners(container, className, eventName, fn) { - container.addEventListener(eventName, function(e) { + + container.addEventListener(eventName, function (e) { var elem = dom.parentWithClass(e.target, className); - elem && fn.call(elem, e) - }) + if (elem) { + fn.call(elem, e); + } + }); } function reloadItem(page, item, apiClient, focusContext) { - currentItem = item, apiClient.getRemoteImageProviders(getBaseRemoteOptions()).then(function(providers) { - for (var btnBrowseAllImages = page.querySelectorAll(".btnBrowseAllImages"), i = 0, length = btnBrowseAllImages.length; i < length; i++) providers.length ? btnBrowseAllImages[i].classList.remove("hide") : btnBrowseAllImages[i].classList.add("hide"); - apiClient.getItemImageInfos(currentItem.Id).then(function(imageInfos) { - renderStandardImages(page, apiClient, item, imageInfos, providers), renderBackdrops(page, apiClient, item, imageInfos, providers), renderScreenshots(page, apiClient, item, imageInfos, providers), loading.hide(), layoutManager.tv && focusManager.autoFocus(focusContext || page) - }) - }) + + currentItem = item; + + apiClient.getRemoteImageProviders(getBaseRemoteOptions()).then(function (providers) { + + var btnBrowseAllImages = page.querySelectorAll('.btnBrowseAllImages'); + for (var i = 0, length = btnBrowseAllImages.length; i < length; i++) { + + if (providers.length) { + btnBrowseAllImages[i].classList.remove('hide'); + } else { + btnBrowseAllImages[i].classList.add('hide'); + } + } + + + apiClient.getItemImageInfos(currentItem.Id).then(function (imageInfos) { + + renderStandardImages(page, apiClient, item, imageInfos, providers); + renderBackdrops(page, apiClient, item, imageInfos, providers); + renderScreenshots(page, apiClient, item, imageInfos, providers); + loading.hide(); + + if (layoutManager.tv) { + focusManager.autoFocus((focusContext || page)); + } + }); + }); } function getImageUrl(item, apiClient, type, index, options) { - return options = options || {}, options.type = type, options.index = index, options.tag = "Backdrop" === type ? item.BackdropImageTags[index] : "Screenshot" === type ? item.ScreenshotImageTags[index] : "Primary" === type ? item.PrimaryImageTag || item.ImageTags[type] : item.ImageTags[type], apiClient.getScaledImageUrl(item.Id || item.ItemId, options) + + options = options || {}; + options.type = type; + options.index = index; + + if (type === 'Backdrop') { + options.tag = item.BackdropImageTags[index]; + } else if (type === 'Screenshot') { + options.tag = item.ScreenshotImageTags[index]; + } else if (type === 'Primary') { + options.tag = item.PrimaryImageTag || item.ImageTags[type]; + } else { + options.tag = item.ImageTags[type]; + } + + // For search hints + return apiClient.getScaledImageUrl(item.Id || item.ItemId, options); } function getCardHtml(image, index, numImages, apiClient, imageProviders, imageSize, tagName, enableFooterButtons) { - var html = "", - cssClass = "card scalableCard imageEditorCard", - cardBoxCssClass = "cardBox visualCardBox"; - return cssClass += " backdropCard backdropCard-scalable", "button" === tagName ? (cssClass += " btnImageCard", layoutManager.tv && !browser.slow && (cardBoxCssClass += " cardBox-focustransform"), layoutManager.tv && (cardBoxCssClass += " card-focuscontent cardBox-withfocuscontent"), html += '' : '', html += index < numImages - 1 ? '' : '') : imageProviders.length && (html += ''), html += '', html += "
"), html += "
", html += "
", html += "" + + var html = ''; + + var cssClass = "card scalableCard imageEditorCard"; + var cardBoxCssClass = 'cardBox visualCardBox'; + + cssClass += " backdropCard backdropCard-scalable"; + + if (tagName === 'button') { + cssClass += ' btnImageCard'; + + if (layoutManager.tv && !browser.slow) { + cardBoxCssClass += ' cardBox-focustransform'; + } + + if (layoutManager.tv) { + cardBoxCssClass += ' card-focuscontent cardBox-withfocuscontent'; + } + + html += ''; + } else { + html += ''; + } + + if (index < numImages - 1) { + html += ''; + } else { + html += ''; + } + } + else { + if (imageProviders.length) { + html += ''; + } + } + + html += ''; + html += '
'; + } + + html += '
'; + html += '
'; + html += ''; + + return html; } function deleteImage(context, itemId, type, index, apiClient, enableConfirmation) { - var afterConfirm = function() { - apiClient.deleteItemImage(itemId, type, index).then(function() { - hasChanges = !0, reload(context) - }) + + var afterConfirm = function () { + apiClient.deleteItemImage(itemId, type, index).then(function () { + + hasChanges = true; + reload(context); + + }); }; - if (!enableConfirmation) return void afterConfirm(); - require(["confirm"], function(confirm) { + + if (!enableConfirmation) { + afterConfirm(); + return; + } + + require(['confirm'], function (confirm) { + confirm({ - text: globalize.translate("sharedcomponents#ConfirmDeleteImage"), - confirmText: globalize.translate("sharedcomponents#Delete"), - primary: "cancel" - }).then(afterConfirm) - }) + + text: globalize.translate('sharedcomponents#ConfirmDeleteImage'), + confirmText: globalize.translate('sharedcomponents#Delete'), + primary: 'cancel' + + }).then(afterConfirm); + }); } function moveImage(context, apiClient, itemId, type, index, newIndex, focusContext) { - apiClient.updateItemImageIndex(itemId, type, index, newIndex).then(function() { - hasChanges = !0, reload(context, null, focusContext) - }, function() { - require(["alert"], function(alert) { - alert(globalize.translate("sharedcomponents#DefaultErrorMessage")) - }) - }) + + apiClient.updateItemImageIndex(itemId, type, index, newIndex).then(function () { + + hasChanges = true; + reload(context, null, focusContext); + }, function () { + + require(['alert'], function (alert) { + alert(globalize.translate('sharedcomponents#DefaultErrorMessage')); + }); + }); } function renderImages(page, item, apiClient, images, imageProviders, elem) { - var html = "", - imageSize = 300, - windowSize = dom.getWindowSize(); - windowSize.innerWidth >= 1280 && (imageSize = Math.round(windowSize.innerWidth / 4)); - for (var tagName = layoutManager.tv ? "button" : "div", enableFooterButtons = !layoutManager.tv, i = 0, length = images.length; i < length; i++) { - html += getCardHtml(images[i], i, length, apiClient, imageProviders, imageSize, tagName, enableFooterButtons) + + var html = ''; + + var imageSize = 300; + var windowSize = dom.getWindowSize(); + if (windowSize.innerWidth >= 1280) { + imageSize = Math.round(windowSize.innerWidth / 4); } - elem.innerHTML = html, imageLoader.lazyChildren(elem) + + var tagName = layoutManager.tv ? 'button' : 'div'; + var enableFooterButtons = !layoutManager.tv; + + for (var i = 0, length = images.length; i < length; i++) { + + var image = images[i]; + + html += getCardHtml(image, i, length, apiClient, imageProviders, imageSize, tagName, enableFooterButtons); + } + + elem.innerHTML = html; + imageLoader.lazyChildren(elem); } function renderStandardImages(page, apiClient, item, imageInfos, imageProviders) { - renderImages(page, item, apiClient, imageInfos.filter(function(i) { - return "Screenshot" !== i.ImageType && "Backdrop" !== i.ImageType && "Chapter" !== i.ImageType - }), imageProviders, page.querySelector("#images")) + + var images = imageInfos.filter(function (i) { + return i.ImageType !== "Screenshot" && i.ImageType !== "Backdrop" && i.ImageType !== "Chapter"; + }); + + renderImages(page, item, apiClient, images, imageProviders, page.querySelector('#images')); } function renderBackdrops(page, apiClient, item, imageInfos, imageProviders) { - var images = imageInfos.filter(function(i) { - return "Backdrop" === i.ImageType - }).sort(function(a, b) { - return a.ImageIndex - b.ImageIndex + + var images = imageInfos.filter(function (i) { + return i.ImageType === "Backdrop"; + + }).sort(function (a, b) { + return a.ImageIndex - b.ImageIndex; }); - images.length ? (page.querySelector("#backdropsContainer", page).classList.remove("hide"), renderImages(page, item, apiClient, images, imageProviders, page.querySelector("#backdrops"))) : page.querySelector("#backdropsContainer", page).classList.add("hide") + + if (images.length) { + page.querySelector('#backdropsContainer', page).classList.remove('hide'); + renderImages(page, item, apiClient, images, imageProviders, page.querySelector('#backdrops')); + } else { + page.querySelector('#backdropsContainer', page).classList.add('hide'); + } } function renderScreenshots(page, apiClient, item, imageInfos, imageProviders) { - var images = imageInfos.filter(function(i) { - return "Screenshot" === i.ImageType - }).sort(function(a, b) { - return a.ImageIndex - b.ImageIndex + + var images = imageInfos.filter(function (i) { + return i.ImageType === "Screenshot"; + + }).sort(function (a, b) { + return a.ImageIndex - b.ImageIndex; }); - images.length ? (page.querySelector("#screenshotsContainer", page).classList.remove("hide"), renderImages(page, item, apiClient, images, imageProviders, page.querySelector("#screenshots"))) : page.querySelector("#screenshotsContainer", page).classList.add("hide") + + if (images.length) { + page.querySelector('#screenshotsContainer', page).classList.remove('hide'); + renderImages(page, item, apiClient, images, imageProviders, page.querySelector('#screenshots')); + } else { + page.querySelector('#screenshotsContainer', page).classList.add('hide'); + } } function showImageDownloader(page, imageType) { - require(["imageDownloader"], function(ImageDownloader) { - ImageDownloader.show(currentItem.Id, currentItem.ServerId, currentItem.Type, imageType).then(function() { - hasChanges = !0, reload(page) - }) - }) + + require(['imageDownloader'], function (ImageDownloader) { + + ImageDownloader.show(currentItem.Id, currentItem.ServerId, currentItem.Type, imageType).then(function () { + + hasChanges = true; + reload(page); + }); + + }); } function showActionSheet(context, imageCard) { - var itemId = imageCard.getAttribute("data-id"), - serverId = imageCard.getAttribute("data-serverid"), - apiClient = connectionManager.getApiClient(serverId), - type = imageCard.getAttribute("data-imagetype"), - index = parseInt(imageCard.getAttribute("data-index")), - providerCount = parseInt(imageCard.getAttribute("data-providers")), - numImages = parseInt(imageCard.getAttribute("data-numimages")); - require(["actionsheet"], function(actionSheet) { + + var itemId = imageCard.getAttribute('data-id'); + var serverId = imageCard.getAttribute('data-serverid'); + var apiClient = connectionManager.getApiClient(serverId); + + var type = imageCard.getAttribute('data-imagetype'); + var index = parseInt(imageCard.getAttribute('data-index')); + var providerCount = parseInt(imageCard.getAttribute('data-providers')); + var numImages = parseInt(imageCard.getAttribute('data-numimages')); + + require(['actionsheet'], function (actionSheet) { + var commands = []; + commands.push({ - name: globalize.translate("sharedcomponents#Delete"), - id: "delete" - }), "Backdrop" !== type && "Screenshot" !== type || (index > 0 && commands.push({ - name: globalize.translate("sharedcomponents#MoveLeft"), - id: "moveleft" - }), index < numImages - 1 && commands.push({ - name: globalize.translate("sharedcomponents#MoveRight"), - id: "moveright" - })), providerCount && commands.push({ - name: globalize.translate("sharedcomponents#Search"), - id: "search" - }), actionSheet.show({ + name: globalize.translate('sharedcomponents#Delete'), + id: 'delete' + }); + + if (type === 'Backdrop' || type === 'Screenshot') { + if (index > 0) { + commands.push({ + name: globalize.translate('sharedcomponents#MoveLeft'), + id: 'moveleft' + }); + } + + if (index < numImages - 1) { + commands.push({ + name: globalize.translate('sharedcomponents#MoveRight'), + id: 'moveright' + }); + } + } + + if (providerCount) { + commands.push({ + name: globalize.translate('sharedcomponents#Search'), + id: 'search' + }); + } + + actionSheet.show({ + items: commands, positionTo: imageCard - }).then(function(id) { + + }).then(function (id) { + switch (id) { - case "delete": - deleteImage(context, itemId, type, index, apiClient, !1); + + case 'delete': + deleteImage(context, itemId, type, index, apiClient, false); break; - case "search": + case 'search': showImageDownloader(context, type); break; - case "moveleft": - moveImage(context, apiClient, itemId, type, index, index - 1, dom.parentWithClass(imageCard, "itemsContainer")); + case 'moveleft': + moveImage(context, apiClient, itemId, type, index, index - 1, dom.parentWithClass(imageCard, 'itemsContainer')); + break; + case 'moveright': + moveImage(context, apiClient, itemId, type, index, index + 1, dom.parentWithClass(imageCard, 'itemsContainer')); + break; + default: break; - case "moveright": - moveImage(context, apiClient, itemId, type, index, index + 1, dom.parentWithClass(imageCard, "itemsContainer")) } - }) - }) + + }); + }); } function initEditor(context, options) { - for (var uploadButtons = context.querySelectorAll(".btnOpenUploadMenu"), isFileInputSupported = appHost.supports("fileinput"), i = 0, length = uploadButtons.length; i < length; i++) isFileInputSupported ? uploadButtons[i].classList.remove("hide") : uploadButtons[i].classList.add("hide"); - addListeners(context, "btnOpenUploadMenu", "click", function() { - var imageType = this.getAttribute("data-imagetype"); - require(["imageUploader"], function(imageUploader) { + + var uploadButtons = context.querySelectorAll('.btnOpenUploadMenu'); + var isFileInputSupported = appHost.supports('fileinput'); + for (var i = 0, length = uploadButtons.length; i < length; i++) { + if (isFileInputSupported) { + uploadButtons[i].classList.remove('hide'); + } else { + uploadButtons[i].classList.add('hide'); + } + } + + addListeners(context, 'btnOpenUploadMenu', 'click', function () { + var imageType = this.getAttribute('data-imagetype'); + + require(['imageUploader'], function (imageUploader) { + imageUploader.show({ + theme: options.theme, imageType: imageType, itemId: currentItem.Id, serverId: currentItem.ServerId - }).then(function(hasChanged) { - hasChanged && (hasChanges = !0, reload(context)) - }) - }) - }), addListeners(context, "btnSearchImages", "click", function() { - showImageDownloader(context, this.getAttribute("data-imagetype")) - }), addListeners(context, "btnBrowseAllImages", "click", function() { - showImageDownloader(context, this.getAttribute("data-imagetype") || "Primary") - }), addListeners(context, "btnImageCard", "click", function() { - showActionSheet(context, this) - }), addListeners(context, "btnDeleteImage", "click", function() { - var type = this.getAttribute("data-imagetype"), - index = this.getAttribute("data-index"); - index = "null" === index ? null : parseInt(index); + + }).then(function (hasChanged) { + + if (hasChanged) { + hasChanges = true; + reload(context); + } + }); + }); + }); + + addListeners(context, 'btnSearchImages', 'click', function () { + showImageDownloader(context, this.getAttribute('data-imagetype')); + }); + + addListeners(context, 'btnBrowseAllImages', 'click', function () { + showImageDownloader(context, this.getAttribute('data-imagetype') || 'Primary'); + }); + + addListeners(context, 'btnImageCard', 'click', function () { + showActionSheet(context, this); + }); + + addListeners(context, 'btnDeleteImage', 'click', function () { + var type = this.getAttribute('data-imagetype'); + var index = this.getAttribute('data-index'); + index = index === "null" ? null : parseInt(index); var apiClient = connectionManager.getApiClient(currentItem.ServerId); - deleteImage(context, currentItem.Id, type, index, apiClient, !0) - }), addListeners(context, "btnMoveImage", "click", function() { - var type = this.getAttribute("data-imagetype"), - index = this.getAttribute("data-index"), - newIndex = this.getAttribute("data-newindex"), - apiClient = connectionManager.getApiClient(currentItem.ServerId); - moveImage(context, apiClient, currentItem.Id, type, index, newIndex, dom.parentWithClass(this, "itemsContainer")) - }) + deleteImage(context, currentItem.Id, type, index, apiClient, true); + }); + + addListeners(context, 'btnMoveImage', 'click', function () { + var type = this.getAttribute('data-imagetype'); + var index = this.getAttribute('data-index'); + var newIndex = this.getAttribute('data-newindex'); + var apiClient = connectionManager.getApiClient(currentItem.ServerId); + moveImage(context, apiClient, currentItem.Id, type, index, newIndex, dom.parentWithClass(this, 'itemsContainer')); + }); } function showEditor(options, resolve, reject) { - var itemId = options.itemId, - serverId = options.serverId; - loading.show(), require(["text!./imageeditor.template.html"], function(template) { + + var itemId = options.itemId; + var serverId = options.serverId; + + loading.show(); + + require(['text!./imageeditor.template.html'], function (template) { var apiClient = connectionManager.getApiClient(serverId); - apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function(item) { + apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) { + var dialogOptions = { - removeOnClose: !0 + removeOnClose: true }; - layoutManager.tv ? dialogOptions.size = "fullscreen" : dialogOptions.size = "fullscreen-border"; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'fullscreen-border'; + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"), dlg.innerHTML = globalize.translateDocument(template, "sharedcomponents"), layoutManager.tv && scrollHelper.centerFocus.on(dlg, !1), initEditor(dlg, options), dlg.addEventListener("close", function() { - layoutManager.tv && scrollHelper.centerFocus.off(dlg, !1), loading.hide(), hasChanges ? resolve() : reject() - }), dialogHelper.open(dlg), reload(dlg, item), dlg.querySelector(".btnCancel").addEventListener("click", function() { - dialogHelper.close(dlg) - }) - }) - }) + + dlg.classList.add('formDialog'); + + dlg.innerHTML = globalize.translateDocument(template, 'sharedcomponents'); + + if (layoutManager.tv) { + scrollHelper.centerFocus.on(dlg, false); + } + + initEditor(dlg, options); + + // Has to be assigned a z-index after the call to .open() + dlg.addEventListener('close', function () { + + if (layoutManager.tv) { + scrollHelper.centerFocus.off(dlg, false); + } + + loading.hide(); + + if (hasChanges) { + resolve(); + } else { + reject(); + } + }); + + dialogHelper.open(dlg); + + reload(dlg, item); + + dlg.querySelector('.btnCancel').addEventListener('click', function () { + + dialogHelper.close(dlg); + }); + }); + }); } - var currentItem, hasChanges = !1; + return { - show: function(options) { - return new Promise(function(resolve, reject) { - hasChanges = !1, showEditor(options, resolve, reject) - }) + show: function (options) { + + return new Promise(function (resolve, reject) { + + hasChanges = false; + + showEditor(options, resolve, reject); + }); } - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/images/basicimagefetcher.js b/src/bower_components/emby-webcomponents/images/basicimagefetcher.js index 7ba86474e0..54d3ef7502 100644 --- a/src/bower_components/emby-webcomponents/images/basicimagefetcher.js +++ b/src/bower_components/emby-webcomponents/images/basicimagefetcher.js @@ -1,18 +1,38 @@ -define(["dom"], function(dom) { - "use strict"; +define(['dom'], function (dom) { + 'use strict'; function loadImage(elem, url) { - return elem ? "IMG" !== elem.tagName ? (elem.style.backgroundImage = "url('" + url + "')", Promise.resolve()) : loadImageIntoImg(elem, url) : Promise.reject("elem cannot be null") + + if (!elem) { + return Promise.reject('elem cannot be null'); + } + + if (elem.tagName !== "IMG") { + + elem.style.backgroundImage = "url('" + url + "')"; + return Promise.resolve(); + + //return loadImageIntoImg(document.createElement('img'), url).then(function () { + // elem.style.backgroundImage = "url('" + url + "')"; + // return Promise.resolve(); + //}); + + } + return loadImageIntoImg(elem, url); } function loadImageIntoImg(elem, url) { - return new Promise(function(resolve, reject) { - dom.addEventListener(elem, "load", resolve, { - once: !0 - }), elem.setAttribute("src", url) - }) + return new Promise(function (resolve, reject) { + + dom.addEventListener(elem, 'load', resolve, { + once: true + }); + elem.setAttribute("src", url); + }); } + return { loadImage: loadImage - } + }; + }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/images/imagehelper.js b/src/bower_components/emby-webcomponents/images/imagehelper.js index ddcf5b7ba7..106de3a696 100644 --- a/src/bower_components/emby-webcomponents/images/imagehelper.js +++ b/src/bower_components/emby-webcomponents/images/imagehelper.js @@ -1,82 +1,255 @@ -define(["lazyLoader", "imageFetcher", "layoutManager", "browser", "appSettings", "require", "css!./style"], function(lazyLoader, imageFetcher, layoutManager, browser, appSettings, require) { - "use strict"; +define(['lazyLoader', 'imageFetcher', 'layoutManager', 'browser', 'appSettings', 'require', 'css!./style'], function (lazyLoader, imageFetcher, layoutManager, browser, appSettings, require) { + 'use strict'; + + var requestIdleCallback = window.requestIdleCallback || function (fn) { + fn(); + }; + + var self = {}; + + // seeing slow performance with firefox + var enableFade = false; function fillImage(elem, source, enableEffects) { - if (!elem) throw new Error("elem cannot be null"); - source || (source = elem.getAttribute("data-src")), source && fillImageElement(elem, source, enableEffects) + + if (!elem) { + throw new Error('elem cannot be null'); + } + + if (!source) { + source = elem.getAttribute('data-src'); + } + + if (!source) { + return; + } + + fillImageElement(elem, source, enableEffects); } function fillImageElement(elem, source, enableEffects) { - imageFetcher.loadImage(elem, source).then(function() { - enableFade && !1 !== enableEffects && fadeIn(elem), elem.removeAttribute("data-src") - }) + imageFetcher.loadImage(elem, source).then(function () { + + var fillingVibrant = false;//fillVibrant(elem, source); + + if (enableFade && enableEffects !== false && !fillingVibrant) { + fadeIn(elem); + } + + elem.removeAttribute("data-src"); + }); + } + + function fillVibrant(img, url, canvas, canvasContext) { + + var vibrantElement = img.getAttribute('data-vibrant'); + if (!vibrantElement) { + return false; + } + + if (window.Vibrant) { + fillVibrantOnLoaded(img, url, vibrantElement, canvas, canvasContext); + return true; + } + + require(['vibrant'], function () { + fillVibrantOnLoaded(img, url, vibrantElement, canvas, canvasContext); + }); + return true; + } + + function fillVibrantOnLoaded(img, url, vibrantElement) { + + vibrantElement = document.getElementById(vibrantElement); + if (!vibrantElement) { + return; + } + + requestIdleCallback(function () { + + //var now = new Date().getTime(); + getVibrantInfoFromElement(img, url).then(function (vibrantInfo) { + + var swatch = vibrantInfo.split('|'); + //console.log('vibrant took ' + (new Date().getTime() - now) + 'ms'); + if (swatch.length) { + + var index = 0; + var style = vibrantElement.style; + style.backgroundColor = swatch[index]; + style.color = swatch[index + 1]; + + var classList = vibrantElement.classList; + + if (classList.contains('cardFooter')) { + classList.add('cardFooter-vibrant'); + } else { + classList.add('vibrant'); + } + } + }); + }); + /* + * Results into: + * Vibrant #7a4426 + * Muted #7b9eae + * DarkVibrant #348945 + * DarkMuted #141414 + * LightVibrant #f3ccb4 + */ } function getVibrantInfoFromElement(elem, url) { - return new Promise(function(resolve, reject) { - require(["vibrant"], function() { - if ("IMG" === elem.tagName) return void resolve(getVibrantInfo(elem, url)); - var img = new Image; - img.onload = function() { - resolve(getVibrantInfo(img, url)) - }, img.src = url - }) - }) + + return new Promise(function (resolve, reject) { + + require(['vibrant'], function () { + + if (elem.tagName === 'IMG') { + resolve(getVibrantInfo(elem, url)); + return; + } + + var img = new Image(); + img.onload = function () { + resolve(getVibrantInfo(img, url)); + }; + img.src = url; + }); + }); } function getSettingsKey(url) { - var parts = url.split("://"); - url = parts[parts.length - 1], url = url.substring(url.indexOf("/") + 1), url = url.split("?")[0]; - return "vibrant31" + url + + var parts = url.split('://'); + url = parts[parts.length - 1]; + + url = url.substring(url.indexOf('/') + 1); + + url = url.split('?')[0]; + + var cacheKey = 'vibrant31'; + //cacheKey = 'vibrant' + new Date().getTime(); + return cacheKey + url; } function getCachedVibrantInfo(url) { - return appSettings.get(getSettingsKey(url)) + + return appSettings.get(getSettingsKey(url)); } function getVibrantInfo(img, url) { + var value = getCachedVibrantInfo(url); - if (value) return value; - var vibrant = new Vibrant(img), - swatches = vibrant.swatches(); - return value = "", value += getSwatchString(swatches.DarkVibrant), appSettings.set(getSettingsKey(url), value), value + if (value) { + return value; + } + + var vibrant = new Vibrant(img); + var swatches = vibrant.swatches(); + + value = ''; + var swatch = swatches.DarkVibrant; + value += getSwatchString(swatch); + + appSettings.set(getSettingsKey(url), value); + + return value; } function getSwatchString(swatch) { - return swatch ? swatch.getHex() + "|" + swatch.getBodyTextColor() + "|" + swatch.getTitleTextColor() : "||" + + if (swatch) { + return swatch.getHex() + '|' + swatch.getBodyTextColor() + '|' + swatch.getTitleTextColor(); + } + return '||'; } function fadeIn(elem) { - elem.classList.add("lazy-image-fadein") + + var cssClass = 'lazy-image-fadein'; + + elem.classList.add(cssClass); } function lazyChildren(elem) { - lazyLoader.lazyChildren(elem, fillImage) + + lazyLoader.lazyChildren(elem, fillImage); } function getPrimaryImageAspectRatio(items) { - for (var values = [], i = 0, length = items.length; i < length; i++) { + + var values = []; + + for (var i = 0, length = items.length; i < length; i++) { + var ratio = items[i].PrimaryImageAspectRatio || 0; - ratio && (values[values.length] = ratio) + + if (!ratio) { + continue; + } + + values[values.length] = ratio; } - if (!values.length) return null; - values.sort(function(a, b) { - return a - b - }); - var result, half = Math.floor(values.length / 2); - result = values.length % 2 ? values[half] : (values[half - 1] + values[half]) / 2; - if (Math.abs(2 / 3 - result) <= .15) return 2 / 3; - if (Math.abs(16 / 9 - result) <= .2) return 16 / 9; - if (Math.abs(1 - result) <= .15) return 1; - return Math.abs(4 / 3 - result) <= .15 ? 4 / 3 : result + + if (!values.length) { + return null; + } + + // Use the median + values.sort(function (a, b) { return a - b; }); + + var half = Math.floor(values.length / 2); + + var result; + + if (values.length % 2) { + result = values[half]; + } + else { + result = (values[half - 1] + values[half]) / 2.0; + } + + // If really close to 2:3 (poster image), just return 2:3 + var aspect2x3 = 2 / 3; + if (Math.abs(aspect2x3 - result) <= 0.15) { + return aspect2x3; + } + + // If really close to 16:9 (episode image), just return 16:9 + var aspect16x9 = 16 / 9; + if (Math.abs(aspect16x9 - result) <= 0.2) { + return aspect16x9; + } + + // If really close to 1 (square image), just return 1 + if (Math.abs(1 - result) <= 0.15) { + return 1; + } + + // If really close to 4:3 (poster image), just return 2:3 + var aspect4x3 = 4 / 3; + if (Math.abs(aspect4x3 - result) <= 0.15) { + return aspect4x3; + } + + return result; } function fillImages(elems) { + for (var i = 0, length = elems.length; i < length; i++) { - fillImage(elems[0]) + var elem = elems[0]; + fillImage(elem); } } - var self = (window.requestIdleCallback, {}), - enableFade = !1; - return self.fillImages = fillImages, self.lazyImage = fillImage, self.lazyChildren = lazyChildren, self.getPrimaryImageAspectRatio = getPrimaryImageAspectRatio, self.getCachedVibrantInfo = getCachedVibrantInfo, self.getVibrantInfoFromElement = getVibrantInfoFromElement, self + + self.fillImages = fillImages; + self.lazyImage = fillImage; + self.lazyChildren = lazyChildren; + self.getPrimaryImageAspectRatio = getPrimaryImageAspectRatio; + self.getCachedVibrantInfo = getCachedVibrantInfo; + self.getVibrantInfoFromElement = getVibrantInfoFromElement; + + return self; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/images/style.css b/src/bower_components/emby-webcomponents/images/style.css index e2fb63c310..5cf39c1e4b 100644 --- a/src/bower_components/emby-webcomponents/images/style.css +++ b/src/bower_components/emby-webcomponents/images/style.css @@ -1,61 +1,47 @@ .lazy-image-fadein { - -webkit-animation: lazy-image-fadein 330ms ease-in normal both; - animation: lazy-image-fadein 330ms ease-in normal both + animation: lazy-image-fadein 330ms ease-in normal both; } .lazy-image-fadein-fast { - -webkit-animation: lazy-image-fadein 160ms ease-in normal both; - animation: lazy-image-fadein 160ms ease-in normal both -} - -@-webkit-keyframes lazy-image-fadein { - from { - opacity: 0 - } - - to { - opacity: 1 - } + animation: lazy-image-fadein 160ms ease-in normal both; } @keyframes lazy-image-fadein { from { - opacity: 0 + opacity: 0; } to { - opacity: 1 + opacity: 1; } } .lazy-image-fadein { opacity: 0; -webkit-animation-duration: .8s; + -moz-animation-duration: .8s; + -o-animation-duration: .8s; animation-duration: .8s; -webkit-animation-name: popInAnimation; + -moz-animation-name: popInAnimation; + -o-animation-name: popInAnimation; animation-name: popInAnimation; -webkit-animation-fill-mode: forwards; + -moz-animation-fill-mode: forwards; + -o-animation-fill-mode: forwards; animation-fill-mode: forwards; - -webkit-animation-timing-function: cubic-bezier(0, 0, .5, 1); - animation-timing-function: cubic-bezier(0, 0, .5, 1) -} - -@-webkit-keyframes popInAnimation { - 0% { - opacity: 0 - } - - 100% { - opacity: 1 - } + -webkit-animation-timing-function: cubic-bezier(0,0,.5,1); + -moz-animation-timing-function: cubic-bezier(0,0,.5,1); + -o-animation-timing-function: cubic-bezier(0,0,.5,1); + animation-timing-function: cubic-bezier(0,0,.5,1); } @keyframes popInAnimation { 0% { - opacity: 0 + opacity: 0; } 100% { - opacity: 1 + opacity: 1; } } \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/imageuploader/imageuploader.js b/src/bower_components/emby-webcomponents/imageuploader/imageuploader.js index 87a0d643ba..eb68505118 100644 --- a/src/bower_components/emby-webcomponents/imageuploader/imageuploader.js +++ b/src/bower_components/emby-webcomponents/imageuploader/imageuploader.js @@ -1,80 +1,188 @@ -define(["dialogHelper", "connectionManager", "dom", "loading", "scrollHelper", "layoutManager", "globalize", "require", "emby-button", "emby-select", "formDialogStyle", "css!./style"], function(dialogHelper, connectionManager, dom, loading, scrollHelper, layoutManager, globalize, require) { - "use strict"; +define(['dialogHelper', 'connectionManager', 'dom', 'loading', 'scrollHelper', 'layoutManager', 'globalize', 'require', 'emby-button', 'emby-select', 'formDialogStyle', 'css!./style'], function (dialogHelper, connectionManager, dom, loading, scrollHelper, layoutManager, globalize, require) { + 'use strict'; + + var currentItemId; + var currentServerId; + var currentFile; + var hasChanges = false; function onFileReaderError(evt) { - switch (loading.hide(), evt.target.error.code) { + + loading.hide(); + + switch (evt.target.error.code) { case evt.target.error.NOT_FOUND_ERR: - require(["toast"], function(toast) { - toast(globalize.translate("sharedcomponents#MessageFileReadError")) + require(['toast'], function (toast) { + toast(globalize.translate('sharedcomponents#MessageFileReadError')); }); break; case evt.target.error.ABORT_ERR: - break; + break; // noop default: - require(["toast"], function(toast) { - toast(globalize.translate("sharedcomponents#MessageFileReadError")) - }) + require(['toast'], function (toast) { + toast(globalize.translate('sharedcomponents#MessageFileReadError')); + }); + break; } } function setFiles(page, files) { + var file = files[0]; - if (!file || !file.type.match("image.*")) return page.querySelector("#imageOutput").innerHTML = "", page.querySelector("#fldUpload").classList.add("hide"), void(currentFile = null); + + if (!file || !file.type.match('image.*')) { + page.querySelector('#imageOutput').innerHTML = ''; + page.querySelector('#fldUpload').classList.add('hide'); + currentFile = null; + return; + } + currentFile = file; - var reader = new FileReader; - reader.onerror = onFileReaderError, reader.onloadstart = function() { - page.querySelector("#fldUpload").classList.add("hide") - }, reader.onabort = function() { - loading.hide(), console.log("File read cancelled") - }, reader.onload = function(theFile) { - return function(e) { - var html = [''].join(""); - page.querySelector("#imageOutput").innerHTML = html, page.querySelector("#fldUpload").classList.remove("hide") - } - }(file), reader.readAsDataURL(file) + + var reader = new FileReader(); + + reader.onerror = onFileReaderError; + reader.onloadstart = function () { + page.querySelector('#fldUpload').classList.add('hide'); + }; + reader.onabort = function () { + loading.hide(); + console.log('File read cancelled'); + }; + + // Closure to capture the file information. + reader.onload = (function (theFile) { + return function (e) { + + // Render thumbnail. + var html = [''].join(''); + + page.querySelector('#imageOutput').innerHTML = html; + page.querySelector('#fldUpload').classList.remove('hide'); + }; + })(file); + + // Read in the image file as a data URL. + reader.readAsDataURL(file); } function onSubmit(e) { + var file = currentFile; - if (!file) return !1; - if ("image/png" !== file.type && "image/jpeg" !== file.type && "image/jpeg" !== file.type) return !1; + + if (!file) { + return false; + } + + if (file.type !== "image/png" && file.type !== "image/x-png" && file.type !== "image/jpeg") { + require(['toast'], function (toast) { + toast(globalize.translate('sharedcomponents#MessageImageFileTypeAllowed')); + }); + e.preventDefault(); + return false; + } + loading.show(); - var dlg = dom.parentWithClass(this, "dialog"), - imageType = dlg.querySelector("#selectImageType").value; - return connectionManager.getApiClient(currentServerId).uploadItemImage(currentItemId, imageType, file).then(function() { - dlg.querySelector("#uploadImage").value = "", loading.hide(), hasChanges = !0, dialogHelper.close(dlg) - }), e.preventDefault(), !1 + + var dlg = dom.parentWithClass(this, 'dialog'); + + var imageType = dlg.querySelector('#selectImageType').value; + if (imageType === "None") { + require(["toast"], function(toast) { + toast(globalize.translate("sharedcomponents#MessageImageTypeNotSelected")); + }); + e.preventDefault(); + return false; + } + + connectionManager.getApiClient(currentServerId).uploadItemImage(currentItemId, imageType, file).then(function () { + + dlg.querySelector('#uploadImage').value = ''; + + loading.hide(); + hasChanges = true; + dialogHelper.close(dlg); + }); + + e.preventDefault(); + return false; } function initEditor(page) { - page.querySelector("form").addEventListener("submit", onSubmit), page.querySelector("#uploadImage").addEventListener("change", function() { - setFiles(page, this.files) - }), page.querySelector(".btnBrowse").addEventListener("click", function() { - page.querySelector("#uploadImage").click() - }) + + page.querySelector('form').addEventListener('submit', onSubmit); + + page.querySelector('#uploadImage').addEventListener("change", function () { + setFiles(page, this.files); + }); + + page.querySelector('.btnBrowse').addEventListener("click", function () { + page.querySelector('#uploadImage').click(); + }); } function showEditor(options, resolve, reject) { - options = options || {}, require(["text!./imageuploader.template.html"], function(template) { - currentItemId = options.itemId, currentServerId = options.serverId; + + options = options || {}; + + require(['text!./imageuploader.template.html'], function (template) { + + currentItemId = options.itemId; + currentServerId = options.serverId; + var dialogOptions = { - removeOnClose: !0 + removeOnClose: true }; - layoutManager.tv ? dialogOptions.size = "fullscreen" : dialogOptions.size = "fullscreen-border"; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'fullscreen-border'; + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"), dlg.innerHTML = globalize.translateDocument(template, "sharedcomponents"), layoutManager.tv && scrollHelper.centerFocus.on(dlg, !1), dlg.addEventListener("close", function() { - layoutManager.tv && scrollHelper.centerFocus.off(dlg, !1), loading.hide(), resolve(hasChanges) - }), dialogHelper.open(dlg), initEditor(dlg), dlg.querySelector("#selectImageType").value = options.imageType || "Primary", dlg.querySelector(".btnCancel").addEventListener("click", function() { - dialogHelper.close(dlg) - }) - }) + + dlg.classList.add('formDialog'); + + dlg.innerHTML = globalize.translateDocument(template, 'sharedcomponents'); + + if (layoutManager.tv) { + scrollHelper.centerFocus.on(dlg, false); + } + + // Has to be assigned a z-index after the call to .open() + dlg.addEventListener('close', function () { + + if (layoutManager.tv) { + scrollHelper.centerFocus.off(dlg, false); + } + + loading.hide(); + resolve(hasChanges); + }); + + dialogHelper.open(dlg); + + initEditor(dlg); + + dlg.querySelector('#selectImageType').value = options.imageType || 'Primary'; + + dlg.querySelector('.btnCancel').addEventListener('click', function () { + + dialogHelper.close(dlg); + }); + }); } - var currentItemId, currentServerId, currentFile, hasChanges = !1; + return { - show: function(options) { - return new Promise(function(resolve, reject) { - hasChanges = !1, showEditor(options, resolve, reject) - }) + show: function (options) { + + return new Promise(function (resolve, reject) { + + hasChanges = false; + + showEditor(options, resolve, reject); + }); } - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/imageuploader/imageuploader.template.html b/src/bower_components/emby-webcomponents/imageuploader/imageuploader.template.html index f47bfd4320..19edc80cc2 100644 --- a/src/bower_components/emby-webcomponents/imageuploader/imageuploader.template.html +++ b/src/bower_components/emby-webcomponents/imageuploader/imageuploader.template.html @@ -22,12 +22,13 @@
${LabelDropImageHere}
- +

', html += "
" + + var idLabel = globalize.translate('sharedcomponents#LabelDynamicExternalId').replace('{0}', idInfo.Name); + + var value = providerIds[idInfo.Key] || ''; + + html += ''; + + html += '
'; } - page.querySelector("#txtLookupName").value = "", "Person" === item.Type || "BoxSet" === item.Type ? (page.querySelector(".fldLookupYear").classList.add("hide"), page.querySelector("#txtLookupYear").value = "") : (page.querySelector(".fldLookupYear").classList.remove("hide"), page.querySelector("#txtLookupYear").value = ""), page.querySelector(".identifyProviderIds").innerHTML = html, page.querySelector(".formDialogHeaderTitle").innerHTML = globalize.translate("sharedcomponents#Identify") - }) + + page.querySelector('#txtLookupName').value = ''; + + if (item.Type === "Person" || item.Type === "BoxSet") { + + page.querySelector('.fldLookupYear').classList.add('hide'); + page.querySelector('#txtLookupYear').value = ''; + } else { + + page.querySelector('.fldLookupYear').classList.remove('hide'); + page.querySelector('#txtLookupYear').value = ''; + } + + page.querySelector('.identifyProviderIds').innerHTML = html; + + page.querySelector('.formDialogHeaderTitle').innerHTML = globalize.translate('sharedcomponents#Identify'); + }); } function showEditor(itemId) { - loading.show(), require(["text!./itemidentifier.template.html"], function(template) { + + loading.show(); + + require(['text!./itemidentifier.template.html'], function (template) { + var apiClient = getApiClient(); - apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function(item) { - currentItem = item, currentItemType = currentItem.Type; + + apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) { + + currentItem = item; + currentItemType = currentItem.Type; + var dialogOptions = { - size: "fullscreen-border", - removeOnClose: !0, - scrollY: !1 + size: 'fullscreen-border', + removeOnClose: true, + scrollY: false }; - layoutManager.tv && (dialogOptions.size = "fullscreen"); + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"), dlg.classList.add("recordingDialog"); - var html = ""; - html += globalize.translateDocument(template, "sharedcomponents"), dlg.innerHTML = html, dlg.addEventListener("close", onDialogClosed), layoutManager.tv && scrollHelper.centerFocus.on(dlg.querySelector(".formDialogContent"), !1), dialogHelper.open(dlg), dlg.querySelector(".popupIdentifyForm").addEventListener("submit", function(e) { - return e.preventDefault(), searchForIdentificationResults(dlg), !1 - }), dlg.querySelector(".identifyOptionsForm").addEventListener("submit", function(e) { - return e.preventDefault(), submitIdentficationResult(dlg), !1 - }), dlg.querySelector(".btnCancel").addEventListener("click", function(e) { - dialogHelper.close(dlg) - }), dlg.classList.add("identifyDialog"), showIdentificationForm(dlg, item), loading.hide() - }) - }) + + dlg.classList.add('formDialog'); + dlg.classList.add('recordingDialog'); + + var html = ''; + html += globalize.translateDocument(template, 'sharedcomponents'); + + dlg.innerHTML = html; + + // Has to be assigned a z-index after the call to .open() + dlg.addEventListener('close', onDialogClosed); + + if (layoutManager.tv) { + scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false); + } + + dialogHelper.open(dlg); + + dlg.querySelector('.popupIdentifyForm').addEventListener('submit', function (e) { + + e.preventDefault(); + searchForIdentificationResults(dlg); + return false; + }); + + dlg.querySelector('.identifyOptionsForm').addEventListener('submit', function (e) { + + e.preventDefault(); + submitIdentficationResult(dlg); + return false; + }); + + dlg.querySelector('.btnCancel').addEventListener('click', function (e) { + + dialogHelper.close(dlg); + }); + + dlg.classList.add('identifyDialog'); + + showIdentificationForm(dlg, item); + loading.hide(); + }); + }); } function onDialogClosed() { - loading.hide(), hasChanges ? currentResolve() : currentReject() + + loading.hide(); + if (hasChanges) { + currentResolve(); + } else { + currentReject(); + } } function showEditorFindNew(itemName, itemYear, itemType, resolveFunc) { - currentItem = null, currentItemType = itemType, require(["text!./itemidentifier.template.html"], function(template) { + + currentItem = null; + currentItemType = itemType; + + require(['text!./itemidentifier.template.html'], function (template) { + var dialogOptions = { - size: "fullscreen-border", - removeOnClose: !0, - scrollY: !1 + size: 'fullscreen-border', + removeOnClose: true, + scrollY: false }; - layoutManager.tv && (dialogOptions.size = "fullscreen"); + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"), dlg.classList.add("recordingDialog"); - var html = ""; - html += globalize.translateDocument(template, "sharedcomponents"), dlg.innerHTML = html, layoutManager.tv && scrollHelper.centerFocus.on(dlg.querySelector(".formDialogContent"), !1), dialogHelper.open(dlg), dlg.querySelector(".btnCancel").addEventListener("click", function(e) { - dialogHelper.close(dlg) - }), dlg.querySelector(".popupIdentifyForm").addEventListener("submit", function(e) { - return e.preventDefault(), searchForIdentificationResults(dlg), !1 - }), dlg.addEventListener("close", function() { - loading.hide(), resolveFunc(hasChanges ? currentSearchResult : null) - }), dlg.classList.add("identifyDialog"), showIdentificationFormFindNew(dlg, itemName, itemYear, itemType) - }) + + dlg.classList.add('formDialog'); + dlg.classList.add('recordingDialog'); + + var html = ''; + html += globalize.translateDocument(template, 'sharedcomponents'); + + dlg.innerHTML = html; + + if (layoutManager.tv) { + scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false); + } + + dialogHelper.open(dlg); + + dlg.querySelector('.btnCancel').addEventListener('click', function (e) { + + dialogHelper.close(dlg); + }); + + dlg.querySelector('.popupIdentifyForm').addEventListener('submit', function (e) { + + e.preventDefault(); + searchForIdentificationResults(dlg); + return false; + }); + + dlg.addEventListener('close', function () { + + loading.hide(); + var foundItem = hasChanges ? currentSearchResult : null; + + resolveFunc(foundItem); + }); + + dlg.classList.add('identifyDialog'); + + showIdentificationFormFindNew(dlg, itemName, itemYear, itemType); + }); } function showIdentificationFormFindNew(dlg, itemName, itemYear, itemType) { - dlg.querySelector("#txtLookupName").value = itemName, "Person" === itemType || "BoxSet" === itemType ? (dlg.querySelector(".fldLookupYear").classList.add("hide"), dlg.querySelector("#txtLookupYear").value = "") : (dlg.querySelector(".fldLookupYear").classList.remove("hide"), dlg.querySelector("#txtLookupYear").value = itemYear), dlg.querySelector(".formDialogHeaderTitle").innerHTML = globalize.translate("sharedcomponents#Search") - } - var currentItem, currentItemType, currentServerId, currentResolve, currentReject, currentSearchResult, hasChanges = !1; - return { - show: function(itemId, serverId) { - return new Promise(function(resolve, reject) { - currentResolve = resolve, currentReject = reject, currentServerId = serverId, hasChanges = !1, showEditor(itemId) - }) - }, - showFindNew: function(itemName, itemYear, itemType, serverId) { - return new Promise(function(resolve, reject) { - currentServerId = serverId, hasChanges = !1, showEditorFindNew(itemName, itemYear, itemType, resolve) - }) + + dlg.querySelector('#txtLookupName').value = itemName; + + if (itemType === "Person" || itemType === "BoxSet") { + + dlg.querySelector('.fldLookupYear').classList.add('hide'); + dlg.querySelector('#txtLookupYear').value = ''; + + } else { + + dlg.querySelector('.fldLookupYear').classList.remove('hide'); + dlg.querySelector('#txtLookupYear').value = itemYear; } + + dlg.querySelector('.formDialogHeaderTitle').innerHTML = globalize.translate('sharedcomponents#Search'); } + + return { + show: function (itemId, serverId) { + + return new Promise(function (resolve, reject) { + + currentResolve = resolve; + currentReject = reject; + currentServerId = serverId; + hasChanges = false; + + showEditor(itemId); + }); + }, + + showFindNew: function (itemName, itemYear, itemType, serverId) { + + return new Promise(function (resolve, reject) { + + currentServerId = serverId; + + hasChanges = false; + showEditorFindNew(itemName, itemYear, itemType, resolve); + }); + } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/itemsrefresher.js b/src/bower_components/emby-webcomponents/itemsrefresher.js index 43a7a3213d..3bdd61236d 100644 --- a/src/bower_components/emby-webcomponents/itemsrefresher.js +++ b/src/bower_components/emby-webcomponents/itemsrefresher.js @@ -1,112 +1,284 @@ -define(["playbackManager", "serverNotifications", "events"], function(playbackManager, serverNotifications, events) { - "use strict"; +define(['playbackManager', 'serverNotifications', 'events'], function (playbackManager, serverNotifications, events) { + 'use strict'; function onUserDataChanged(e, apiClient, userData) { - var instance = this, - eventsToMonitor = getEventsToMonitor(instance); - 1 !== eventsToMonitor.indexOf("markfavorite") ? instance.notifyRefreshNeeded() : -1 !== eventsToMonitor.indexOf("markplayed") && instance.notifyRefreshNeeded() + + var instance = this; + + var eventsToMonitor = getEventsToMonitor(instance); + + // TODO: Check user data change reason? + if (eventsToMonitor.indexOf('markfavorite') !== -1) { + + instance.notifyRefreshNeeded(); + } + else if (eventsToMonitor.indexOf('markplayed') !== -1) { + + instance.notifyRefreshNeeded(); + } } function getEventsToMonitor(instance) { - var options = instance.options, - monitor = options ? options.monitorEvents : null; - return monitor ? monitor.split(",") : [] + + var options = instance.options; + var monitor = options ? options.monitorEvents : null; + if (monitor) { + return monitor.split(','); + } + + return []; } function onTimerCreated(e, apiClient, data) { + var instance = this; - if (-1 !== getEventsToMonitor(instance).indexOf("timers")) return void instance.notifyRefreshNeeded() + + if (getEventsToMonitor(instance).indexOf('timers') !== -1) { + + instance.notifyRefreshNeeded(); + return; + } } function onSeriesTimerCreated(e, apiClient, data) { + var instance = this; - if (-1 !== getEventsToMonitor(instance).indexOf("seriestimers")) return void instance.notifyRefreshNeeded() + if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) { + + instance.notifyRefreshNeeded(); + return; + } } function onTimerCancelled(e, apiClient, data) { var instance = this; - if (-1 !== getEventsToMonitor(instance).indexOf("timers")) return void instance.notifyRefreshNeeded() + + if (getEventsToMonitor(instance).indexOf('timers') !== -1) { + + instance.notifyRefreshNeeded(); + return; + } } function onSeriesTimerCancelled(e, apiClient, data) { + var instance = this; - if (-1 !== getEventsToMonitor(instance).indexOf("seriestimers")) return void instance.notifyRefreshNeeded() + if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) { + + instance.notifyRefreshNeeded(); + return; + } } function onLibraryChanged(e, apiClient, data) { - var instance = this, - eventsToMonitor = getEventsToMonitor(instance); - if (-1 === eventsToMonitor.indexOf("seriestimers") && -1 === eventsToMonitor.indexOf("timers")) { - var itemsAdded = data.ItemsAdded || [], - itemsRemoved = data.ItemsRemoved || []; - if (itemsAdded.length || itemsRemoved.length) { - var options = instance.options || {}, - parentId = options.parentId; - if (parentId) { - var foldersAddedTo = data.FoldersAddedTo || [], - foldersRemovedFrom = data.FoldersRemovedFrom || [], - collectionFolders = data.CollectionFolders || []; - if (-1 === foldersAddedTo.indexOf(parentId) && -1 === foldersRemovedFrom.indexOf(parentId) && -1 === collectionFolders.indexOf(parentId)) return - } - instance.notifyRefreshNeeded() + + var instance = this; + var eventsToMonitor = getEventsToMonitor(instance); + if (eventsToMonitor.indexOf('seriestimers') !== -1 || eventsToMonitor.indexOf('timers') !== -1) { + + // yes this is an assumption + return; + } + + var itemsAdded = data.ItemsAdded || []; + var itemsRemoved = data.ItemsRemoved || []; + if (!itemsAdded.length && !itemsRemoved.length) { + return; + } + + var options = instance.options || {}; + var parentId = options.parentId; + if (parentId) { + var foldersAddedTo = data.FoldersAddedTo || []; + var foldersRemovedFrom = data.FoldersRemovedFrom || []; + var collectionFolders = data.CollectionFolders || []; + + if (foldersAddedTo.indexOf(parentId) === -1 && foldersRemovedFrom.indexOf(parentId) === -1 && collectionFolders.indexOf(parentId) === -1) { + return; + } + } + + instance.notifyRefreshNeeded(); + } + + function onPlaybackStopped(e, stopInfo) { + + var instance = this; + + var state = stopInfo.state; + + var eventsToMonitor = getEventsToMonitor(instance); + if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Video') { + + if (eventsToMonitor.indexOf('videoplayback') !== -1) { + + instance.notifyRefreshNeeded(true); + return; + } + } + + else if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Audio') { + + if (eventsToMonitor.indexOf('audioplayback') !== -1) { + + instance.notifyRefreshNeeded(true); + return; } } } - function onPlaybackStopped(e, stopInfo) { - var instance = this, - state = stopInfo.state, - eventsToMonitor = getEventsToMonitor(instance); - if (state.NowPlayingItem && "Video" === state.NowPlayingItem.MediaType) { - if (-1 !== eventsToMonitor.indexOf("videoplayback")) return void instance.notifyRefreshNeeded(!0) - } else if (state.NowPlayingItem && "Audio" === state.NowPlayingItem.MediaType && -1 !== eventsToMonitor.indexOf("audioplayback")) return void instance.notifyRefreshNeeded(!0) - } - function addNotificationEvent(instance, name, handler, owner) { + var localHandler = handler.bind(instance); - owner = owner || serverNotifications, events.on(owner, name, localHandler), instance["event_" + name] = localHandler + owner = owner || serverNotifications; + events.on(owner, name, localHandler); + instance['event_' + name] = localHandler; } function removeNotificationEvent(instance, name, owner) { - var handler = instance["event_" + name]; - handler && (owner = owner || serverNotifications, events.off(owner, name, handler), instance["event_" + name] = null) + + var handler = instance['event_' + name]; + if (handler) { + owner = owner || serverNotifications; + events.off(owner, name, handler); + instance['event_' + name] = null; + } } function ItemsRefresher(options) { - this.options = options || {}, addNotificationEvent(this, "UserDataChanged", onUserDataChanged), addNotificationEvent(this, "TimerCreated", onTimerCreated), addNotificationEvent(this, "SeriesTimerCreated", onSeriesTimerCreated), addNotificationEvent(this, "TimerCancelled", onTimerCancelled), addNotificationEvent(this, "SeriesTimerCancelled", onSeriesTimerCancelled), addNotificationEvent(this, "LibraryChanged", onLibraryChanged), addNotificationEvent(this, "playbackstop", onPlaybackStopped, playbackManager) + + this.options = options || {}; + + addNotificationEvent(this, 'UserDataChanged', onUserDataChanged); + addNotificationEvent(this, 'TimerCreated', onTimerCreated); + addNotificationEvent(this, 'SeriesTimerCreated', onSeriesTimerCreated); + addNotificationEvent(this, 'TimerCancelled', onTimerCancelled); + addNotificationEvent(this, 'SeriesTimerCancelled', onSeriesTimerCancelled); + addNotificationEvent(this, 'LibraryChanged', onLibraryChanged); + addNotificationEvent(this, 'playbackstop', onPlaybackStopped, playbackManager); } + ItemsRefresher.prototype.pause = function () { + + clearRefreshInterval(this, true); + + this.paused = true; + }; + + ItemsRefresher.prototype.resume = function (options) { + + this.paused = false; + + var refreshIntervalEndTime = this.refreshIntervalEndTime; + if (refreshIntervalEndTime) { + + var remainingMs = refreshIntervalEndTime - new Date().getTime(); + if (remainingMs > 0 && !this.needsRefresh) { + + resetRefreshInterval(this, remainingMs); + + } else { + this.needsRefresh = true; + this.refreshIntervalEndTime = null; + } + } + + if (this.needsRefresh || (options && options.refresh)) { + return this.refreshItems(); + } + + return Promise.resolve(); + }; + + ItemsRefresher.prototype.refreshItems = function () { + + if (!this.fetchData) { + return Promise.resolve(); + } + + if (this.paused) { + this.needsRefresh = true; + return Promise.resolve(); + } + + this.needsRefresh = false; + + return this.fetchData().then(onDataFetched.bind(this)); + }; + + ItemsRefresher.prototype.notifyRefreshNeeded = function (isInForeground) { + + if (this.paused) { + this.needsRefresh = true; + return; + } + + var timeout = this.refreshTimeout; + if (timeout) { + clearTimeout(timeout); + } + + if (isInForeground === true) { + this.refreshItems(); + } else { + this.refreshTimeout = setTimeout(this.refreshItems.bind(this), 10000); + } + }; + function clearRefreshInterval(instance, isPausing) { - instance.refreshInterval && (clearInterval(instance.refreshInterval), instance.refreshInterval = null, isPausing || (instance.refreshIntervalEndTime = null)) + + if (instance.refreshInterval) { + + clearInterval(instance.refreshInterval); + instance.refreshInterval = null; + + if (!isPausing) { + instance.refreshIntervalEndTime = null; + } + } } function resetRefreshInterval(instance, intervalMs) { - if (clearRefreshInterval(instance), !intervalMs) { + + clearRefreshInterval(instance); + + if (!intervalMs) { var options = instance.options; - options && (intervalMs = options.refreshIntervalMs) + if (options) { + intervalMs = options.refreshIntervalMs; + } + } + + if (intervalMs) { + instance.refreshInterval = setInterval(instance.notifyRefreshNeeded.bind(instance), intervalMs); + instance.refreshIntervalEndTime = new Date().getTime() + intervalMs; } - intervalMs && (instance.refreshInterval = setInterval(instance.notifyRefreshNeeded.bind(instance), intervalMs), instance.refreshIntervalEndTime = (new Date).getTime() + intervalMs) } function onDataFetched(result) { - resetRefreshInterval(this), this.afterRefresh && this.afterRefresh(result) - } - return ItemsRefresher.prototype.pause = function() { - clearRefreshInterval(this, !0), this.paused = !0 - }, ItemsRefresher.prototype.resume = function(options) { - this.paused = !1; - var refreshIntervalEndTime = this.refreshIntervalEndTime; - if (refreshIntervalEndTime) { - var remainingMs = refreshIntervalEndTime - (new Date).getTime(); - remainingMs > 0 && !this.needsRefresh ? resetRefreshInterval(this, remainingMs) : (this.needsRefresh = !0, this.refreshIntervalEndTime = null) + + resetRefreshInterval(this); + + if (this.afterRefresh) { + this.afterRefresh(result); } - return this.needsRefresh || options && options.refresh ? this.refreshItems() : Promise.resolve() - }, ItemsRefresher.prototype.refreshItems = function() { - return this.fetchData ? this.paused ? (this.needsRefresh = !0, Promise.resolve()) : (this.needsRefresh = !1, this.fetchData().then(onDataFetched.bind(this))) : Promise.resolve() - }, ItemsRefresher.prototype.notifyRefreshNeeded = function(isInForeground) { - if (this.paused) return void(this.needsRefresh = !0); - var timeout = this.refreshTimeout; - timeout && clearTimeout(timeout), !0 === isInForeground ? this.refreshItems() : this.refreshTimeout = setTimeout(this.refreshItems.bind(this), 1e4) - }, ItemsRefresher.prototype.destroy = function() { - clearRefreshInterval(this), removeNotificationEvent(this, "UserDataChanged"), removeNotificationEvent(this, "TimerCreated"), removeNotificationEvent(this, "SeriesTimerCreated"), removeNotificationEvent(this, "TimerCancelled"), removeNotificationEvent(this, "SeriesTimerCancelled"), removeNotificationEvent(this, "LibraryChanged"), removeNotificationEvent(this, "playbackstop", playbackManager), this.fetchData = null, this.options = null - }, ItemsRefresher + } + + ItemsRefresher.prototype.destroy = function () { + + clearRefreshInterval(this); + + removeNotificationEvent(this, 'UserDataChanged'); + removeNotificationEvent(this, 'TimerCreated'); + removeNotificationEvent(this, 'SeriesTimerCreated'); + removeNotificationEvent(this, 'TimerCancelled'); + removeNotificationEvent(this, 'SeriesTimerCancelled'); + removeNotificationEvent(this, 'LibraryChanged'); + removeNotificationEvent(this, 'playbackstop', playbackManager); + + this.fetchData = null; + this.options = null; + }; + + return ItemsRefresher; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/layoutmanager.js b/src/bower_components/emby-webcomponents/layoutmanager.js index ae33babaa6..1059bf5758 100644 --- a/src/bower_components/emby-webcomponents/layoutmanager.js +++ b/src/bower_components/emby-webcomponents/layoutmanager.js @@ -1,19 +1,67 @@ -define(["browser", "appSettings", "events"], function(browser, appSettings, events) { - "use strict"; +define(['browser', 'appSettings', 'events'], function (browser, appSettings, events) { + 'use strict'; function setLayout(instance, layout, selectedLayout) { - layout === selectedLayout ? (instance[layout] = !0, document.documentElement.classList.add("layout-" + layout)) : (instance[layout] = !1, document.documentElement.classList.remove("layout-" + layout)) + + if (layout === selectedLayout) { + instance[layout] = true; + document.documentElement.classList.add('layout-' + layout); + } else { + instance[layout] = false; + document.documentElement.classList.remove('layout-' + layout); + } } - function LayoutManager() {} - return LayoutManager.prototype.setLayout = function(layout, save) { - layout && "auto" !== layout ? (setLayout(this, "mobile", layout), setLayout(this, "tv", layout), setLayout(this, "desktop", layout), !1 !== save && appSettings.set("layout", layout)) : (this.autoLayout(), !1 !== save && appSettings.set("layout", "")), events.trigger(this, "modechange") - }, LayoutManager.prototype.getSavedLayout = function(layout) { - return appSettings.get("layout") - }, LayoutManager.prototype.autoLayout = function() { - browser.mobile ? this.setLayout("mobile", !1) : browser.tv || browser.xboxOne ? this.setLayout("tv", !1) : this.setLayout(this.defaultLayout || "tv", !1) - }, LayoutManager.prototype.init = function() { + function LayoutManager() { + + } + + LayoutManager.prototype.setLayout = function (layout, save) { + + if (!layout || layout === 'auto') { + this.autoLayout(); + + if (save !== false) { + appSettings.set('layout', ''); + } + } else { + setLayout(this, 'mobile', layout); + setLayout(this, 'tv', layout); + setLayout(this, 'desktop', layout); + + if (save !== false) { + appSettings.set('layout', layout); + } + } + + events.trigger(this, 'modechange'); + }; + + LayoutManager.prototype.getSavedLayout = function (layout) { + + return appSettings.get('layout'); + }; + + LayoutManager.prototype.autoLayout = function () { + + // Take a guess at initial layout. The consuming app can override + if (browser.mobile) { + this.setLayout('mobile', false); + } else if (browser.tv || browser.xboxOne) { + this.setLayout('tv', false); + } else { + this.setLayout(this.defaultLayout || 'tv', false); + } + }; + + LayoutManager.prototype.init = function () { var saved = this.getSavedLayout(); - saved ? this.setLayout(saved, !1) : this.autoLayout() - }, new LayoutManager + if (saved) { + this.setLayout(saved, false); + } else { + this.autoLayout(); + } + }; + + return new LayoutManager(); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/lazyloader/lazyedgehack.css b/src/bower_components/emby-webcomponents/lazyloader/lazyedgehack.css index ca435a074d..e0fea48c40 100644 --- a/src/bower_components/emby-webcomponents/lazyloader/lazyedgehack.css +++ b/src/bower_components/emby-webcomponents/lazyloader/lazyedgehack.css @@ -1,4 +1,5 @@ .lazy { + /* In edge, intersection observer will not fire on 0px sized elements */ min-width: .1em; - min-height: .1em -} \ No newline at end of file + min-height: .1em; +} diff --git a/src/bower_components/emby-webcomponents/lazyloader/lazyloader-intersectionobserver.js b/src/bower_components/emby-webcomponents/lazyloader/lazyloader-intersectionobserver.js index df4a2e12a2..261ca84261 100644 --- a/src/bower_components/emby-webcomponents/lazyloader/lazyloader-intersectionobserver.js +++ b/src/bower_components/emby-webcomponents/lazyloader/lazyloader-intersectionobserver.js @@ -1,45 +1,103 @@ -define(["require", "browser"], function(require, browser) { - "use strict"; +define(['require', 'browser'], function (require, browser) { + 'use strict'; function LazyLoader(options) { - this.options = options + + this.options = options; } - function unveilElements(elements, root, callback) { - if (elements.length) { - new LazyLoader({ - callback: callback - }).addElements(elements) - } + if (browser.edge) { + require(['css!./lazyedgehack']); } - return browser.edge && require(["css!./lazyedgehack"]), LazyLoader.prototype.createObserver = function() { - var observerOptions = {}, - options = this.options, - loadedCount = 0, - callback = options.callback; + + LazyLoader.prototype.createObserver = function () { + + var observerOptions = {}; + var options = this.options; + var loadedCount = 0; + var callback = options.callback; + observerOptions.rootMargin = "50%"; - var observerId = "obs" + (new Date).getTime(), - self = this, - observer = new IntersectionObserver(function(entries) { - for (var j = 0, length2 = entries.length; j < length2; j++) { - var entry = entries[j]; - if (entry.intersectionRatio > 0) { - var target = entry.target; - observer.unobserve(target), target[observerId] || (target[observerId] = 1, callback(target), ++loadedCount >= self.elementCount && self.destroyObserver()) + + var observerId = 'obs' + new Date().getTime(); + + var self = this; + var observer = new IntersectionObserver(function (entries) { + for (var j = 0, length2 = entries.length; j < length2; j++) { + var entry = entries[j]; + + if (entry.intersectionRatio > 0) { + + // Stop watching and load the image + var target = entry.target; + + observer.unobserve(target); + + if (!target[observerId]) { + target[observerId] = 1; + callback(target); + loadedCount++; + + if (loadedCount >= self.elementCount) { + self.destroyObserver(); + } } } - }, observerOptions); - this.observer = observer - }, LazyLoader.prototype.addElements = function(elements) { + } + }, + observerOptions + ); + + this.observer = observer; + }; + + LazyLoader.prototype.addElements = function (elements) { + var observer = this.observer; - observer || (this.createObserver(), observer = this.observer), this.elementCount = (this.elementCount || 0) + elements.length; - for (var i = 0, length = elements.length; i < length; i++) observer.observe(elements[i]) - }, LazyLoader.prototype.destroyObserver = function(elements) { + + if (!observer) { + this.createObserver(); + observer = this.observer; + } + + this.elementCount = (this.elementCount || 0) + elements.length; + + for (var i = 0, length = elements.length; i < length; i++) { + observer.observe(elements[i]); + } + }; + + LazyLoader.prototype.destroyObserver = function (elements) { + var observer = this.observer; - observer && (observer.disconnect(), this.observer = null) - }, LazyLoader.prototype.destroy = function(elements) { - this.destroyObserver(), this.options = null - }, LazyLoader.lazyChildren = function(elem, callback) { - unveilElements(elem.getElementsByClassName("lazy"), elem, callback) - }, LazyLoader + + if (observer) { + observer.disconnect(); + this.observer = null; + } + }; + + LazyLoader.prototype.destroy = function (elements) { + + this.destroyObserver(); + this.options = null; + }; + + function unveilElements(elements, root, callback) { + + if (!elements.length) { + return; + } + var lazyLoader = new LazyLoader({ + callback: callback + }); + lazyLoader.addElements(elements); + } + + LazyLoader.lazyChildren = function (elem, callback) { + + unveilElements(elem.getElementsByClassName('lazy'), elem, callback); + }; + + return LazyLoader; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/lazyloader/lazyloader-scroll.js b/src/bower_components/emby-webcomponents/lazyloader/lazyloader-scroll.js index bab5cff978..318f6dbea0 100644 --- a/src/bower_components/emby-webcomponents/lazyloader/lazyloader-scroll.js +++ b/src/bower_components/emby-webcomponents/lazyloader/lazyloader-scroll.js @@ -1,103 +1,189 @@ -define(["visibleinviewport", "dom", "browser"], function(visibleinviewport, dom, browser) { - "use strict"; +define(['visibleinviewport', 'dom', 'browser'], function (visibleinviewport, dom, browser) { + 'use strict'; + + var thresholdX; + var thresholdY; + + var requestIdleCallback = window.requestIdleCallback || function (fn) { + fn(); + }; function resetThresholds() { - thresholdX = .3 * screen.availWidth, thresholdY = .3 * screen.availHeight + + var threshold = 0.3; + + thresholdX = screen.availWidth * threshold; + thresholdY = screen.availHeight * threshold; } function resetThresholdsOnTimer() { - setTimeout(resetThresholds, 500) + + setTimeout(resetThresholds, 500); } + if (browser.iOS) { + dom.addEventListener(window, "orientationchange", resetThresholdsOnTimer, { passive: true }); + dom.addEventListener(window, 'resize', resetThresholdsOnTimer, { passive: true }); + } else { + dom.addEventListener(window, "orientationchange", resetThresholds, { passive: true }); + dom.addEventListener(window, 'resize', resetThresholds, { passive: true }); + } + resetThresholds(); + function isVisible(elem) { - return visibleinviewport(elem, !0, thresholdX, thresholdY) + return visibleinviewport(elem, true, thresholdX, thresholdY); } + var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel' : 'mousewheel'); + var self = {}; + function cancelAll(tokens) { - for (var i = 0, length = tokens.length; i < length; i++) tokens[i] = !0 + for (var i = 0, length = tokens.length; i < length; i++) { + + tokens[i] = true; + } } function unveilElementsInternal(instance, callback) { + + var unveiledElements = []; + var cancellationTokens = []; + var loadedCount = 0; + function unveilInternal(tokenIndex) { - for (var anyFound = !1, out = !1, elements = instance.elements, i = 0, length = elements.length; i < length; i++) { - if (cancellationTokens[tokenIndex]) return; - if (!unveiledElements[i]) { - var elem = elements[i]; - !out && isVisible(elem) ? (anyFound = !0, unveiledElements[i] = !0, callback(elem), loadedCount++) : anyFound && (out = !0) + + var anyFound = false; + var out = false; + + var elements = instance.elements; + // TODO: This out construct assumes left to right, top to bottom + + for (var i = 0, length = elements.length; i < length; i++) { + + if (cancellationTokens[tokenIndex]) { + return; + } + if (unveiledElements[i]) { + continue; + } + var elem = elements[i]; + if (!out && isVisible(elem)) { + anyFound = true; + unveiledElements[i] = true; + callback(elem); + loadedCount++; + } else { + + if (anyFound) { + out = true; + } } } - loadedCount >= elements.length && (dom.removeEventListener(document, "focus", unveil, { - capture: !0, - passive: !0 - }), dom.removeEventListener(document, "scroll", unveil, { - capture: !0, - passive: !0 - }), dom.removeEventListener(document, wheelEvent, unveil, { - capture: !0, - passive: !0 - }), dom.removeEventListener(window, "resize", unveil, { - capture: !0, - passive: !0 - })) + + if (loadedCount >= elements.length) { + dom.removeEventListener(document, 'focus', unveil, { + capture: true, + passive: true + }); + dom.removeEventListener(document, 'scroll', unveil, { + capture: true, + passive: true + }); + dom.removeEventListener(document, wheelEvent, unveil, { + capture: true, + passive: true + }); + dom.removeEventListener(window, 'resize', unveil, { + capture: true, + passive: true + }); + } } function unveil() { + cancelAll(cancellationTokens); + var index = cancellationTokens.length; - cancellationTokens.length++, setTimeout(function() { - unveilInternal(index) - }, 1) + cancellationTokens.length++; + + setTimeout(function () { + unveilInternal(index); + }, 1); } - var unveiledElements = [], - cancellationTokens = [], - loadedCount = 0; - dom.addEventListener(document, "focus", unveil, { - capture: !0, - passive: !0 - }), dom.addEventListener(document, "scroll", unveil, { - capture: !0, - passive: !0 - }), dom.addEventListener(document, wheelEvent, unveil, { - capture: !0, - passive: !0 - }), dom.addEventListener(window, "resize", unveil, { - capture: !0, - passive: !0 - }), unveil() + + dom.addEventListener(document, 'focus', unveil, { + capture: true, + passive: true + }); + dom.addEventListener(document, 'scroll', unveil, { + capture: true, + passive: true + }); + dom.addEventListener(document, wheelEvent, unveil, { + capture: true, + passive: true + }); + dom.addEventListener(window, 'resize', unveil, { + capture: true, + passive: true + }); + + unveil(); } function LazyLoader(options) { - this.options = options + + this.options = options; } - function unveilElements(elements, root, callback) { - if (elements.length) { - new LazyLoader({ - callback: callback - }).addElements(elements) - } - } - var thresholdX, thresholdY; - window.requestIdleCallback; - browser.iOS ? (dom.addEventListener(window, "orientationchange", resetThresholdsOnTimer, { - passive: !0 - }), dom.addEventListener(window, "resize", resetThresholdsOnTimer, { - passive: !0 - })) : (dom.addEventListener(window, "orientationchange", resetThresholds, { - passive: !0 - }), dom.addEventListener(window, "resize", resetThresholds, { - passive: !0 - })), resetThresholds(); - var wheelEvent = document.implementation.hasFeature("Event.wheel", "3.0") ? "wheel" : "mousewheel"; - return LazyLoader.prototype.createObserver = function() { - unveilElementsInternal(this, this.options.callback), this.observer = 1 - }, LazyLoader.prototype.addElements = function(elements) { + LazyLoader.prototype.createObserver = function () { + + unveilElementsInternal(this, this.options.callback); + this.observer = 1; + }; + + LazyLoader.prototype.addElements = function (elements) { + this.elements = this.elements || []; - for (var i = 0, length = elements.length; i < length; i++) this.elements.push(elements[i]); - this.observer || this.createObserver() - }, LazyLoader.prototype.destroyObserver = function(elements) {}, LazyLoader.prototype.destroy = function(elements) { - this.destroyObserver(), this.options = null - }, LazyLoader.lazyChildren = function(elem, callback) { - unveilElements(elem.getElementsByClassName("lazy"), elem, callback) - }, LazyLoader + + for (var i = 0, length = elements.length; i < length; i++) { + this.elements.push(elements[i]); + } + + var observer = this.observer; + + if (!observer) { + this.createObserver(); + } + + }; + + LazyLoader.prototype.destroyObserver = function (elements) { + + }; + + LazyLoader.prototype.destroy = function (elements) { + + this.destroyObserver(); + this.options = null; + }; + + function unveilElements(elements, root, callback) { + + if (!elements.length) { + return; + } + var lazyLoader = new LazyLoader({ + callback: callback + }); + lazyLoader.addElements(elements); + } + + LazyLoader.lazyChildren = function (elem, callback) { + + unveilElements(elem.getElementsByClassName('lazy'), elem, callback); + }; + + return LazyLoader; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/listview/listview.css b/src/bower_components/emby-webcomponents/listview/listview.css index ac9afc6e13..4b4e88f842 100644 --- a/src/bower_components/emby-webcomponents/listview/listview.css +++ b/src/bower_components/emby-webcomponents/listview/listview.css @@ -1,130 +1,97 @@ -.listItem-withContentWrapper, -.listItemBody { - -webkit-box-orient: vertical; - -webkit-box-direction: normal -} - -.listItemBody, -.listItemBodyText { - overflow: hidden; - -o-text-overflow: ellipsis; - text-overflow: ellipsis -} - .listItem { - background: 0 0; + background: transparent; border: 0; - outline: 0 !important; + outline: none !important; color: inherit; vertical-align: middle; font-family: inherit; font-size: inherit; margin: 0; - -webkit-box-align: center; - -webkit-align-items: center; + display: block; align-items: center; text-align: left; padding: .25em .25em .25em .5em; cursor: pointer; - overflow: hidden + overflow: hidden; } .listItem-withContentWrapper { - -webkit-flex-direction: column; flex-direction: column; - -webkit-box-align: start; - -webkit-align-items: flex-start; - align-items: flex-start + align-items: flex-start; } .listItem[data-action=none] { - cursor: default + cursor: default; } .listItem-content { - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - width: 100% -} - -.listItemBody, -.listItemButton, -.listItemIcon, -.listItemImage { - display: inline-block; - vertical-align: middle + width: 100%; } .listItem-button { - width: 100% + width: 100%; } .listItem-indexnumberleft { - margin-right: 1em + margin-right: 1em; } .listItem-border { border-bottom-width: .1em; - border-bottom-style: solid + border-bottom-style: solid; } -.listItemAside, -.listItemIcon, -.listItemImage { - -webkit-flex-shrink: 0; - flex-shrink: 0 +.listItemImage, .listItemIcon, .listItemAside { + flex-shrink: 0; +} + +.listItemBody, .listItemImage, .listItemIcon { + display: inline-block; + vertical-align: middle; } .listItemButton { margin: 0; - -webkit-flex-shrink: 0; + display: inline-block; + vertical-align: middle; flex-shrink: 0; - contain: layout style -} - -.listItemImage, -.listItemImageButton { - display: -webkit-box; - display: -webkit-flex + contain: layout style; } .listViewDragHandle { margin-left: -.25em !important; - touch-action: none + touch-action: none; } .listItemBody { - -webkit-box-flex: 1; - -webkit-flex-grow: 1; flex-grow: 1; padding: .85em .75em; - -webkit-flex-direction: column; + overflow: hidden; + text-overflow: ellipsis; flex-direction: column; vertical-align: middle; - -webkit-box-pack: center; - -webkit-justify-content: center; - justify-content: center + justify-content: center; } .layout-tv .listItemBody { - padding: .35em .75em + padding: .35em .75em; } .listItemBody-noleftpadding { - padding-left: 0 !important + padding-left: 0 !important; } .listItemBodyText { margin: 0; - padding: .1em 0 + padding: .1em 0; + overflow: hidden; + text-overflow: ellipsis; } .listItemBodyText-nowrap { - white-space: nowrap + white-space: nowrap; } .listItemImage { @@ -133,189 +100,164 @@ min-width: 2.78em; min-height: 2.78em; background-repeat: no-repeat; - -webkit-background-size: contain; background-size: contain; - -webkit-flex-shrink: 0; flex-shrink: 0; background-position: center center; position: relative; - display: flex + display: flex; } .listItemImage-large { width: 19.5vw; height: 13vw; background-position: center center; - margin-right: .75em + margin-right: .75em; } .listItemImageButton { - -webkit-align-self: center; align-self: center; justify-self: center; margin: auto; color: rgba(255, 255, 255, .6); font-size: 1.6em; - background: 0 0; - -webkit-transition: -webkit-transform .2s ease-out; - -o-transition: transform .2s ease-out; - transition: transform .2s ease-out; - display: flex + background: transparent; + transition: transform 200ms ease-out; + display: flex; } .listItemImageButton:hover { - -webkit-transform: scale(1.2, 1.2); - transform: scale(1.2, 1.2) + transform: scale(1.2, 1.2); } .listItemImageButton-icon { - background: rgba(0, 0, 0, .4); + background: rgba(0,0,0,.4); border: .08em solid currentColor; - -webkit-border-radius: 100em; border-radius: 100em; - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - padding: .21em + padding: .21em; } -@media all and (max-width:64em) { +@media all and (max-width: 64em) { + .listItemImage-large { width: 33.75vw; height: 22.5vw; - margin-right: 0 !important + margin-right: 0 !important; } .listItemImageButton { - font-size: 1.02em !important + font-size: 1.02em !important; } .listItemBody { - padding-left: .75em + padding-left: .75em; } } -@media all and (max-width:50em) { +@media all and (max-width: 50em) { + .listItemBody { - padding-right: .5em + padding-right: .5em; } } .listItemImage-large-tv { width: 30vw !important; - height: 20vw !important + height: 20vw !important; } .listItemIcon { width: 1em !important; height: 1em !important; font-size: 143%; - padding: 0 .25em 0 0 + padding: 0 .25em 0 0; } .listItemIcon:not(.listItemIcon-transparent) { background-color: #00a4dc; color: #fff; padding: .5em; - -webkit-border-radius: 100em; border-radius: 100em; - margin: 0 .2em 0 .4em + margin: 0 .2em 0 .4em; } .listItemProgressBar { position: absolute; bottom: 0; left: 0; - right: 0 + right: 0; } .listItem:focus { - -webkit-border-radius: .2em; - border-radius: .2em + border-radius: .2em; } .listItem:focus .secondary { - color: inherit !important + color: inherit !important; } .listItem-focusscale { - -webkit-transition: -webkit-transform .2s ease-out; - -o-transition: transform .2s ease-out; - transition: transform .2s ease-out + transition: transform .2s ease-out; } .listItem-focusscale:focus { - -webkit-transform: scale(1.025, 1.025); - transform: scale(1.025, 1.025) + transform: scale(1.025, 1.025); } .paperList { - margin: .5em auto + margin: .5em auto; } .paperList-clear { - background-color: transparent !important + background-color: transparent !important; } .listItemMediaInfo { - -webkit-box-align: center; - -webkit-align-items: center; + /* Don't display if flex not supported */ + display: none; align-items: center; - margin-right: 1em + margin-right: 1em; } .listGroupHeader-first { - margin-top: 0 + margin-top: 0; } .listItemIndicators { right: .324em; top: .324em; position: absolute; - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; - align-items: center + align-items: center; } -.listItem, -.listItemBody, -.listItemMediaInfo { - display: -webkit-box; - display: -webkit-flex; +.listItem, .listItemBody, .listItemMediaInfo { display: flex; - contain: layout style + contain: layout style; } .listItem-bottomoverview { font-size: 88%; margin-bottom: 1em; - margin-top: .2em + margin-top: .2em; } -@media all and (max-width:50em) { +@media all and (max-width: 50em) { - .listItem .criticRating, - .listItem .endsAt, - .listItem-overview { - display: none !important + .listItem .endsAt, .listItem .criticRating, .listItem-overview { + display: none !important; } } -@media all and (min-width:50em) { +@media all and (min-width: 50em) { + .listItem-bottomoverview { - display: none !important + display: none !important; } } .listItemCheckboxContainer { - width: auto !important + width: auto !important; } \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/listview/listview.js b/src/bower_components/emby-webcomponents/listview/listview.js index be183c9978..4f4a0a91eb 100644 --- a/src/bower_components/emby-webcomponents/listview/listview.js +++ b/src/bower_components/emby-webcomponents/listview/listview.js @@ -1,131 +1,543 @@ -define(["itemHelper", "mediaInfo", "indicators", "connectionManager", "layoutManager", "globalize", "datetime", "apphost", "css!./listview", "emby-ratingbutton", "emby-playstatebutton"], function(itemHelper, mediaInfo, indicators, connectionManager, layoutManager, globalize, datetime, appHost) { - "use strict"; +define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutManager', 'globalize', 'datetime', 'apphost', 'css!./listview', 'emby-ratingbutton', 'emby-playstatebutton'], function (itemHelper, mediaInfo, indicators, connectionManager, layoutManager, globalize, datetime, appHost) { + 'use strict'; function getIndex(item, options) { - if ("disc" === options.index) return null == item.ParentIndexNumber ? "" : globalize.translate("sharedcomponents#ValueDiscNumber", item.ParentIndexNumber); - var code, name, sortBy = (options.sortBy || "").toLowerCase(); - return 0 === sortBy.indexOf("sortname") ? "Episode" === item.Type ? "" : (name = (item.SortName || item.Name || "?")[0].toUpperCase(), code = name.charCodeAt(0), code < 65 || code > 90 ? "#" : name.toUpperCase()) : 0 === sortBy.indexOf("officialrating") ? item.OfficialRating || globalize.translate("sharedcomponents#Unrated") : 0 === sortBy.indexOf("communityrating") ? null == item.CommunityRating ? globalize.translate("sharedcomponents#Unrated") : Math.floor(item.CommunityRating) : 0 === sortBy.indexOf("criticrating") ? null == item.CriticRating ? globalize.translate("sharedcomponents#Unrated") : Math.floor(item.CriticRating) : 0 === sortBy.indexOf("albumartist") && item.AlbumArtist ? (name = item.AlbumArtist[0].toUpperCase(), code = name.charCodeAt(0), code < 65 || code > 90 ? "#" : name.toUpperCase()) : "" + + if (options.index === 'disc') { + + return item.ParentIndexNumber == null ? '' : globalize.translate('sharedcomponents#ValueDiscNumber', item.ParentIndexNumber); + } + + var sortBy = (options.sortBy || '').toLowerCase(); + var code, name; + + if (sortBy.indexOf('sortname') === 0) { + + if (item.Type === 'Episode') { + return ''; + } + + // SortName + name = (item.SortName || item.Name || '?')[0].toUpperCase(); + + code = name.charCodeAt(0); + if (code < 65 || code > 90) { + return '#'; + } + + return name.toUpperCase(); + } + if (sortBy.indexOf('officialrating') === 0) { + + return item.OfficialRating || globalize.translate('sharedcomponents#Unrated'); + } + if (sortBy.indexOf('communityrating') === 0) { + + if (item.CommunityRating == null) { + return globalize.translate('sharedcomponents#Unrated'); + } + + return Math.floor(item.CommunityRating); + } + if (sortBy.indexOf('criticrating') === 0) { + + if (item.CriticRating == null) { + return globalize.translate('sharedcomponents#Unrated'); + } + + return Math.floor(item.CriticRating); + } + if (sortBy.indexOf('albumartist') === 0) { + + // SortName + if (!item.AlbumArtist) { + return ''; + } + + name = item.AlbumArtist[0].toUpperCase(); + + code = name.charCodeAt(0); + if (code < 65 || code > 90) { + return '#'; + } + + return name.toUpperCase(); + } + return ''; } function getImageUrl(item, width) { - var apiClient = connectionManager.getApiClient(item.ServerId), - options = { - width: width, - type: "Primary" - }; - return item.ImageTags && item.ImageTags.Primary ? (options.tag = item.ImageTags.Primary, apiClient.getScaledImageUrl(item.Id, options)) : item.AlbumId && item.AlbumPrimaryImageTag ? (options.tag = item.AlbumPrimaryImageTag, apiClient.getScaledImageUrl(item.AlbumId, options)) : item.SeriesId && item.SeriesPrimaryImageTag ? (options.tag = item.SeriesPrimaryImageTag, apiClient.getScaledImageUrl(item.SeriesId, options)) : item.ParentPrimaryImageTag ? (options.tag = item.ParentPrimaryImageTag, apiClient.getScaledImageUrl(item.ParentPrimaryImageItemId, options)) : null + + var apiClient = connectionManager.getApiClient(item.ServerId); + + var options = { + width: width, + type: "Primary" + }; + + if (item.ImageTags && item.ImageTags.Primary) { + + options.tag = item.ImageTags.Primary; + return apiClient.getScaledImageUrl(item.Id, options); + } + + if (item.AlbumId && item.AlbumPrimaryImageTag) { + + options.tag = item.AlbumPrimaryImageTag; + return apiClient.getScaledImageUrl(item.AlbumId, options); + } + + else if (item.SeriesId && item.SeriesPrimaryImageTag) { + + options.tag = item.SeriesPrimaryImageTag; + return apiClient.getScaledImageUrl(item.SeriesId, options); + + } + else if (item.ParentPrimaryImageTag) { + + options.tag = item.ParentPrimaryImageTag; + return apiClient.getScaledImageUrl(item.ParentPrimaryImageItemId, options); + } + + return null; } function getChannelImageUrl(item, width) { - var apiClient = connectionManager.getApiClient(item.ServerId), - options = { - width: width, - type: "Primary" - }; - return item.ChannelId && item.ChannelPrimaryImageTag ? (options.tag = item.ChannelPrimaryImageTag, apiClient.getScaledImageUrl(item.ChannelId, options)) : null + + var apiClient = connectionManager.getApiClient(item.ServerId); + + var options = { + width: width, + type: "Primary" + }; + + if (item.ChannelId && item.ChannelPrimaryImageTag) { + + options.tag = item.ChannelPrimaryImageTag; + return apiClient.getScaledImageUrl(item.ChannelId, options); + } + + return null; } function getTextLinesHtml(textlines, isLargeStyle) { - for (var html = "", largeTitleTagName = layoutManager.tv ? "h2" : "div", i = 0, length = textlines.length; i < length; i++) { - textlines[i] && (html += 0 === i ? isLargeStyle ? "<" + largeTitleTagName + ' class="listItemBodyText">' : '
' : '
', html += textlines[i] || " ", html += 0 === i && isLargeStyle ? "" : "
") + + var html = ''; + + var largeTitleTagName = layoutManager.tv ? 'h2' : 'div'; + + for (var i = 0, length = textlines.length; i < length; i++) { + + var text = textlines[i]; + + if (!text) { + continue; + } + + if (i === 0) { + if (isLargeStyle) { + html += '<' + largeTitleTagName + ' class="listItemBodyText">'; + } else { + html += '
'; + } + } else { + html += '
'; + } + html += (textlines[i] || ' '); + if (i === 0 && isLargeStyle) { + html += ''; + } else { + html += '
'; + } } - return html + + return html; } function getRightButtonsHtml(options) { - for (var html = "", i = 0, length = options.rightButtons.length; i < length; i++) { + + var html = ''; + + for (var i = 0, length = options.rightButtons.length; i < length; i++) { + var button = options.rightButtons[i]; - html += '" + + html += ''; } - return html + + return html; } function getId(item) { - return item.Id + return item.Id; } function getListViewHtml(options) { - for (var items = options.items, groupTitle = "", action = options.action || "link", isLargeStyle = "large" === options.imageSize, enableOverview = options.enableOverview, clickEntireItem = !!layoutManager.tv, outerTagName = clickEntireItem ? "button" : "div", enableSideMediaInfo = null == options.enableSideMediaInfo || options.enableSideMediaInfo, outerHtml = "", enableContentWrapper = options.enableOverview && !layoutManager.tv, containerAlbumArtistIds = (options.containerAlbumArtists || []).map(getId), i = 0, length = items.length; i < length; i++) { - var item = items[i], - html = ""; + + var items = options.items; + + var groupTitle = ''; + var action = options.action || 'link'; + + var isLargeStyle = options.imageSize === 'large'; + var enableOverview = options.enableOverview; + + var clickEntireItem = layoutManager.tv ? true : false; + var outerTagName = clickEntireItem ? 'button' : 'div'; + var enableSideMediaInfo = options.enableSideMediaInfo != null ? options.enableSideMediaInfo : true; + + var outerHtml = ''; + + var enableContentWrapper = options.enableOverview && !layoutManager.tv; + var containerAlbumArtistIds = (options.containerAlbumArtists || []).map(getId); + + for (var i = 0, length = items.length; i < length; i++) { + + var item = items[i]; + + var html = ''; + if (options.showIndex) { + var itemGroupTitle = getIndex(item, options); - itemGroupTitle !== groupTitle && (html && (html += "
"), html += 0 === i ? '

' : '

', html += itemGroupTitle, html += "

", html += "
", groupTitle = itemGroupTitle) + + if (itemGroupTitle !== groupTitle) { + + if (html) { + html += '
'; + } + + if (i === 0) { + html += '

'; + } + else { + html += '

'; + } + html += itemGroupTitle; + html += '

'; + + html += '
'; + + groupTitle = itemGroupTitle; + } } + var cssClass = "listItem"; - (options.border || !1 !== options.highlight && !layoutManager.tv) && (cssClass += " listItem-border"), clickEntireItem && (cssClass += " itemAction listItem-button"), layoutManager.tv && (cssClass += " listItem-focusscale"); - var downloadWidth = 80; - isLargeStyle && (cssClass += " listItem-largeImage", downloadWidth = 500); - var playlistItemId = item.PlaylistItemId ? ' data-playlistitemid="' + item.PlaylistItemId + '"' : "", - positionTicksData = item.UserData && item.UserData.PlaybackPositionTicks ? ' data-positionticks="' + item.UserData.PlaybackPositionTicks + '"' : "", - collectionIdData = options.collectionId ? ' data-collectionid="' + options.collectionId + '"' : "", - playlistIdData = options.playlistId ? ' data-playlistid="' + options.playlistId + '"' : "", - mediaTypeData = item.MediaType ? ' data-mediatype="' + item.MediaType + '"' : "", - collectionTypeData = item.CollectionType ? ' data-collectiontype="' + item.CollectionType + '"' : "", - channelIdData = item.ChannelId ? ' data-channelid="' + item.ChannelId + '"' : ""; - if (enableContentWrapper && (cssClass += " listItem-withContentWrapper"), html += "<" + outerTagName + ' class="' + cssClass + '"' + playlistItemId + ' data-action="' + action + '" data-isfolder="' + item.IsFolder + '" data-id="' + item.Id + '" data-serverid="' + item.ServerId + '" data-type="' + item.Type + '"' + mediaTypeData + collectionTypeData + channelIdData + positionTicksData + collectionIdData + playlistIdData + ">", enableContentWrapper && (html += '
'), !clickEntireItem && options.dragHandle && (html += ''), !1 !== options.image) { - var imgUrl = "channel" === options.imageSource ? getChannelImageUrl(item, downloadWidth) : getImageUrl(item, downloadWidth); - console.log(imgUrl); - var imageClass = isLargeStyle ? "listItemImage listItemImage-large" : "listItemImage"; - isLargeStyle && layoutManager.tv && (imageClass += " listItemImage-large-tv"); - var playOnImageClick = options.imagePlayButton && !layoutManager.tv; - clickEntireItem || (imageClass += " itemAction"); - var imageAction = playOnImageClick ? "resume" : action; - html += imgUrl ? '
' : '
'; - var indicatorsHtml = ""; - indicatorsHtml += indicators.getPlayedIndicatorHtml(item), indicatorsHtml && (html += '
' + indicatorsHtml + "
"), playOnImageClick && (html += ''); - var progressHtml = indicators.getProgressBarHtml(item, { - containerClass: "listItemProgressBar" - }); - progressHtml && (html += progressHtml), html += "
" + + if (options.border || (options.highlight !== false && !layoutManager.tv)) { + cssClass += ' listItem-border'; } - options.showIndexNumberLeft && (html += '
', html += item.IndexNumber || " ", html += "
"); + + if (clickEntireItem) { + cssClass += ' itemAction listItem-button'; + } + + if (layoutManager.tv) { + cssClass += ' listItem-focusscale'; + } + + var downloadWidth = 80; + + if (isLargeStyle) { + cssClass += " listItem-largeImage"; + downloadWidth = 500; + } + + var playlistItemId = item.PlaylistItemId ? (' data-playlistitemid="' + item.PlaylistItemId + '"') : ''; + + var positionTicksData = item.UserData && item.UserData.PlaybackPositionTicks ? (' data-positionticks="' + item.UserData.PlaybackPositionTicks + '"') : ''; + var collectionIdData = options.collectionId ? (' data-collectionid="' + options.collectionId + '"') : ''; + var playlistIdData = options.playlistId ? (' data-playlistid="' + options.playlistId + '"') : ''; + var mediaTypeData = item.MediaType ? (' data-mediatype="' + item.MediaType + '"') : ''; + var collectionTypeData = item.CollectionType ? (' data-collectiontype="' + item.CollectionType + '"') : ''; + var channelIdData = item.ChannelId ? (' data-channelid="' + item.ChannelId + '"') : ''; + + if (enableContentWrapper) { + + cssClass += ' listItem-withContentWrapper'; + } + + html += '<' + outerTagName + ' class="' + cssClass + '"' + playlistItemId + ' data-action="' + action + '" data-isfolder="' + item.IsFolder + '" data-id="' + item.Id + '" data-serverid="' + item.ServerId + '" data-type="' + item.Type + '"' + mediaTypeData + collectionTypeData + channelIdData + positionTicksData + collectionIdData + playlistIdData + '>'; + + if (enableContentWrapper) { + + html += '
'; + } + + if (!clickEntireItem && options.dragHandle) { + //html += ''; + // Firefox and Edge are not allowing the button to be draggable + html += ''; + } + + if (options.image !== false) { + var imgUrl = options.imageSource === 'channel' ? getChannelImageUrl(item, downloadWidth) : getImageUrl(item, downloadWidth); + console.log(imgUrl); + var imageClass = isLargeStyle ? 'listItemImage listItemImage-large' : 'listItemImage'; + + if (isLargeStyle && layoutManager.tv) { + imageClass += ' listItemImage-large-tv'; + } + + var playOnImageClick = options.imagePlayButton && !layoutManager.tv; + + if (!clickEntireItem) { + imageClass += ' itemAction'; + } + + var imageAction = playOnImageClick ? 'resume' : action; + + if (imgUrl) { + html += '
'; + } else { + html += '
'; + } + + var indicatorsHtml = ''; + indicatorsHtml += indicators.getPlayedIndicatorHtml(item); + + if (indicatorsHtml) { + html += '
' + indicatorsHtml + '
'; + } + + if (playOnImageClick) { + html += ''; + } + + var progressHtml = indicators.getProgressBarHtml(item, { + containerClass: 'listItemProgressBar' + }); + + if (progressHtml) { + html += progressHtml; + } + html += '
'; + } + + if (options.showIndexNumberLeft) { + + html += '
'; + html += (item.IndexNumber || ' '); + html += '
'; + } + var textlines = []; - options.showProgramDateTime && textlines.push(datetime.toLocaleString(datetime.parseISO8601Date(item.StartDate), { - weekday: "long", - month: "short", - day: "numeric", - hour: "numeric", - minute: "2-digit" - })), options.showProgramTime && textlines.push(datetime.getDisplayTime(datetime.parseISO8601Date(item.StartDate))), options.showChannel && item.ChannelName && textlines.push(item.ChannelName); + + if (options.showProgramDateTime) { + textlines.push(datetime.toLocaleString(datetime.parseISO8601Date(item.StartDate), { + + weekday: 'long', + month: 'short', + day: 'numeric', + hour: 'numeric', + minute: '2-digit' + })); + } + + if (options.showProgramTime) { + textlines.push(datetime.getDisplayTime(datetime.parseISO8601Date(item.StartDate))); + } + + if (options.showChannel) { + if (item.ChannelName) { + textlines.push(item.ChannelName); + } + } + var parentTitle = null; - options.showParentTitle && ("Episode" === item.Type ? parentTitle = item.SeriesName : (item.IsSeries || item.EpisodeTitle && item.Name) && (parentTitle = item.Name)); + + if (options.showParentTitle) { + if (item.Type === 'Episode') { + parentTitle = item.SeriesName; + } + + else if (item.IsSeries || (item.EpisodeTitle && item.Name)) { + parentTitle = item.Name; + } + } + var displayName = itemHelper.getDisplayName(item, { includeParentInfo: options.includeParentInfoInTitle }); - if (options.showIndexNumber && null != item.IndexNumber && (displayName = item.IndexNumber + ". " + displayName), options.showParentTitle && options.parentTitleWithTitle ? (displayName && (parentTitle && (parentTitle += " - "), parentTitle = (parentTitle || "") + displayName), textlines.push(parentTitle || "")) : options.showParentTitle && textlines.push(parentTitle || ""), displayName && !options.parentTitleWithTitle && textlines.push(displayName), item.IsFolder) !1 !== options.artist && item.AlbumArtist && "MusicAlbum" === item.Type && textlines.push(item.AlbumArtist); - else { - var showArtist = !0 === options.artist, - artistItems = item.ArtistItems; - showArtist || !1 === options.artist || (artistItems && artistItems.length ? (artistItems.length > 1 || -1 === containerAlbumArtistIds.indexOf(artistItems[0].Id)) && (showArtist = !0) : showArtist = !0), showArtist && artistItems && "MusicAlbum" !== item.Type && textlines.push(artistItems.map(function(a) { - return a.Name - }).join(", ")) + + if (options.showIndexNumber && item.IndexNumber != null) { + displayName = item.IndexNumber + ". " + displayName; } - "Game" === item.Type && textlines.push(item.GameSystem), "TvChannel" === item.Type && item.CurrentProgram && textlines.push(itemHelper.getDisplayName(item.CurrentProgram)), cssClass = "listItemBody", clickEntireItem || (cssClass += " itemAction"), !1 === options.image && (cssClass += " listItemBody-noleftpadding"), html += '
'; - if (html += getTextLinesHtml(textlines, isLargeStyle), !1 !== options.mediaInfo && !enableSideMediaInfo) { - html += '
' + mediaInfo.getPrimaryMediaInfoHtml(item, { - episodeTitle: !1, - originalAirDate: !1, - subtitles: !1 - }) + "
" + + if (options.showParentTitle && options.parentTitleWithTitle) { + + if (displayName) { + + if (parentTitle) { + parentTitle += ' - '; + } + parentTitle = (parentTitle || '') + displayName; + } + + textlines.push(parentTitle || ''); } - if (enableOverview && item.Overview && (html += '
', html += item.Overview, html += "
"), html += "
", !1 !== options.mediaInfo && enableSideMediaInfo && (html += '
' + mediaInfo.getPrimaryMediaInfoHtml(item, { - year: !1, - container: !1, - episodeTitle: !1, - criticRating: !1, - endsAt: !1 - }) + "
"), options.recordButton || "Timer" !== item.Type && "Program" !== item.Type || (html += indicators.getTimerIndicator(item).replace("indicatorIcon", "indicatorIcon listItemAside")), !clickEntireItem && (options.addToListButton && (html += ''), !1 !== options.moreButton && (html += ''), options.infoButton && (html += ''), options.rightButtons && (html += getRightButtonsHtml(options)), !1 !== options.enableUserDataButtons)) { - html += ''; - var userData = item.UserData || {}, - likes = null == userData.Likes ? "" : userData.Likes; - itemHelper.canMarkPlayed(item) && (html += ''), itemHelper.canRate(item) && (html += ''), html += "" + else if (options.showParentTitle) { + textlines.push(parentTitle || ''); } - enableContentWrapper && (html += "
", enableOverview && item.Overview && (html += '
', html += item.Overview, html += "
")), html += "", outerHtml += html + + if (displayName && !options.parentTitleWithTitle) { + textlines.push(displayName); + } + + if (item.IsFolder) { + if (options.artist !== false) { + + if (item.AlbumArtist && item.Type === 'MusicAlbum') { + textlines.push(item.AlbumArtist); + } + } + } else { + + var showArtist = options.artist === true; + var artistItems = item.ArtistItems; + + if (!showArtist && options.artist !== false) { + + if (!artistItems || !artistItems.length) { + showArtist = true; + } + else if (artistItems.length > 1 || containerAlbumArtistIds.indexOf(artistItems[0].Id) === -1) { + showArtist = true; + } + } + + if (showArtist) { + + if (artistItems && item.Type !== 'MusicAlbum') { + textlines.push(artistItems.map(function (a) { + return a.Name; + }).join(', ')); + } + } + } + + if (item.Type === 'Game') { + textlines.push(item.GameSystem); + } + + if (item.Type === 'TvChannel') { + + if (item.CurrentProgram) { + textlines.push(itemHelper.getDisplayName(item.CurrentProgram)); + } + } + + cssClass = 'listItemBody'; + if (!clickEntireItem) { + cssClass += ' itemAction'; + } + + if (options.image === false) { + cssClass += ' listItemBody-noleftpadding'; + } + + html += '
'; + + var moreIcon = ''; + + html += getTextLinesHtml(textlines, isLargeStyle); + + if (options.mediaInfo !== false) { + if (!enableSideMediaInfo) { + + var mediaInfoClass = 'secondary listItemMediaInfo listItemBodyText'; + + html += '
' + mediaInfo.getPrimaryMediaInfoHtml(item, { + episodeTitle: false, + originalAirDate: false, + subtitles: false + + }) + '
'; + } + } + + if (enableOverview && item.Overview) { + html += '
'; + html += item.Overview; + html += '
'; + } + + html += '
'; + + if (options.mediaInfo !== false) { + if (enableSideMediaInfo) { + html += '
' + mediaInfo.getPrimaryMediaInfoHtml(item, { + + year: false, + container: false, + episodeTitle: false, + criticRating: false, + endsAt: false + + }) + '
'; + } + } + + if (!options.recordButton && (item.Type === 'Timer' || item.Type === 'Program')) { + html += indicators.getTimerIndicator(item).replace('indicatorIcon', 'indicatorIcon listItemAside'); + } + + if (!clickEntireItem) { + + if (options.addToListButton) { + html += ''; + } + + if (options.moreButton !== false) { + html += ''; + } + + if (options.infoButton) { + html += ''; + } + + if (options.rightButtons) { + html += getRightButtonsHtml(options); + } + + if (options.enableUserDataButtons !== false) { + + html += ''; + + var userData = item.UserData || {}; + var likes = userData.Likes == null ? '' : userData.Likes; + + if (itemHelper.canMarkPlayed(item)) { + html += ''; + } + + if (itemHelper.canRate(item)) { + html += ''; + } + + html += ''; + } + } + + if (enableContentWrapper) { + html += '
'; + + if (enableOverview && item.Overview) { + html += '
'; + html += item.Overview; + html += '
'; + } + } + + html += ''; + + outerHtml += html; } - return outerHtml + + return outerHtml; } + return { getListViewHtml: getListViewHtml - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/loading/loading-legacy.css b/src/bower_components/emby-webcomponents/loading/loading-legacy.css index af2627b988..17ea94950e 100644 --- a/src/bower_components/emby-webcomponents/loading/loading-legacy.css +++ b/src/bower_components/emby-webcomponents/loading/loading-legacy.css @@ -6,5 +6,5 @@ position: fixed; top: 50%; left: 50%; - z-index: 9999999 -} \ No newline at end of file + z-index: 9999999; +} diff --git a/src/bower_components/emby-webcomponents/loading/loading-legacy.js b/src/bower_components/emby-webcomponents/loading/loading-legacy.js index cd6620558f..bb8d8a99ab 100644 --- a/src/bower_components/emby-webcomponents/loading/loading-legacy.js +++ b/src/bower_components/emby-webcomponents/loading/loading-legacy.js @@ -1,14 +1,33 @@ -define(["require", "css!./loading-legacy"], function(require) { - "use strict"; +define(['require', 'css!./loading-legacy'], function (require) { + 'use strict'; + var loadingElem; + return { - show: function() { + show: function () { var elem = loadingElem; - elem || (elem = document.createElement("img"), elem.src = require.toUrl(".").split("?")[0] + "/loader2.gif", loadingElem = elem, elem.classList.add("loading-spinner"), document.body.appendChild(elem)), elem.classList.remove("hide") + + if (!elem) { + + elem = document.createElement("img"); + elem.src = require.toUrl('.').split('?')[0] + '/loader2.gif'; + + loadingElem = elem; + + elem.classList.add('loading-spinner'); + + document.body.appendChild(elem); + } + + elem.classList.remove('hide'); }, - hide: function() { + hide: function () { var elem = loadingElem; - elem && elem.classList.add("hide") + + if (elem) { + + elem.classList.add('hide'); + } } - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/loading/loading-lite.css b/src/bower_components/emby-webcomponents/loading/loading-lite.css index d70b2969a8..6d8472fc15 100644 --- a/src/bower_components/emby-webcomponents/loading/loading-lite.css +++ b/src/bower_components/emby-webcomponents/loading/loading-lite.css @@ -2,26 +2,26 @@ position: relative; width: 1.95em; height: 1.95em; - display: none + display: none; } .mdlSpinnerActive { display: inline-block; - -webkit-animation: mdl-spinner__container-rotate 1.568s linear infinite; - animation: mdl-spinner__container-rotate 1.568s linear infinite + -webkit-animation: mdl-spinner__container-rotate 1568.23529412ms linear infinite; + animation: mdl-spinner__container-rotate 1568.23529412ms linear infinite; } @-webkit-keyframes mdl-spinner__container-rotate { to { -webkit-transform: rotate(360deg); - transform: rotate(360deg) + transform: rotate(360deg); } } @keyframes mdl-spinner__container-rotate { to { -webkit-transform: rotate(360deg); - transform: rotate(360deg) + transform: rotate(360deg); } } @@ -29,240 +29,319 @@ position: absolute; width: 100%; height: 100%; - opacity: 0 + opacity: 0; } .mdl-spinner__layer-1 { - border-color: #42a5f5 + border-color: rgb(66,165,245); } .mdl-spinner__layer-1-active { - -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4, 0, .2, 1) infinite both, mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(.4, 0, .2, 1) infinite both; - animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4, 0, .2, 1) infinite both, mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(.4, 0, .2, 1) infinite both + -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; + animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } .mdl-spinner__layer-2 { - border-color: #f44336 + border-color: rgb(244,67,54); } .mdl-spinner__layer-2-active { - -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4, 0, .2, 1) infinite both, mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(.4, 0, .2, 1) infinite both; - animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4, 0, .2, 1) infinite both, mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(.4, 0, .2, 1) infinite both + -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; + animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } .mdl-spinner__layer-3 { - border-color: #fdd835 + border-color: rgb(253,216,53); } .mdl-spinner__layer-3-active { - -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4, 0, .2, 1) infinite both, mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(.4, 0, .2, 1) infinite both; - animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4, 0, .2, 1) infinite both, mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(.4, 0, .2, 1) infinite both + -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; + animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } .mdl-spinner__layer-4 { - border-color: #4caf50 + border-color: rgb(76,175,80); } .mdl-spinner__layer-4-active { - -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4, 0, .2, 1) infinite both, mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(.4, 0, .2, 1) infinite both; - animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4, 0, .2, 1) infinite both, mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(.4, 0, .2, 1) infinite both + -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; + animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } @-webkit-keyframes mdl-spinner__fill-unfill-rotate { 12.5% { -webkit-transform: rotate(135deg); - transform: rotate(135deg) + transform: rotate(135deg); } 25% { -webkit-transform: rotate(270deg); - transform: rotate(270deg) + transform: rotate(270deg); } 37.5% { -webkit-transform: rotate(405deg); - transform: rotate(405deg) + transform: rotate(405deg); } 50% { -webkit-transform: rotate(540deg); - transform: rotate(540deg) + transform: rotate(540deg); } 62.5% { -webkit-transform: rotate(675deg); - transform: rotate(675deg) + transform: rotate(675deg); } 75% { -webkit-transform: rotate(810deg); - transform: rotate(810deg) + transform: rotate(810deg); } 87.5% { -webkit-transform: rotate(945deg); - transform: rotate(945deg) + transform: rotate(945deg); } to { -webkit-transform: rotate(1080deg); - transform: rotate(1080deg) + transform: rotate(1080deg); } } @keyframes mdl-spinner__fill-unfill-rotate { 12.5% { -webkit-transform: rotate(135deg); - transform: rotate(135deg) + transform: rotate(135deg); } 25% { -webkit-transform: rotate(270deg); - transform: rotate(270deg) + transform: rotate(270deg); } 37.5% { -webkit-transform: rotate(405deg); - transform: rotate(405deg) + transform: rotate(405deg); } 50% { -webkit-transform: rotate(540deg); - transform: rotate(540deg) + transform: rotate(540deg); } 62.5% { -webkit-transform: rotate(675deg); - transform: rotate(675deg) + transform: rotate(675deg); } 75% { -webkit-transform: rotate(810deg); - transform: rotate(810deg) + transform: rotate(810deg); } 87.5% { -webkit-transform: rotate(945deg); - transform: rotate(945deg) + transform: rotate(945deg); } to { -webkit-transform: rotate(1080deg); - transform: rotate(1080deg) + transform: rotate(1080deg); } } +/** +* HACK: Even though the intention is to have the current .mdl-spinner__layer-N +* at `opacity: 1`, we set it to `opacity: 0.99` instead since this forces Chrome +* to do proper subpixel rendering for the elements being animated. This is +* especially visible in Chrome 39 on Ubuntu 14.04. See: +* +* - https://github.com/Polymer/paper-spinner/issues/9 +* - https://code.google.com/p/chromium/issues/detail?id=436255 +*/ @-webkit-keyframes mdl-spinner__layer-1-fade-in-out { - - 100%, - 25%, - 90%, from { - opacity: .99 + opacity: 0.99; + } + + 25% { + opacity: 0.99; + } + + 26% { + opacity: 0; } - 26%, 89% { - opacity: 0 + opacity: 0; + } + + 90% { + opacity: 0.99; + } + + 100% { + opacity: 0.99; } } @keyframes mdl-spinner__layer-1-fade-in-out { - - 100%, - 25%, - 90%, from { - opacity: .99 + opacity: 0.99; + } + + 25% { + opacity: 0.99; + } + + 26% { + opacity: 0; } - 26%, 89% { - opacity: 0 + opacity: 0; + } + + 90% { + opacity: 0.99; + } + + 100% { + opacity: 0.99; } } @-webkit-keyframes mdl-spinner__layer-2-fade-in-out { - - 15%, - 51%, from { - opacity: 0 + opacity: 0; + } + + 15% { + opacity: 0; + } + + 25% { + opacity: 0.99; } - 25%, 50% { - opacity: .99 + opacity: 0.99; + } + + 51% { + opacity: 0; } } @keyframes mdl-spinner__layer-2-fade-in-out { - - 15%, - 51%, from { - opacity: 0 + opacity: 0; + } + + 15% { + opacity: 0; + } + + 25% { + opacity: 0.99; } - 25%, 50% { - opacity: .99 + opacity: 0.99; + } + + 51% { + opacity: 0; } } @-webkit-keyframes mdl-spinner__layer-3-fade-in-out { - - 40%, - 76%, from { - opacity: 0 + opacity: 0; + } + + 40% { + opacity: 0; + } + + 50% { + opacity: 0.99; } - 50%, 75% { - opacity: .99 + opacity: 0.99; + } + + 76% { + opacity: 0; } } @keyframes mdl-spinner__layer-3-fade-in-out { - - 40%, - 76%, from { - opacity: 0 + opacity: 0; + } + + 40% { + opacity: 0; + } + + 50% { + opacity: 0.99; } - 50%, 75% { - opacity: .99 + opacity: 0.99; + } + + 76% { + opacity: 0; } } @-webkit-keyframes mdl-spinner__layer-4-fade-in-out { - - 100%, - 65%, from { - opacity: 0 + opacity: 0; + } + + 65% { + opacity: 0; + } + + 75% { + opacity: 0.99; } - 75%, 90% { - opacity: .99 + opacity: 0.99; + } + + 100% { + opacity: 0; } } @keyframes mdl-spinner__layer-4-fade-in-out { - - 100%, - 65%, from { - opacity: 0 + opacity: 0; + } + + 65% { + opacity: 0; + } + + 75% { + opacity: 0.99; } - 75%, 90% { - opacity: .99 + opacity: 0.99; + } + + 100% { + opacity: 0; } } @@ -272,22 +351,20 @@ width: 50%; height: 100%; overflow: hidden; - border-color: inherit + border-color: inherit; } -.mdl-spinner__circle-clipper .mdl-spinner__circle { - width: 200% -} + .mdl-spinner__circle-clipper .mdl-spinner__circle { + width: 200%; + } .mdl-spinner__circle { - -webkit-box-sizing: border-box; box-sizing: border-box; height: 100%; border-width: .21em; border-style: solid; border-color: inherit; border-bottom-color: transparent !important; - -webkit-border-radius: 50%; border-radius: 50%; -webkit-animation: none; animation: none; @@ -295,85 +372,97 @@ top: 0; right: 0; bottom: 0; - left: 0 + left: 0; } .mdl-spinner__circleLeft { border-right-color: transparent !important; -webkit-transform: rotate(129deg); - transform: rotate(129deg) + transform: rotate(129deg); } .mdl-spinner__circleLeft-active { - -webkit-animation: mdl-spinner__left-spin 1333ms cubic-bezier(.4, 0, .2, 1) infinite both; - animation: mdl-spinner__left-spin 1333ms cubic-bezier(.4, 0, .2, 1) infinite both + -webkit-animation: mdl-spinner__left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; + animation: mdl-spinner__left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } .mdl-spinner__circleRight { left: -100%; border-left-color: transparent !important; -webkit-transform: rotate(-129deg); - transform: rotate(-129deg) + transform: rotate(-129deg); } .mdl-spinner__circleRight-active { - -webkit-animation: mdl-spinner__right-spin 1333ms cubic-bezier(.4, 0, .2, 1) infinite both; - animation: mdl-spinner__right-spin 1333ms cubic-bezier(.4, 0, .2, 1) infinite both + -webkit-animation: mdl-spinner__right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; + animation: mdl-spinner__right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } @-webkit-keyframes mdl-spinner__left-spin { - - from, - to { + from { -webkit-transform: rotate(130deg); - transform: rotate(130deg) + transform: rotate(130deg); } 50% { -webkit-transform: rotate(-5deg); - transform: rotate(-5deg) + transform: rotate(-5deg); + } + + to { + -webkit-transform: rotate(130deg); + transform: rotate(130deg); } } @keyframes mdl-spinner__left-spin { - - from, - to { + from { -webkit-transform: rotate(130deg); - transform: rotate(130deg) + transform: rotate(130deg); } 50% { -webkit-transform: rotate(-5deg); - transform: rotate(-5deg) + transform: rotate(-5deg); + } + + to { + -webkit-transform: rotate(130deg); + transform: rotate(130deg); } } @-webkit-keyframes mdl-spinner__right-spin { - - from, - to { + from { -webkit-transform: rotate(-130deg); - transform: rotate(-130deg) + transform: rotate(-130deg); } 50% { -webkit-transform: rotate(5deg); - transform: rotate(5deg) + transform: rotate(5deg); + } + + to { + -webkit-transform: rotate(-130deg); + transform: rotate(-130deg); } } @keyframes mdl-spinner__right-spin { - - from, - to { + from { -webkit-transform: rotate(-130deg); - transform: rotate(-130deg) + transform: rotate(-130deg); } 50% { -webkit-transform: rotate(5deg); - transform: rotate(5deg) + transform: rotate(5deg); + } + + to { + -webkit-transform: rotate(-130deg); + transform: rotate(-130deg); } } @@ -386,5 +475,5 @@ top: 50%; left: 50%; z-index: 9999999; - contain: layout style size -} \ No newline at end of file + contain: layout style size; +} diff --git a/src/bower_components/emby-webcomponents/loading/loading-lite.js b/src/bower_components/emby-webcomponents/loading/loading-lite.js index ecdfb255d8..00aff8f0f8 100644 --- a/src/bower_components/emby-webcomponents/loading/loading-lite.js +++ b/src/bower_components/emby-webcomponents/loading/loading-lite.js @@ -1,22 +1,78 @@ -define(["css!./loading-lite"], function() { - "use strict"; - var loadingElem, layer1, layer2, layer3, layer4, circleLefts, circleRights; +define(['css!./loading-lite'], function () { + 'use strict'; + + var loadingElem; + var layer1; + var layer2; + var layer3; + var layer4; + var circleLefts; + var circleRights; + return { - show: function() { + show: function () { var elem = loadingElem; - elem || (elem = document.createElement("div"), loadingElem = elem, elem.classList.add("docspinner"), elem.classList.add("mdl-spinner"), elem.innerHTML = '
', document.body.appendChild(elem), layer1 = elem.querySelector(".mdl-spinner__layer-1"), layer2 = elem.querySelector(".mdl-spinner__layer-2"), layer3 = elem.querySelector(".mdl-spinner__layer-3"), layer4 = elem.querySelector(".mdl-spinner__layer-4"), circleLefts = elem.querySelectorAll(".mdl-spinner__circleLeft"), circleRights = elem.querySelectorAll(".mdl-spinner__circleRight")), elem.classList.add("mdlSpinnerActive"), layer1.classList.add("mdl-spinner__layer-1-active"), layer2.classList.add("mdl-spinner__layer-2-active"), layer3.classList.add("mdl-spinner__layer-3-active"), layer4.classList.add("mdl-spinner__layer-4-active"); + + if (!elem) { + + elem = document.createElement("div"); + loadingElem = elem; + + elem.classList.add('docspinner'); + elem.classList.add('mdl-spinner'); + + elem.innerHTML = '
'; + + document.body.appendChild(elem); + + layer1 = elem.querySelector('.mdl-spinner__layer-1'); + layer2 = elem.querySelector('.mdl-spinner__layer-2'); + layer3 = elem.querySelector('.mdl-spinner__layer-3'); + layer4 = elem.querySelector('.mdl-spinner__layer-4'); + + circleLefts = elem.querySelectorAll('.mdl-spinner__circleLeft'); + circleRights = elem.querySelectorAll('.mdl-spinner__circleRight'); + } + + elem.classList.add('mdlSpinnerActive'); + + layer1.classList.add('mdl-spinner__layer-1-active'); + layer2.classList.add('mdl-spinner__layer-2-active'); + layer3.classList.add('mdl-spinner__layer-3-active'); + layer4.classList.add('mdl-spinner__layer-4-active'); + var i, length; - for (i = 0, length = circleLefts.length; i < length; i++) circleLefts[i].classList.add("mdl-spinner__circleLeft-active"); - for (i = 0, length = circleRights.length; i < length; i++) circleRights[i].classList.add("mdl-spinner__circleRight-active") + + for (i = 0, length = circleLefts.length; i < length; i++) { + circleLefts[i].classList.add('mdl-spinner__circleLeft-active'); + } + + for (i = 0, length = circleRights.length; i < length; i++) { + circleRights[i].classList.add('mdl-spinner__circleRight-active'); + } }, - hide: function() { + hide: function () { var elem = loadingElem; + if (elem) { - elem.classList.remove("mdlSpinnerActive"), elem.classList.remove("mdl-spinner__layer-1-active"), elem.classList.remove("mdl-spinner__layer-2-active"), elem.classList.remove("mdl-spinner__layer-3-active"), elem.classList.remove("mdl-spinner__layer-4-active"); + + elem.classList.remove('mdlSpinnerActive'); + + elem.classList.remove('mdl-spinner__layer-1-active'); + elem.classList.remove('mdl-spinner__layer-2-active'); + elem.classList.remove('mdl-spinner__layer-3-active'); + elem.classList.remove('mdl-spinner__layer-4-active'); + var i, length; - for (i = 0, length = circleLefts.length; i < length; i++) circleLefts[i].classList.remove("mdl-spinner__circleLeft-active"); - for (i = 0, length = circleRights.length; i < length; i++) circleRights[i].classList.remove("mdl-spinner__circleRight-active") + + for (i = 0, length = circleLefts.length; i < length; i++) { + circleLefts[i].classList.remove('mdl-spinner__circleLeft-active'); + } + + for (i = 0, length = circleRights.length; i < length; i++) { + circleRights[i].classList.remove('mdl-spinner__circleRight-active'); + } } } - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/loadingdialog/loadingdialog.js b/src/bower_components/emby-webcomponents/loadingdialog/loadingdialog.js index 63694066e4..5f176399db 100644 --- a/src/bower_components/emby-webcomponents/loadingdialog/loadingdialog.js +++ b/src/bower_components/emby-webcomponents/loadingdialog/loadingdialog.js @@ -1,34 +1,101 @@ -define(["loading", "events", "dialogHelper", "dom", "layoutManager", "scrollHelper", "globalize", "require", "material-icons", "emby-button", "paper-icon-button-light", "emby-input", "formDialogStyle", "flexStyles"], function(loading, events, dialogHelper, dom, layoutManager, scrollHelper, globalize, require) { - "use strict"; +define(['loading', 'events', 'dialogHelper', 'dom', 'layoutManager', 'scrollHelper', 'globalize', 'require', 'material-icons', 'emby-button', 'paper-icon-button-light', 'emby-input', 'formDialogStyle', 'flexStyles'], function (loading, events, dialogHelper, dom, layoutManager, scrollHelper, globalize, require) { + 'use strict'; function showDialog(instance, options, template) { + var dialogOptions = { - removeOnClose: !0, - scrollY: !1 - }, - enableTvLayout = layoutManager.tv; - enableTvLayout && (dialogOptions.size = "fullscreen"); + removeOnClose: true, + scrollY: false + }; + + var enableTvLayout = layoutManager.tv; + + if (enableTvLayout) { + dialogOptions.size = 'fullscreen'; + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"), dlg.innerHTML = globalize.translateHtml(template, "sharedcomponents"), dlg.classList.add("align-items-center"), dlg.classList.add("justify-items-center"); - var formDialogContent = dlg.querySelector(".formDialogContent"); - return formDialogContent.style["flex-grow"] = "initial", formDialogContent.style["max-width"] = "50%", formDialogContent.style["max-height"] = "60%", enableTvLayout ? (scrollHelper.centerFocus.on(formDialogContent, !1), dlg.querySelector(".formDialogHeader").style.marginTop = "15%") : dlg.classList.add("dialog-fullscreen-lowres"), dlg.querySelector(".formDialogHeaderTitle").innerHTML = options.title, dlg.querySelector(".text").innerHTML = options.text, instance.dlg = dlg, dialogHelper.open(dlg).then(function() { - enableTvLayout && scrollHelper.centerFocus.off(dlg.querySelector(".formDialogContent"), !1), loading.hide() - }) + + var configuredButtons = []; + + dlg.classList.add('formDialog'); + + dlg.innerHTML = globalize.translateHtml(template, 'sharedcomponents'); + + dlg.classList.add('align-items-center'); + dlg.classList.add('justify-items-center'); + + var formDialogContent = dlg.querySelector('.formDialogContent'); + formDialogContent.style['flex-grow'] = 'initial'; + formDialogContent.style['max-width'] = '50%'; + formDialogContent.style['max-height'] = '60%'; + + if (enableTvLayout) { + scrollHelper.centerFocus.on(formDialogContent, false); + dlg.querySelector('.formDialogHeader').style.marginTop = '15%'; + } else { + dlg.classList.add('dialog-fullscreen-lowres'); + } + + //dlg.querySelector('.btnCancel').addEventListener('click', function (e) { + // dialogHelper.close(dlg); + //}); + + dlg.querySelector('.formDialogHeaderTitle').innerHTML = options.title; + + dlg.querySelector('.text').innerHTML = options.text; + + instance.dlg = dlg; + + return dialogHelper.open(dlg).then(function () { + + if (enableTvLayout) { + scrollHelper.centerFocus.off(dlg.querySelector('.formDialogContent'), false); + } + + loading.hide(); + }); } function LoadingDialog(options) { - this.options = options + + this.options = options; } - return LoadingDialog.prototype.show = function() { + + LoadingDialog.prototype.show = function () { + var instance = this; - return loading.show(), new Promise(function(resolve, reject) { - require(["text!./../dialog/dialog.template.html"], function(template) { - showDialog(instance, instance.options, template), resolve() - }) - }) - }, LoadingDialog.prototype.setTitle = function(title) {}, LoadingDialog.prototype.setText = function(text) {}, LoadingDialog.prototype.hide = function() { - this.dlg && (dialogHelper.close(this.dlg), this.dlg = null) - }, LoadingDialog.prototype.destroy = function() { - this.dlg = null, this.options = null - }, LoadingDialog + loading.show(); + + return new Promise(function (resolve, reject) { + require(['text!./../dialog/dialog.template.html'], function (template) { + showDialog(instance, instance.options, template); + resolve(); + }); + }); + }; + + LoadingDialog.prototype.setTitle = function (title) { + + }; + + LoadingDialog.prototype.setText = function (text) { + + }; + + LoadingDialog.prototype.hide = function () { + + if (this.dlg) { + dialogHelper.close(this.dlg); + this.dlg = null; + } + }; + + LoadingDialog.prototype.destroy = function () { + + this.dlg = null; + this.options = null; + }; + + return LoadingDialog; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/maintabsmanager.js b/src/bower_components/emby-webcomponents/maintabsmanager.js index 2a213f31eb..07f575c11c 100644 --- a/src/bower_components/emby-webcomponents/maintabsmanager.js +++ b/src/bower_components/emby-webcomponents/maintabsmanager.js @@ -1,97 +1,267 @@ -define(["dom", "browser", "events", "emby-tabs", "emby-button", "emby-linkbutton"], function(dom, browser, events) { - "use strict"; +define(['dom', 'browser', 'events', 'emby-tabs', 'emby-button', 'emby-linkbutton'], function (dom, browser, events) { + 'use strict'; + + var tabOwnerView; + var queryScope = document.querySelector('.skinHeader'); + var footerTabsContainer; + var headerTabsContainer; + var tabsElem; function enableTabsInFooter() { - return !1 + return false; + } + + function getTabsContainerElem() { } function ensureElements(enableInFooter) { - enableInFooter && (footerTabsContainer || (footerTabsContainer = document.createElement("div"), footerTabsContainer.classList.add("footerTabs"), footerTabsContainer.classList.add("sectionTabs"), footerTabsContainer.classList.add("hide"))), headerTabsContainer || (headerTabsContainer = queryScope.querySelector(".headerTabs")) + + if (enableInFooter) { + if (!footerTabsContainer) { + footerTabsContainer = document.createElement('div'); + footerTabsContainer.classList.add('footerTabs'); + footerTabsContainer.classList.add('sectionTabs'); + footerTabsContainer.classList.add('hide'); + //appFooter.add(footerTabsContainer); + } + } + + if (!headerTabsContainer) { + headerTabsContainer = queryScope.querySelector('.headerTabs'); + } } function onViewTabsReady() { - this.selectedIndex(this.readySelectedIndex), this.readySelectedIndex = null + this.selectedIndex(this.readySelectedIndex); + this.readySelectedIndex = null; } function allowSwipe(target) { - for (var parent = target; null != parent;) { - if (! function(elem) { - if (dom.parentWithTag(elem, "input")) return !1; - var classList = elem.classList; - return !classList || !classList.contains("scrollX") && !classList.contains("animatedScrollX") - }(parent)) return !1; - parent = parent.parentNode + + function allowSwipeOn(elem) { + + if (dom.parentWithTag(elem, 'input')) { + return false; + } + + var classList = elem.classList; + if (classList) { + return !classList.contains('scrollX') && !classList.contains('animatedScrollX'); + } + + return true; } - return !0 + + var parent = target; + while (parent != null) { + if (!allowSwipeOn(parent)) { + return false; + } + parent = parent.parentNode; + } + + return true; } function configureSwipeTabs(view, tabsElem, getTabContainersFn) { - if (browser.touch) { - var onSwipeLeft = (getTabContainersFn().length, function(e, target) { - allowSwipe(target) && view.contains(target) && tabsElem.selectNext() - }), - onSwipeRight = function(e, target) { - allowSwipe(target) && view.contains(target) && tabsElem.selectPrevious() - }; - require(["touchHelper"], function(TouchHelper) { - var touchHelper = new TouchHelper(view.parentNode.parentNode); - events.on(touchHelper, "swipeleft", onSwipeLeft), events.on(touchHelper, "swiperight", onSwipeRight), view.addEventListener("viewdestroy", function() { - touchHelper.destroy() - }) - }) + + if (!browser.touch) { + return; } + + // implement without hammer + var pageCount = getTabContainersFn().length; + var onSwipeLeft = function (e, target) { + if (allowSwipe(target) && view.contains(target)) { + tabsElem.selectNext(); + } + }; + + var onSwipeRight = function (e, target) { + if (allowSwipe(target) && view.contains(target)) { + tabsElem.selectPrevious(); + } + }; + + require(['touchHelper'], function (TouchHelper) { + + var touchHelper = new TouchHelper(view.parentNode.parentNode); + + events.on(touchHelper, 'swipeleft', onSwipeLeft); + events.on(touchHelper, 'swiperight', onSwipeRight); + + view.addEventListener('viewdestroy', function () { + touchHelper.destroy(); + }); + }); } function setTabs(view, selectedIndex, getTabsFn, getTabContainersFn, onBeforeTabChange, onTabChange, setSelectedIndex) { + var enableInFooter = enableTabsInFooter(); - if (!view) return tabOwnerView && (headerTabsContainer || (headerTabsContainer = queryScope.querySelector(".headerTabs")), ensureElements(enableInFooter), document.body.classList.remove("withSectionTabs"), headerTabsContainer.innerHTML = "", headerTabsContainer.classList.add("hide"), footerTabsContainer && (footerTabsContainer.innerHTML = "", footerTabsContainer.classList.add("hide")), tabOwnerView = null), { - tabsContainer: headerTabsContainer, - replaced: !1 - }; - ensureElements(enableInFooter); - var tabsContainerElem = enableInFooter ? footerTabsContainer : headerTabsContainer; - if (tabOwnerView || tabsContainerElem.classList.remove("hide"), tabOwnerView !== view) { - var index = 0, - indexAttribute = null == selectedIndex ? "" : ' data-index="' + selectedIndex + '"', - tabsHtml = '
' + getTabsFn().map(function(t) { - var tabClass = "emby-tab-button"; - !1 === t.enabled && (tabClass += " hide"); - var tabHtml; - return t.cssClass && (tabClass += " " + t.cssClass), tabHtml = t.href ? '
' + t.name + "
" : '", index++, tabHtml - }).join("") + "
"; - return tabsContainerElem.innerHTML = tabsHtml, document.body.classList.add("withSectionTabs"), tabOwnerView = view, tabsElem = tabsContainerElem.querySelector('[is="emby-tabs"]'), configureSwipeTabs(view, tabsElem, getTabContainersFn), tabsElem.addEventListener("beforetabchange", function(e) { - var tabContainers = getTabContainersFn(); - if (null != e.detail.previousIndex) { - var previousPanel = tabContainers[e.detail.previousIndex]; - previousPanel && previousPanel.classList.remove("is-active") + + if (!view) { + if (tabOwnerView) { + + if (!headerTabsContainer) { + headerTabsContainer = queryScope.querySelector('.headerTabs'); } + + ensureElements(enableInFooter); + + document.body.classList.remove('withSectionTabs'); + + headerTabsContainer.innerHTML = ''; + headerTabsContainer.classList.add('hide'); + + if (footerTabsContainer) { + footerTabsContainer.innerHTML = ''; + footerTabsContainer.classList.add('hide'); + } + + tabOwnerView = null; + } + return { + tabsContainer: headerTabsContainer, + replaced: false + }; + } + + ensureElements(enableInFooter); + + var tabsContainerElem = enableInFooter ? footerTabsContainer : headerTabsContainer; + + if (!tabOwnerView) { + tabsContainerElem.classList.remove('hide'); + } + + if (tabOwnerView !== view) { + + var index = 0; + + var indexAttribute = selectedIndex == null ? '' : (' data-index="' + selectedIndex + '"'); + var tabsHtml = '
' + getTabsFn().map(function (t) { + + var tabClass = 'emby-tab-button'; + + if (t.enabled === false) { + tabClass += ' hide'; + } + + var tabHtml; + + if (t.cssClass) { + tabClass += ' ' + t.cssClass; + } + + if (t.href) { + tabHtml = '
' + t.name + '
'; + } else { + tabHtml = ''; + } + + index++; + return tabHtml; + + }).join('') + '
'; + + tabsContainerElem.innerHTML = tabsHtml; + + document.body.classList.add('withSectionTabs'); + tabOwnerView = view; + + tabsElem = tabsContainerElem.querySelector('[is="emby-tabs"]'); + + configureSwipeTabs(view, tabsElem, getTabContainersFn); + + tabsElem.addEventListener('beforetabchange', function (e) { + + var tabContainers = getTabContainersFn(); + if (e.detail.previousIndex != null) { + + var previousPanel = tabContainers[e.detail.previousIndex]; + if (previousPanel) { + previousPanel.classList.remove('is-active'); + } + } + var newPanel = tabContainers[e.detail.selectedTabIndex]; - newPanel && newPanel.classList.add("is-active") - }), onBeforeTabChange && tabsElem.addEventListener("beforetabchange", onBeforeTabChange), onTabChange && tabsElem.addEventListener("tabchange", onTabChange), !1 !== setSelectedIndex && (tabsElem.selectedIndex ? tabsElem.selectedIndex(selectedIndex) : (tabsElem.readySelectedIndex = selectedIndex, tabsElem.addEventListener("ready", onViewTabsReady))), { + + //if (e.detail.previousIndex != null && e.detail.previousIndex != e.detail.selectedTabIndex) { + // if (newPanel.animate && (animateTabs || []).indexOf(e.detail.selectedTabIndex) != -1) { + // fadeInRight(newPanel); + // } + //} + + if (newPanel) { + newPanel.classList.add('is-active'); + } + }); + + if (onBeforeTabChange) { + tabsElem.addEventListener('beforetabchange', onBeforeTabChange); + } + if (onTabChange) { + tabsElem.addEventListener('tabchange', onTabChange); + } + + if (setSelectedIndex !== false) { + if (tabsElem.selectedIndex) { + tabsElem.selectedIndex(selectedIndex); + } else { + + tabsElem.readySelectedIndex = selectedIndex; + tabsElem.addEventListener('ready', onViewTabsReady); + } + } + + //if (enableSwipe !== false) { + // libraryBrowser.configureSwipeTabs(ownerpage, tabs); + //} + + return { tabsContainer: tabsContainerElem, tabs: tabsContainerElem.querySelector('[is="emby-tabs"]'), - replaced: !0 - } + replaced: true + }; } - return tabsElem || (tabsElem = tabsContainerElem.querySelector('[is="emby-tabs"]')), tabsElem.selectedIndex(selectedIndex), tabOwnerView = view, { + + if (!tabsElem) { + tabsElem = tabsContainerElem.querySelector('[is="emby-tabs"]'); + } + + tabsElem.selectedIndex(selectedIndex); + + tabOwnerView = view; + return { tabsContainer: tabsContainerElem, tabs: tabsElem, - replaced: !1 - } + replaced: false + }; } function selectedTabIndex(index) { + var tabsContainerElem = headerTabsContainer; - tabsElem || (tabsElem = tabsContainerElem.querySelector('[is="emby-tabs"]')), null != index ? tabsElem.selectedIndex(index) : tabsElem.triggerTabChange() + + if (!tabsElem) { + tabsElem = tabsContainerElem.querySelector('[is="emby-tabs"]'); + } + + if (index != null) { + tabsElem.selectedIndex(index); + } else { + tabsElem.triggerTabChange(); + } } function getTabsElement() { - return document.querySelector(".tabs-viewmenubar") + return document.querySelector('.tabs-viewmenubar'); } - var tabOwnerView, footerTabsContainer, headerTabsContainer, tabsElem, queryScope = document.querySelector(".skinHeader"); + return { setTabs: setTabs, getTabsElement: getTabsElement, selectedTabIndex: selectedTabIndex - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/mediainfo/mediainfo.css b/src/bower_components/emby-webcomponents/mediainfo/mediainfo.css index 0fdb8fc7f1..9b27e1253e 100644 --- a/src/bower_components/emby-webcomponents/mediainfo/mediainfo.css +++ b/src/bower_components/emby-webcomponents/mediainfo/mediainfo.css @@ -1,93 +1,78 @@ .mediaInfoItem { margin: 0 1em 0 0; - padding: 0 + padding: 0; } .mediaInfoText { padding: .22em .5em; - -webkit-border-radius: .25em; border-radius: .25em; font-size: 92%; - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; white-space: nowrap; - margin: 0 .5em 0 0 -} - -.mediaInfoCriticRating, -.starRatingContainer { - display: -webkit-box; - display: -webkit-flex; - -webkit-box-align: center + margin: 0 .5em 0 0; } .mediaInfoText-upper { - text-transform: uppercase + text-transform: uppercase; } .mediaInfoIconItem { width: auto; height: auto; font-size: 1.6em; - margin-right: .6em + margin-right: .6em; } .mediaInfoItem:last-child { - margin-right: 0 + margin-right: 0; } .starRatingContainer { display: flex; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; vertical-align: middle; padding-top: 0; - padding-bottom: 0 + padding-bottom: 0; } .starIcon { width: auto !important; height: auto !important; - font-size: 1.4em + font-size: 1.4em; } .mediaInfoCriticRating { padding-left: 1.5em; background-position: left center; background-repeat: no-repeat; - -webkit-background-size: auto 1.2em; background-size: auto 1.2em; min-height: 1.2em; display: flex; - -webkit-align-items: center; - align-items: center + align-items: center; } .mediaInfoCriticRatingFresh { - background-image: url(fresh.png) + background-image: url(fresh.png); } .mediaInfoCriticRatingRotten { - background-image: url(rotten.png) + background-image: url(rotten.png); } .mediaInfoProgramAttribute { text-transform: uppercase; padding: .16em .6em; - -webkit-border-radius: .15em; border-radius: .15em; - font-size: 80% + font-size: 80%; } .closedCaptionMediaInfoText { - font-weight: 700 + /*padding: .24em .4em;*/ + font-weight: bold; + /*font-size: inherit;*/ + /*background: rgba(81, 81, 81, .7);*/ } .mediaInfoOfficialRating { @@ -95,16 +80,9 @@ padding: 0 .6em; height: 1.3em; line-height: 1.8em; - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; - -webkit-border-radius: .1em; border-radius: .1em; - font-size: 96% -} \ No newline at end of file + font-size: 96%; +} diff --git a/src/bower_components/emby-webcomponents/mediainfo/mediainfo.js b/src/bower_components/emby-webcomponents/mediainfo/mediainfo.js index 14f71ed273..c0cccbcb83 100644 --- a/src/bower_components/emby-webcomponents/mediainfo/mediainfo.js +++ b/src/bower_components/emby-webcomponents/mediainfo/mediainfo.js @@ -1,248 +1,658 @@ -define(["datetime", "globalize", "appRouter", "itemHelper", "indicators", "material-icons", "css!./mediainfo.css", "programStyles", "emby-linkbutton"], function(datetime, globalize, appRouter, itemHelper, indicators) { - "use strict"; +define(['datetime', 'globalize', 'appRouter', 'itemHelper', 'indicators', 'material-icons', 'css!./mediainfo.css', 'programStyles', 'emby-linkbutton'], function (datetime, globalize, appRouter, itemHelper, indicators) { + 'use strict'; function getTimerIndicator(item) { + var status; - if ("SeriesTimer" === item.Type) return ''; - if (item.TimerId || item.SeriesTimerId) status = item.Status || "Cancelled"; - else { - if ("Timer" !== item.Type) return ""; - status = item.Status + + if (item.Type === 'SeriesTimer') { + return ''; } - return item.SeriesTimerId ? "Cancelled" !== status ? '' : '' : '' + else if (item.TimerId || item.SeriesTimerId) { + + status = item.Status || 'Cancelled'; + } + else if (item.Type === 'Timer') { + + status = item.Status; + } + else { + return ''; + } + + if (item.SeriesTimerId) { + + if (status !== 'Cancelled') { + return ''; + } + + return ''; + } + + return ''; } function getProgramInfoHtml(item, options) { - var text, date, html = "", - miscInfo = []; - if (item.StartDate && !1 !== options.programTime) try { - text = "", date = datetime.parseISO8601Date(item.StartDate), !1 !== options.startDate && (text += datetime.toLocaleDateString(date, { - weekday: "short", - month: "short", - day: "numeric" - })), text += " " + datetime.getDisplayTime(date), item.EndDate && (date = datetime.parseISO8601Date(item.EndDate), text += " - " + datetime.getDisplayTime(date)), miscInfo.push(text) - } catch (e) { - console.log("Error parsing date: " + item.StartDate) + var html = ''; + + var miscInfo = []; + var text, date; + + if (item.StartDate && options.programTime !== false) { + + try { + + text = ''; + + date = datetime.parseISO8601Date(item.StartDate); + + if (options.startDate !== false) { + text += datetime.toLocaleDateString(date, { weekday: 'short', month: 'short', day: 'numeric' }); + } + + text += ' ' + datetime.getDisplayTime(date); + + if (item.EndDate) { + date = datetime.parseISO8601Date(item.EndDate); + text += ' - ' + datetime.getDisplayTime(date); + } + + miscInfo.push(text); + } + catch (e) { + console.log("Error parsing date: " + item.StartDate); + } } - if (item.ChannelNumber && miscInfo.push("CH " + item.ChannelNumber), item.ChannelName && (options.interactive && item.ChannelId ? miscInfo.push({ - html: '' + item.ChannelName + "" - }) : miscInfo.push(item.ChannelName)), !1 !== options.timerIndicator) { + + if (item.ChannelNumber) { + miscInfo.push('CH ' + item.ChannelNumber); + } + + if (item.ChannelName) { + + if (options.interactive && item.ChannelId) { + miscInfo.push({ + html: '' + item.ChannelName + '' + }); + } else { + miscInfo.push(item.ChannelName); + } + } + + if (options.timerIndicator !== false) { var timerHtml = getTimerIndicator(item); - timerHtml && miscInfo.push({ - html: timerHtml - }) + if (timerHtml) { + miscInfo.push({ + html: timerHtml + }); + } } - return html += miscInfo.map(function(m) { - return getMediaInfoItem(m) - }).join("") + + html += miscInfo.map(function (m) { + return getMediaInfoItem(m); + }).join(''); + + return html; } function getMediaInfoHtml(item, options) { - var html = "", - miscInfo = []; + var html = ''; + + var miscInfo = []; options = options || {}; - var text, date, minutes, count, showFolderRuntime = "MusicAlbum" === item.Type || "MusicArtist" === item.MediaType || "Playlist" === item.MediaType || "MusicGenre" === item.MediaType; - if (showFolderRuntime ? (count = item.SongCount || item.ChildCount, count && miscInfo.push(globalize.translate("sharedcomponents#TrackCount", count)), item.RunTimeTicks && miscInfo.push(datetime.getDisplayRunningTime(item.RunTimeTicks))) : "PhotoAlbum" !== item.Type && "BoxSet" !== item.Type || (count = item.ChildCount) && miscInfo.push(globalize.translate("sharedcomponents#ItemCount", count)), ("Episode" === item.Type || "Photo" === item.MediaType) && !1 !== options.originalAirDate && item.PremiereDate) try { - date = datetime.parseISO8601Date(item.PremiereDate), text = datetime.toLocaleDateString(date), miscInfo.push(text) - } catch (e) { - console.log("Error parsing date: " + item.PremiereDate) - } - if ("SeriesTimer" === item.Type && (item.RecordAnyTime ? miscInfo.push(globalize.translate("sharedcomponents#Anytime")) : miscInfo.push(datetime.getDisplayTime(item.StartDate)), item.RecordAnyChannel ? miscInfo.push(globalize.translate("sharedcomponents#AllChannels")) : miscInfo.push(item.ChannelName || globalize.translate("sharedcomponents#OneChannel"))), item.StartDate && "Program" !== item.Type && "SeriesTimer" !== item.Type) try { - date = datetime.parseISO8601Date(item.StartDate), text = datetime.toLocaleDateString(date), miscInfo.push(text), "Recording" !== item.Type && (text = datetime.getDisplayTime(date), miscInfo.push(text)) - } catch (e) { - console.log("Error parsing date: " + item.StartDate) - } - if (!1 !== options.year && item.ProductionYear && "Series" === item.Type) - if ("Continuing" === item.Status) miscInfo.push(globalize.translate("sharedcomponents#SeriesYearToPresent", item.ProductionYear)); - else if (item.ProductionYear) { - if (text = item.ProductionYear, item.EndDate) try { - var endYear = datetime.parseISO8601Date(item.EndDate).getFullYear(); - endYear !== item.ProductionYear && (text += "-" + datetime.parseISO8601Date(item.EndDate).getFullYear()) - } catch (e) { - console.log("Error parsing date: " + item.EndDate) + var text, date, minutes; + var count; + + var showFolderRuntime = item.Type === "MusicAlbum" || item.MediaType === 'MusicArtist' || item.MediaType === 'Playlist' || item.MediaType === 'MusicGenre'; + + if (showFolderRuntime) { + + count = item.SongCount || item.ChildCount; + + if (count) { + + miscInfo.push(globalize.translate('sharedcomponents#TrackCount', count)); + } + + if (item.RunTimeTicks) { + miscInfo.push(datetime.getDisplayRunningTime(item.RunTimeTicks)); } - miscInfo.push(text) } - if ("Program" === item.Type) - if (!1 !== options.programIndicator && (item.IsLive ? miscInfo.push({ - html: '
' + globalize.translate("sharedcomponents#Live") + "
" - }) : item.IsPremiere ? miscInfo.push({ - html: '
' + globalize.translate("sharedcomponents#Premiere") + "
" - }) : item.IsSeries && !item.IsRepeat ? miscInfo.push({ - html: '
' + globalize.translate("sharedcomponents#AttributeNew") + "
" - }) : item.IsSeries && item.IsRepeat && miscInfo.push({ - html: '
' + globalize.translate("sharedcomponents#Repeat") + "
" - })), (item.IsSeries || item.EpisodeTitle) && !1 !== options.episodeTitle)(text = itemHelper.getDisplayName(item, { - includeIndexNumber: options.episodeTitleIndexNumber - })) && miscInfo.push(text); - else if (item.IsMovie && item.ProductionYear && !1 !== options.originalAirDate) miscInfo.push(item.ProductionYear); - else if (item.PremiereDate && !1 !== options.originalAirDate) try { - date = datetime.parseISO8601Date(item.PremiereDate), text = globalize.translate("sharedcomponents#OriginalAirDateValue", datetime.toLocaleDateString(date)), miscInfo.push(text) - } catch (e) { - console.log("Error parsing date: " + item.PremiereDate) - } else item.ProductionYear && miscInfo.push(item.ProductionYear); - if (!1 !== options.year && "Series" !== item.Type && "Episode" !== item.Type && "Person" !== item.Type && "Photo" !== item.MediaType && "Program" !== item.Type && "Season" !== item.Type) - if (item.ProductionYear) miscInfo.push(item.ProductionYear); - else if (item.PremiereDate) try { - text = datetime.parseISO8601Date(item.PremiereDate).getFullYear(), miscInfo.push(text) - } catch (e) { - console.log("Error parsing date: " + item.PremiereDate) + + else if (item.Type === "PhotoAlbum" || item.Type === "BoxSet") { + + count = item.ChildCount; + + if (count) { + + miscInfo.push(globalize.translate('sharedcomponents#ItemCount', count)); + } } - if (item.RunTimeTicks && "Series" !== item.Type && "Program" !== item.Type && !showFolderRuntime && !1 !== options.runtime && ("Audio" === item.Type ? miscInfo.push(datetime.getDisplayRunningTime(item.RunTimeTicks)) : (minutes = item.RunTimeTicks / 6e8, minutes = minutes || 1, miscInfo.push(Math.round(minutes) + " mins"))), item.OfficialRating && "Season" !== item.Type && "Episode" !== item.Type && miscInfo.push({ + + if ((item.Type === "Episode" || item.MediaType === 'Photo') && options.originalAirDate !== false) { + + if (item.PremiereDate) { + + try { + date = datetime.parseISO8601Date(item.PremiereDate); + + text = datetime.toLocaleDateString(date); + miscInfo.push(text); + } + catch (e) { + console.log("Error parsing date: " + item.PremiereDate); + } + } + } + + if (item.Type === 'SeriesTimer') { + if (item.RecordAnyTime) { + + miscInfo.push(globalize.translate('sharedcomponents#Anytime')); + } else { + miscInfo.push(datetime.getDisplayTime(item.StartDate)); + } + + if (item.RecordAnyChannel) { + miscInfo.push(globalize.translate('sharedcomponents#AllChannels')); + } + else { + miscInfo.push(item.ChannelName || globalize.translate('sharedcomponents#OneChannel')); + } + } + + if (item.StartDate && item.Type !== 'Program' && item.Type !== 'SeriesTimer') { + + try { + date = datetime.parseISO8601Date(item.StartDate); + + text = datetime.toLocaleDateString(date); + miscInfo.push(text); + + if (item.Type !== "Recording") { + text = datetime.getDisplayTime(date); + miscInfo.push(text); + } + } + catch (e) { + console.log("Error parsing date: " + item.StartDate); + } + } + + if (options.year !== false && item.ProductionYear && item.Type === "Series") { + + if (item.Status === "Continuing") { + miscInfo.push(globalize.translate('sharedcomponents#SeriesYearToPresent', item.ProductionYear)); + + } + else if (item.ProductionYear) { + + text = item.ProductionYear; + + if (item.EndDate) { + + try { + + var endYear = datetime.parseISO8601Date(item.EndDate).getFullYear(); + + if (endYear !== item.ProductionYear) { + text += "-" + datetime.parseISO8601Date(item.EndDate).getFullYear(); + } + + } + catch (e) { + console.log("Error parsing date: " + item.EndDate); + } + } + + miscInfo.push(text); + } + } + + if (item.Type === 'Program') { + + if (options.programIndicator !== false) { + if (item.IsLive) { + miscInfo.push({ + html: '
' + globalize.translate('sharedcomponents#Live') + '
' + }); + } + else if (item.IsPremiere) { + miscInfo.push({ + html: '
' + globalize.translate('sharedcomponents#Premiere') + '
' + }); + } + else if (item.IsSeries && !item.IsRepeat) { + miscInfo.push({ + html: '
' + globalize.translate('sharedcomponents#AttributeNew') + '
' + }); + } + else if (item.IsSeries && item.IsRepeat) { + miscInfo.push({ + html: '
' + globalize.translate('sharedcomponents#Repeat') + '
' + }); + } + } + + if ((item.IsSeries || item.EpisodeTitle) && options.episodeTitle !== false) { + + text = itemHelper.getDisplayName(item, { + includeIndexNumber: options.episodeTitleIndexNumber + }); + + if (text) { + miscInfo.push(text); + } + } + + else if (item.IsMovie && item.ProductionYear && options.originalAirDate !== false) { + miscInfo.push(item.ProductionYear); + } + + else if (item.PremiereDate && options.originalAirDate !== false) { + + try { + date = datetime.parseISO8601Date(item.PremiereDate); + text = globalize.translate('sharedcomponents#OriginalAirDateValue', datetime.toLocaleDateString(date)); + miscInfo.push(text); + } + catch (e) { + console.log("Error parsing date: " + item.PremiereDate); + } + } else if (item.ProductionYear) { + miscInfo.push(item.ProductionYear); + } + } + + if (options.year !== false) { + if (item.Type !== "Series" && item.Type !== "Episode" && item.Type !== "Person" && item.MediaType !== 'Photo' && item.Type !== 'Program' && item.Type !== 'Season') { + + if (item.ProductionYear) { + + miscInfo.push(item.ProductionYear); + } + else if (item.PremiereDate) { + + try { + text = datetime.parseISO8601Date(item.PremiereDate).getFullYear(); + miscInfo.push(text); + } + catch (e) { + console.log("Error parsing date: " + item.PremiereDate); + } + } + } + } + + if (item.RunTimeTicks && item.Type !== "Series" && item.Type !== 'Program' && !showFolderRuntime && options.runtime !== false) { + + if (item.Type === "Audio") { + + miscInfo.push(datetime.getDisplayRunningTime(item.RunTimeTicks)); + + } else { + minutes = item.RunTimeTicks / 600000000; + + minutes = minutes || 1; + + miscInfo.push(Math.round(minutes) + " mins"); + } + } + + if (item.OfficialRating && item.Type !== "Season" && item.Type !== "Episode") { + miscInfo.push({ text: item.OfficialRating, - cssClass: "mediaInfoOfficialRating" - }), item.Video3DFormat && miscInfo.push("3D"), "Photo" === item.MediaType && item.Width && item.Height && miscInfo.push(item.Width + "x" + item.Height), !1 !== options.container && "Audio" === item.Type && item.Container && miscInfo.push(item.Container), html += miscInfo.map(function(m) { - return getMediaInfoItem(m) - }).join(""), html += getStarIconsHtml(item), item.HasSubtitles && !1 !== options.subtitles && (html += '
CC
'), item.CriticRating && !1 !== options.criticRating && (item.CriticRating >= 60 ? html += '
' + item.CriticRating + "
" : html += '
' + item.CriticRating + "
"), !1 !== options.endsAt) { - var endsAt = getEndsAt(item); - endsAt && (html += getMediaInfoItem(endsAt, "endsAt")) + cssClass: 'mediaInfoOfficialRating' + }); } - return html += indicators.getMissingIndicator(item) + + if (item.Video3DFormat) { + miscInfo.push("3D"); + } + + if (item.MediaType === 'Photo' && item.Width && item.Height) { + miscInfo.push(item.Width + "x" + item.Height); + } + + if (options.container !== false && item.Type === 'Audio' && item.Container) { + miscInfo.push(item.Container); + } + + html += miscInfo.map(function (m) { + return getMediaInfoItem(m); + }).join(''); + + html += getStarIconsHtml(item); + + if (item.HasSubtitles && options.subtitles !== false) { + html += '
CC
'; + } + + if (item.CriticRating && options.criticRating !== false) { + + if (item.CriticRating >= 60) { + html += '
' + item.CriticRating + '
'; + } else { + html += '
' + item.CriticRating + '
'; + } + } + + if (options.endsAt !== false) { + + var endsAt = getEndsAt(item); + if (endsAt) { + html += getMediaInfoItem(endsAt, 'endsAt'); + } + } + + html += indicators.getMissingIndicator(item); + + return html; } function getEndsAt(item) { - if ("Video" === item.MediaType && item.RunTimeTicks && !item.StartDate) { - var endDate = (new Date).getTime() + item.RunTimeTicks / 1e4; - endDate = new Date(endDate); - var displayTime = datetime.getDisplayTime(endDate); - return globalize.translate("sharedcomponents#EndsAtValue", displayTime) + + if (item.MediaType === 'Video' && item.RunTimeTicks) { + + if (!item.StartDate) { + var endDate = new Date().getTime() + (item.RunTimeTicks / 10000); + endDate = new Date(endDate); + + var displayTime = datetime.getDisplayTime(endDate); + return globalize.translate('sharedcomponents#EndsAtValue', displayTime); + } } - return null + + return null; } function getEndsAtFromPosition(runtimeTicks, positionTicks, includeText) { - var endDate = (new Date).getTime() + (runtimeTicks - (positionTicks || 0)) / 1e4; + + var endDate = new Date().getTime() + ((runtimeTicks - (positionTicks || 0)) / 10000); endDate = new Date(endDate); + var displayTime = datetime.getDisplayTime(endDate); - return !1 === includeText ? displayTime : globalize.translate("sharedcomponents#EndsAtValue", displayTime) + + if (includeText === false) { + return displayTime; + } + return globalize.translate('sharedcomponents#EndsAtValue', displayTime); } function getMediaInfoItem(m, cssClass) { - cssClass = cssClass ? cssClass + " mediaInfoItem" : "mediaInfoItem"; + + cssClass = cssClass ? (cssClass + ' mediaInfoItem') : 'mediaInfoItem'; var mediaInfoText = m; - if ("string" != typeof m && "number" != typeof m) { - if (m.html) return m.html; - mediaInfoText = m.text, cssClass += " " + m.cssClass + + if (typeof (m) !== 'string' && typeof (m) !== 'number') { + + if (m.html) { + return m.html; + } + mediaInfoText = m.text; + cssClass += ' ' + m.cssClass; } - return '
' + mediaInfoText + "
" + return '
' + mediaInfoText + '
'; } function getStarIconsHtml(item) { - var html = "", - rating = item.CommunityRating; - return rating && (html += '
', html += '', html += rating, html += "
"), html + + var html = ''; + + var rating = item.CommunityRating; + + if (rating) { + html += '
'; + + html += ''; + html += rating; + html += '
'; + } + + return html; } function dynamicEndTime(elem, item) { - var interval = setInterval(function() { - if (!document.body.contains(elem)) return void clearInterval(interval); - elem.innerHTML = getEndsAt(item) - }, 6e4) + + var interval = setInterval(function () { + + if (!document.body.contains(elem)) { + + clearInterval(interval); + return; + } + + elem.innerHTML = getEndsAt(item); + + }, 60000); } function fillPrimaryMediaInfo(elem, item, options) { var html = getPrimaryMediaInfoHtml(item, options); - elem.innerHTML = html, afterFill(elem, item, options) + + elem.innerHTML = html; + afterFill(elem, item, options); } function fillSecondaryMediaInfo(elem, item, options) { var html = getSecondaryMediaInfoHtml(item, options); - elem.innerHTML = html, afterFill(elem, item, options) + + elem.innerHTML = html; + afterFill(elem, item, options); } function afterFill(elem, item, options) { - if (!1 !== options.endsAt) { - var endsAtElem = elem.querySelector(".endsAt"); - endsAtElem && dynamicEndTime(endsAtElem, item) + + if (options.endsAt !== false) { + var endsAtElem = elem.querySelector('.endsAt'); + if (endsAtElem) { + dynamicEndTime(endsAtElem, item); + } + } + + var lnkChannel = elem.querySelector('.lnkChannel'); + if (lnkChannel) { + lnkChannel.addEventListener('click', onChannelLinkClick); } - var lnkChannel = elem.querySelector(".lnkChannel"); - lnkChannel && lnkChannel.addEventListener("click", onChannelLinkClick) } function onChannelLinkClick(e) { - var channelId = this.getAttribute("data-id"), - serverId = this.getAttribute("data-serverid"); - return appRouter.showItem(channelId, serverId), e.preventDefault(), !1 + + var channelId = this.getAttribute('data-id'); + var serverId = this.getAttribute('data-serverid'); + + appRouter.showItem(channelId, serverId); + + e.preventDefault(); + return false; } function getPrimaryMediaInfoHtml(item, options) { - return options = options || {}, null == options.interactive && (options.interactive = !1), getMediaInfoHtml(item, options) + + options = options || {}; + if (options.interactive == null) { + options.interactive = false; + } + + return getMediaInfoHtml(item, options); } function getSecondaryMediaInfoHtml(item, options) { - return options = options || {}, null == options.interactive && (options.interactive = !1), "Program" === item.Type ? getProgramInfoHtml(item, options) : "" + + options = options || {}; + if (options.interactive == null) { + options.interactive = false; + } + if (item.Type === 'Program') { + return getProgramInfoHtml(item, options); + } + + return ''; } function getResolutionText(i) { - var width = i.Width, - height = i.Height; + + var width = i.Width; + var height = i.Height; + if (width && height) { - if (width >= 3800 || height >= 2e3) return "4K"; - if (width >= 2500 || height >= 1400) return i.IsInterlaced ? "1440i" : "1440P"; - if (width >= 1800 || height >= 1e3) return i.IsInterlaced ? "1080i" : "1080P"; - if (width >= 1200 || height >= 700) return i.IsInterlaced ? "720i" : "720P"; - if (width >= 700 || height >= 400) return i.IsInterlaced ? "480i" : "480P" + + if (width >= 3800 || height >= 2000) { + return '4K'; + } + if (width >= 2500 || height >= 1400) { + if (i.IsInterlaced) { + return '1440i'; + } + return '1440P'; + } + if (width >= 1800 || height >= 1000) { + if (i.IsInterlaced) { + return '1080i'; + } + return '1080P'; + } + if (width >= 1200 || height >= 700) { + if (i.IsInterlaced) { + return '720i'; + } + return '720P'; + } + if (width >= 700 || height >= 400) { + + if (i.IsInterlaced) { + return '480i'; + } + return '480P'; + } + } - return null + return null; } function getAudioStreamForDisplay(item) { - if (!item.MediaSources) return null; + + if (!item.MediaSources) { + return null; + } + var mediaSource = item.MediaSources[0]; - return mediaSource ? (mediaSource.MediaStreams || []).filter(function(i) { - return "Audio" === i.Type && (i.Index === mediaSource.DefaultAudioStreamIndex || null == mediaSource.DefaultAudioStreamIndex) - })[0] : null + if (!mediaSource) { + return null; + } + + return (mediaSource.MediaStreams || []).filter(function (i) { + return i.Type === 'Audio' && (i.Index === mediaSource.DefaultAudioStreamIndex || mediaSource.DefaultAudioStreamIndex == null); + })[0]; } function getMediaInfoStats(item, options) { + options = options || {}; - var list = [], - mediaSource = (item.MediaSources || [])[0] || {}, - videoStream = (mediaSource.MediaStreams || []).filter(function(i) { - return "Video" === i.Type - })[0] || {}, - audioStream = getAudioStreamForDisplay(item) || {}; - "Dvd" === item.VideoType && list.push({ - type: "mediainfo", - text: "Dvd" - }), "BluRay" === item.VideoType && list.push({ - type: "mediainfo", - text: "BluRay" - }); - var resolutionText = getResolutionText(videoStream); - resolutionText && list.push({ - type: "mediainfo", - text: resolutionText - }), videoStream.Codec && list.push({ - type: "mediainfo", - text: videoStream.Codec - }); - var channelText, channels = audioStream.Channels; - 8 === channels ? channelText = "7.1" : 7 === channels ? channelText = "6.1" : 6 === channels ? channelText = "5.1" : 2 === channels && (channelText = "2.0"), channelText && list.push({ - type: "mediainfo", - text: channelText - }); - var audioCodec = (audioStream.Codec || "").toLowerCase(); - if ("dca" !== audioCodec && "dts" !== audioCodec || !audioStream.Profile ? audioStream.Codec && list.push({ - type: "mediainfo", - text: audioStream.Codec - }) : list.push({ - type: "mediainfo", - text: audioStream.Profile - }), item.DateCreated && itemHelper.enableDateAddedDisplay(item)) { - var dateCreated = datetime.parseISO8601Date(item.DateCreated); + + var list = []; + + var mediaSource = (item.MediaSources || [])[0] || {}; + + var videoStream = (mediaSource.MediaStreams || []).filter(function (i) { + return i.Type === 'Video'; + })[0] || {}; + var audioStream = getAudioStreamForDisplay(item) || {}; + + if (item.VideoType === 'Dvd') { list.push({ - type: "added", - text: globalize.translate("sharedcomponents#AddedOnValue", datetime.toLocaleDateString(dateCreated) + " " + datetime.getDisplayTime(dateCreated)) - }) + type: 'mediainfo', + text: 'Dvd' + }); } - return list + + if (item.VideoType === 'BluRay') { + list.push({ + type: 'mediainfo', + text: 'BluRay' + }); + } + + //if (mediaSource.Container) { + // html += '
' + mediaSource.Container + '
'; + //} + + var resolutionText = getResolutionText(videoStream); + if (resolutionText) { + list.push({ + type: 'mediainfo', + text: resolutionText + }); + } + + if (videoStream.Codec) { + list.push({ + type: 'mediainfo', + text: videoStream.Codec + }); + } + + var channels = audioStream.Channels; + var channelText; + + if (channels === 8) { + + channelText = '7.1'; + + } else if (channels === 7) { + + channelText = '6.1'; + + } else if (channels === 6) { + + channelText = '5.1'; + + } else if (channels === 2) { + + channelText = '2.0'; + } + + if (channelText) { + list.push({ + type: 'mediainfo', + text: channelText + }); + } + + var audioCodec = (audioStream.Codec || '').toLowerCase(); + + if ((audioCodec === 'dca' || audioCodec === 'dts') && audioStream.Profile) { + list.push({ + type: 'mediainfo', + text: audioStream.Profile + }); + } else if (audioStream.Codec) { + list.push({ + type: 'mediainfo', + text: audioStream.Codec + }); + } + + if (item.DateCreated && itemHelper.enableDateAddedDisplay(item)) { + + var dateCreated = datetime.parseISO8601Date(item.DateCreated); + + list.push({ + type: 'added', + text: globalize.translate('sharedcomponents#AddedOnValue', datetime.toLocaleDateString(dateCreated) + ' ' + datetime.getDisplayTime(dateCreated)) + }); + } + + return list; } + return { getMediaInfoHtml: getPrimaryMediaInfoHtml, fill: fillPrimaryMediaInfo, @@ -254,5 +664,5 @@ define(["datetime", "globalize", "appRouter", "itemHelper", "indicators", "mater fillSecondaryMediaInfo: fillSecondaryMediaInfo, getMediaInfoStats: getMediaInfoStats, getResolutionText: getResolutionText - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/metadataeditor/metadataeditor.js b/src/bower_components/emby-webcomponents/metadataeditor/metadataeditor.js index abf15cc882..5ba7689ceb 100644 --- a/src/bower_components/emby-webcomponents/metadataeditor/metadataeditor.js +++ b/src/bower_components/emby-webcomponents/metadataeditor/metadataeditor.js @@ -1,482 +1,1137 @@ -define(["itemHelper", "dom", "layoutManager", "dialogHelper", "datetime", "loading", "focusManager", "connectionManager", "globalize", "require", "shell", "emby-checkbox", "emby-input", "emby-select", "listViewStyle", "emby-textarea", "emby-button", "paper-icon-button-light", "css!./../formdialog", "clearButtonStyle", "flexStyles"], function(itemHelper, dom, layoutManager, dialogHelper, datetime, loading, focusManager, connectionManager, globalize, require, shell) { - "use strict"; +define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loading', 'focusManager', 'connectionManager', 'globalize', 'require', 'shell', 'emby-checkbox', 'emby-input', 'emby-select', 'listViewStyle', 'emby-textarea', 'emby-button', 'paper-icon-button-light', 'css!./../formdialog', 'clearButtonStyle', 'flexStyles'], function (itemHelper, dom, layoutManager, dialogHelper, datetime, loading, focusManager, connectionManager, globalize, require, shell) { + 'use strict'; + + var currentContext; + var metadataEditorInfo; + var currentItem; function isDialog() { - return currentContext.classList.contains("dialog") + return currentContext.classList.contains('dialog'); } function closeDialog(isSubmitted) { - isDialog() && dialogHelper.close(currentContext) + + if (isDialog()) { + dialogHelper.close(currentContext); + } } function submitUpdatedItem(form, item) { + function afterContentTypeUpdated() { - require(["toast"], function(toast) { - toast(globalize.translate("sharedcomponents#MessageItemSaved")) - }), loading.hide(), closeDialog(!0) + + require(['toast'], function (toast) { + toast(globalize.translate('sharedcomponents#MessageItemSaved')); + }); + + loading.hide(); + closeDialog(true); } + var apiClient = getApiClient(); - apiClient.updateItem(item).then(function() { - var newContentType = form.querySelector("#selectContentType").value || ""; - (metadataEditorInfo.ContentType || "") !== newContentType ? apiClient.ajax({ - url: apiClient.getUrl("Items/" + item.Id + "/ContentType", { - ContentType: newContentType - }), - type: "POST" - }).then(function() { - afterContentTypeUpdated() - }) : afterContentTypeUpdated() - }) + + apiClient.updateItem(item).then(function () { + + var newContentType = form.querySelector('#selectContentType').value || ''; + + if ((metadataEditorInfo.ContentType || '') !== newContentType) { + + apiClient.ajax({ + + url: apiClient.getUrl('Items/' + item.Id + '/ContentType', { + ContentType: newContentType + }), + + type: 'POST' + + }).then(function () { + afterContentTypeUpdated(); + }); + + } else { + afterContentTypeUpdated(); + } + + }); } function getSelectedAirDays(form) { - var checkedItems = form.querySelectorAll(".chkAirDay:checked") || []; - return Array.prototype.map.call(checkedItems, function(c) { - return c.getAttribute("data-day") - }) + var checkedItems = form.querySelectorAll('.chkAirDay:checked') || []; + return Array.prototype.map.call(checkedItems, function (c) { + return c.getAttribute('data-day'); + }); } function getAlbumArtists(form) { - return form.querySelector("#txtAlbumArtist").value.trim().split(";").filter(function(s) { - return s.length > 0 - }).map(function(a) { + + return form.querySelector('#txtAlbumArtist').value.trim().split(';').filter(function (s) { + + return s.length > 0; + + }).map(function (a) { + return { Name: a - } - }) + }; + }); } function getArtists(form) { - return form.querySelector("#txtArtist").value.trim().split(";").filter(function(s) { - return s.length > 0 - }).map(function(a) { + + return form.querySelector('#txtArtist').value.trim().split(';').filter(function (s) { + + return s.length > 0; + + }).map(function (a) { + return { Name: a - } - }) + }; + }); } function getDateValue(form, element, property) { + var val = form.querySelector(element).value; - if (!val) return null; + + if (!val) { + return null; + } + if (currentItem[property]) { - var date = datetime.parseISO8601Date(currentItem[property], !0), - parts = date.toISOString().split("T"); - if (0 === parts[0].indexOf(val)) { - val += "T" + parts[1] + + var date = datetime.parseISO8601Date(currentItem[property], true); + + var parts = date.toISOString().split('T'); + + // If the date is the same, preserve the time + if (parts[0].indexOf(val) === 0) { + + var iso = parts[1]; + + val += 'T' + iso; } } - return val + + return val; } function onSubmit(e) { + loading.show(); - var form = this, - item = { - Id: currentItem.Id, - Name: form.querySelector("#txtName").value, - OriginalTitle: form.querySelector("#txtOriginalName").value, - ForcedSortName: form.querySelector("#txtSortName").value, - CommunityRating: form.querySelector("#txtCommunityRating").value, - CriticRating: form.querySelector("#txtCriticRating").value, - IndexNumber: form.querySelector("#txtIndexNumber").value || null, - AirsBeforeSeasonNumber: form.querySelector("#txtAirsBeforeSeason").value, - AirsAfterSeasonNumber: form.querySelector("#txtAirsAfterSeason").value, - AirsBeforeEpisodeNumber: form.querySelector("#txtAirsBeforeEpisode").value, - ParentIndexNumber: form.querySelector("#txtParentIndexNumber").value || null, - DisplayOrder: form.querySelector("#selectDisplayOrder").value, - Album: form.querySelector("#txtAlbum").value, - AlbumArtists: getAlbumArtists(form), - ArtistItems: getArtists(form), - Overview: form.querySelector("#txtOverview").value, - Status: form.querySelector("#selectStatus").value, - AirDays: getSelectedAirDays(form), - AirTime: form.querySelector("#txtAirTime").value, - Genres: getListValues(form.querySelector("#listGenres")), - Tags: getListValues(form.querySelector("#listTags")), - Studios: getListValues(form.querySelector("#listStudios")).map(function(element) { - return { - Name: element - } - }), - PremiereDate: getDateValue(form, "#txtPremiereDate", "PremiereDate"), - DateCreated: getDateValue(form, "#txtDateAdded", "DateCreated"), - EndDate: getDateValue(form, "#txtEndDate", "EndDate"), - ProductionYear: form.querySelector("#txtProductionYear").value, - AspectRatio: form.querySelector("#txtOriginalAspectRatio").value, - Video3DFormat: form.querySelector("#select3dFormat").value, - OfficialRating: form.querySelector("#selectOfficialRating").value, - CustomRating: form.querySelector("#selectCustomRating").value, - People: currentItem.People, - LockData: form.querySelector("#chkLockData").checked, - LockedFields: Array.prototype.filter.call(form.querySelectorAll(".selectLockedField"), function(c) { - return !c.checked - }).map(function(c) { - return c.getAttribute("data-value") - }) - }; + + var form = this; + + var item = { + Id: currentItem.Id, + Name: form.querySelector('#txtName').value, + OriginalTitle: form.querySelector('#txtOriginalName').value, + ForcedSortName: form.querySelector('#txtSortName').value, + CommunityRating: form.querySelector('#txtCommunityRating').value, + CriticRating: form.querySelector('#txtCriticRating').value, + IndexNumber: form.querySelector('#txtIndexNumber').value || null, + AirsBeforeSeasonNumber: form.querySelector('#txtAirsBeforeSeason').value, + AirsAfterSeasonNumber: form.querySelector('#txtAirsAfterSeason').value, + AirsBeforeEpisodeNumber: form.querySelector('#txtAirsBeforeEpisode').value, + ParentIndexNumber: form.querySelector('#txtParentIndexNumber').value || null, + DisplayOrder: form.querySelector('#selectDisplayOrder').value, + Album: form.querySelector('#txtAlbum').value, + AlbumArtists: getAlbumArtists(form), + ArtistItems: getArtists(form), + Overview: form.querySelector('#txtOverview').value, + Status: form.querySelector('#selectStatus').value, + AirDays: getSelectedAirDays(form), + AirTime: form.querySelector('#txtAirTime').value, + Genres: getListValues(form.querySelector("#listGenres")), + Tags: getListValues(form.querySelector("#listTags")), + Studios: getListValues(form.querySelector("#listStudios")).map(function (element) { return { Name: element }; }), + + PremiereDate: getDateValue(form, '#txtPremiereDate', 'PremiereDate'), + DateCreated: getDateValue(form, '#txtDateAdded', 'DateCreated'), + EndDate: getDateValue(form, '#txtEndDate', 'EndDate'), + ProductionYear: form.querySelector('#txtProductionYear').value, + AspectRatio: form.querySelector('#txtOriginalAspectRatio').value, + Video3DFormat: form.querySelector('#select3dFormat').value, + + OfficialRating: form.querySelector('#selectOfficialRating').value, + CustomRating: form.querySelector('#selectCustomRating').value, + People: currentItem.People, + LockData: form.querySelector("#chkLockData").checked, + LockedFields: Array.prototype.filter.call(form.querySelectorAll('.selectLockedField'), function (c) { + return !c.checked; + }).map(function (c) { + return c.getAttribute('data-value'); + }) + }; + item.ProviderIds = Object.assign({}, currentItem.ProviderIds); - var idElements = form.querySelectorAll(".txtExternalId"); - if (Array.prototype.map.call(idElements, function(idElem) { - var providerKey = idElem.getAttribute("data-providerkey"); - item.ProviderIds[providerKey] = idElem.value - }), item.PreferredMetadataLanguage = form.querySelector("#selectLanguage").value, item.PreferredMetadataCountryCode = form.querySelector("#selectCountry").value, "Person" === currentItem.Type) { - var placeOfBirth = form.querySelector("#txtPlaceOfBirth").value; - item.ProductionLocations = placeOfBirth ? [placeOfBirth] : [] + + var idElements = form.querySelectorAll('.txtExternalId'); + Array.prototype.map.call(idElements, function (idElem) { + var providerKey = idElem.getAttribute('data-providerkey'); + item.ProviderIds[providerKey] = idElem.value; + }); + + item.PreferredMetadataLanguage = form.querySelector('#selectLanguage').value; + item.PreferredMetadataCountryCode = form.querySelector('#selectCountry').value; + + if (currentItem.Type === "Person") { + + var placeOfBirth = form.querySelector('#txtPlaceOfBirth').value; + + item.ProductionLocations = placeOfBirth ? [placeOfBirth] : []; } - if ("Series" === currentItem.Type) { - var seriesRuntime = form.querySelector("#txtSeriesRuntime").value; - item.RunTimeTicks = seriesRuntime ? 6e8 * seriesRuntime : null + + if (currentItem.Type === "Series") { + + // 600000000 + var seriesRuntime = form.querySelector('#txtSeriesRuntime').value; + item.RunTimeTicks = seriesRuntime ? (seriesRuntime * 600000000) : null; } - var tagline = form.querySelector("#txtTagline").value; - return item.Taglines = tagline ? [tagline] : [], submitUpdatedItem(form, item), e.preventDefault(), e.stopPropagation(), !1 + + var tagline = form.querySelector('#txtTagline').value; + item.Taglines = tagline ? [tagline] : []; + + submitUpdatedItem(form, item); + + e.preventDefault(); + e.stopPropagation(); + + // Disable default form submission + return false; } function getListValues(list) { - return Array.prototype.map.call(list.querySelectorAll(".textValue"), function(el) { - return el.textContent - }) + return Array.prototype.map.call(list.querySelectorAll('.textValue'), function (el) { return el.textContent; }); } function addElementToList(source, sortCallback) { - require(["prompt"], function(prompt) { + require(['prompt'], function (prompt) { + prompt({ - label: "Value:" - }).then(function(text) { - var list = dom.parentWithClass(source, "editableListviewContainer").querySelector(".paperList"), - items = getListValues(list); - items.push(text), populateListView(list, items, sortCallback) - }) - }) + label: 'Value:' + }).then(function (text) { + var list = dom.parentWithClass(source, 'editableListviewContainer').querySelector('.paperList'); + var items = getListValues(list); + items.push(text); + populateListView(list, items, sortCallback); + }); + }); } function removeElementFromList(source) { - var el = dom.parentWithClass(source, "listItem"); - el.parentNode.removeChild(el) + var el = dom.parentWithClass(source, 'listItem'); + el.parentNode.removeChild(el); } function editPerson(context, person, index) { - require(["personEditor"], function(personEditor) { - personEditor.show(person).then(function(updatedPerson) { - -1 === index && currentItem.People.push(updatedPerson), populatePeople(context, currentItem.People) - }) - }) + + require(['personEditor'], function (personEditor) { + + personEditor.show(person).then(function (updatedPerson) { + + var isNew = index === -1; + + if (isNew) { + currentItem.People.push(updatedPerson); + } + + populatePeople(context, currentItem.People); + }); + }); } function showMoreMenu(context, button, user) { - require(["itemContextMenu"], function(itemContextMenu) { + + require(['itemContextMenu'], function (itemContextMenu) { + var item = currentItem; + itemContextMenu.show({ + item: item, positionTo: button, - edit: !1, - editImages: !0, - editSubtitles: !0, - sync: !1, - share: !1, - play: !1, - queue: !1, + edit: false, + editImages: true, + editSubtitles: true, + sync: false, + share: false, + play: false, + queue: false, user: user - }).then(function(result) { - result.deleted ? afterDeleted(context, item) : result.updated && reload(context, item.Id, item.ServerId) - }) - }) + + }).then(function (result) { + + if (result.deleted) { + afterDeleted(context, item); + + } else if (result.updated) { + reload(context, item.Id, item.ServerId); + } + }); + }); } function afterDeleted(context, item) { + var parentId = item.ParentId || item.SeasonId || item.SeriesId; - parentId ? reload(context, parentId, item.ServerId) : require(["appRouter"], function(appRouter) { - appRouter.goHome() - }) + + if (parentId) { + reload(context, parentId, item.ServerId); + } else { + require(['appRouter'], function (appRouter) { + appRouter.goHome(); + }); + } } function onEditorClick(e) { - var btnRemoveFromEditorList = dom.parentWithClass(e.target, "btnRemoveFromEditorList"); - if (btnRemoveFromEditorList) return void removeElementFromList(btnRemoveFromEditorList); - var btnAddTextItem = dom.parentWithClass(e.target, "btnAddTextItem"); - btnAddTextItem && addElementToList(btnAddTextItem) + + var btnRemoveFromEditorList = dom.parentWithClass(e.target, 'btnRemoveFromEditorList'); + if (btnRemoveFromEditorList) { + removeElementFromList(btnRemoveFromEditorList); + return; + } + + var btnAddTextItem = dom.parentWithClass(e.target, 'btnAddTextItem'); + if (btnAddTextItem) { + addElementToList(btnAddTextItem); + } } function getApiClient() { - return connectionManager.getApiClient(currentItem.ServerId) + return connectionManager.getApiClient(currentItem.ServerId); } function init(context, apiClient) { - context.querySelector(".externalIds").addEventListener("click", function(e) { - var btnOpenExternalId = dom.parentWithClass(e.target, "btnOpenExternalId"); + + context.querySelector('.externalIds').addEventListener('click', function (e) { + var btnOpenExternalId = dom.parentWithClass(e.target, 'btnOpenExternalId'); if (btnOpenExternalId) { - var field = context.querySelector("#" + btnOpenExternalId.getAttribute("data-fieldid")), - formatString = field.getAttribute("data-formatstring"); - field.value && shell.openUrl(formatString.replace("{0}", field.value)) + var field = context.querySelector('#' + btnOpenExternalId.getAttribute('data-fieldid')); + + var formatString = field.getAttribute('data-formatstring'); + + if (field.value) { + shell.openUrl(formatString.replace('{0}', field.value)); + } } - }), context.querySelector(".btnCancel").addEventListener("click", function() { - closeDialog(!1) - }), context.querySelector(".btnMore").addEventListener("click", function(e) { - getApiClient().getCurrentUser().then(function(user) { - showMoreMenu(context, e.target, user) - }) - }), context.querySelector(".btnHeaderSave").addEventListener("click", function(e) { - context.querySelector(".btnSave").click() - }), context.querySelector("#chkLockData").addEventListener("click", function(e) { - e.target.checked ? hideElement(".providerSettingsContainer") : showElement(".providerSettingsContainer") - }), context.removeEventListener("click", onEditorClick), context.addEventListener("click", onEditorClick); - var form = context.querySelector("form"); - form.removeEventListener("submit", onSubmit), form.addEventListener("submit", onSubmit), context.querySelector("#btnAddPerson").addEventListener("click", function(event, data) { - editPerson(context, {}, -1) - }), context.querySelector("#peopleList").addEventListener("click", function(e) { - var index, btnDeletePerson = dom.parentWithClass(e.target, "btnDeletePerson"); - btnDeletePerson && (index = parseInt(btnDeletePerson.getAttribute("data-index")), currentItem.People.splice(index, 1), populatePeople(context, currentItem.People)); - var btnEditPerson = dom.parentWithClass(e.target, "btnEditPerson"); - btnEditPerson && (index = parseInt(btnEditPerson.getAttribute("data-index")), editPerson(context, currentItem.People[index], index)) - }) + }); + + context.querySelector('.btnCancel').addEventListener('click', function () { + + closeDialog(false); + }); + + context.querySelector('.btnMore').addEventListener('click', function (e) { + + getApiClient().getCurrentUser().then(function (user) { + showMoreMenu(context, e.target, user); + }); + + }); + + context.querySelector('.btnHeaderSave').addEventListener('click', function (e) { + + context.querySelector('.btnSave').click(); + }); + + context.querySelector('#chkLockData').addEventListener('click', function (e) { + + if (!e.target.checked) { + showElement('.providerSettingsContainer'); + } else { + hideElement('.providerSettingsContainer'); + } + }); + + context.removeEventListener('click', onEditorClick); + context.addEventListener('click', onEditorClick); + + var form = context.querySelector('form'); + form.removeEventListener('submit', onSubmit); + form.addEventListener('submit', onSubmit); + + context.querySelector("#btnAddPerson").addEventListener('click', function (event, data) { + + editPerson(context, {}, -1); + }); + + context.querySelector('#peopleList').addEventListener('click', function (e) { + + var index; + var btnDeletePerson = dom.parentWithClass(e.target, 'btnDeletePerson'); + if (btnDeletePerson) { + index = parseInt(btnDeletePerson.getAttribute('data-index')); + currentItem.People.splice(index, 1); + populatePeople(context, currentItem.People); + } + + var btnEditPerson = dom.parentWithClass(e.target, 'btnEditPerson'); + if (btnEditPerson) { + index = parseInt(btnEditPerson.getAttribute('data-index')); + editPerson(context, currentItem.People[index], index); + } + }); } function getItem(itemId, serverId) { + var apiClient = connectionManager.getApiClient(serverId); - return itemId ? apiClient.getItem(apiClient.getCurrentUserId(), itemId) : apiClient.getRootFolder(apiClient.getCurrentUserId()) + + if (itemId) { + return apiClient.getItem(apiClient.getCurrentUserId(), itemId); + } + + return apiClient.getRootFolder(apiClient.getCurrentUserId()); } function getEditorConfig(itemId, serverId) { + var apiClient = connectionManager.getApiClient(serverId); - return itemId ? apiClient.getJSON(apiClient.getUrl("Items/" + itemId + "/MetadataEditor")) : Promise.resolve({}) + + if (itemId) { + return apiClient.getJSON(apiClient.getUrl('Items/' + itemId + '/MetadataEditor')); + } + + return Promise.resolve({}); } function populateCountries(select, allCountries) { + var html = ""; + html += ""; + for (var i = 0, length = allCountries.length; i < length; i++) { + var culture = allCountries[i]; - html += "" + + html += ""; } - select.innerHTML = html + + select.innerHTML = html; } function populateLanguages(select, languages) { + var html = ""; + html += ""; + for (var i = 0, length = languages.length; i < length; i++) { + var culture = languages[i]; - html += "" + + html += ""; } - select.innerHTML = html + + select.innerHTML = html; } function renderContentTypeOptions(context, metadataInfo) { - metadataInfo.ContentTypeOptions.length ? showElement("#fldContentType", context) : hideElement("#fldContentType", context); - var html = metadataInfo.ContentTypeOptions.map(function(i) { - return '" - }).join(""), - selectEl = context.querySelector("#selectContentType"); - selectEl.innerHTML = html, selectEl.value = metadataInfo.ContentType || "" + + if (!metadataInfo.ContentTypeOptions.length) { + hideElement('#fldContentType', context); + } else { + showElement('#fldContentType', context); + } + + var html = metadataInfo.ContentTypeOptions.map(function (i) { + + + return ''; + + }).join(''); + + var selectEl = context.querySelector('#selectContentType'); + selectEl.innerHTML = html; + selectEl.value = metadataInfo.ContentType || ''; } function loadExternalIds(context, item, externalIds) { - for (var html = "", providerIds = item.ProviderIds || {}, i = 0, length = externalIds.length; i < length; i++) { - var idInfo = externalIds[i], - id = "txt1" + idInfo.Key, - formatString = idInfo.UrlFormatString || "", - labelText = globalize.translate("sharedcomponents#LabelDynamicExternalId").replace("{0}", idInfo.Name); - html += '
', html += '
'; - var value = providerIds[idInfo.Key] || ""; - html += '
', html += '', html += "
", formatString && (html += ''), html += "
", html += "
" + + var html = ''; + + var providerIds = item.ProviderIds || {}; + + for (var i = 0, length = externalIds.length; i < length; i++) { + + var idInfo = externalIds[i]; + + var id = "txt1" + idInfo.Key; + var formatString = idInfo.UrlFormatString || ''; + + var labelText = globalize.translate('sharedcomponents#LabelDynamicExternalId').replace('{0}', idInfo.Name); + + html += '
'; + html += '
'; + + var value = providerIds[idInfo.Key] || ''; + + html += '
'; + html += ''; + html += '
'; + + if (formatString) { + html += ''; + } + html += '
'; + + html += '
'; + } + + var elem = context.querySelector('.externalIds', context); + elem.innerHTML = html; + + if (externalIds.length) { + context.querySelector('.externalIdsSection').classList.remove('hide'); + } else { + context.querySelector('.externalIdsSection').classList.add('hide'); } - context.querySelector(".externalIds", context).innerHTML = html, externalIds.length ? context.querySelector(".externalIdsSection").classList.remove("hide") : context.querySelector(".externalIdsSection").classList.add("hide") } + // Function to hide the element by selector or raw element + // Selector can be an element or a selector string + // Context is optional and restricts the querySelector to the context function hideElement(selector, context, multiple) { - if (context = context || document, "string" == typeof selector) { + context = context || document; + if (typeof selector === 'string') { + var elements = multiple ? context.querySelectorAll(selector) : [context.querySelector(selector)]; - Array.prototype.forEach.call(elements, function(el) { - el && el.classList.add("hide") - }) - } else selector.classList.add("hide") + + Array.prototype.forEach.call(elements, function (el) { + if (el) { + el.classList.add('hide'); + } + }); + } else { + selector.classList.add('hide'); + } } + // Function to show the element by selector or raw element + // Selector can be an element or a selector string + // Context is optional and restricts the querySelector to the context function showElement(selector, context, multiple) { - if (context = context || document, "string" == typeof selector) { + context = context || document; + if (typeof selector === 'string') { + var elements = multiple ? context.querySelectorAll(selector) : [context.querySelector(selector)]; - Array.prototype.forEach.call(elements, function(el) { - el && el.classList.remove("hide") - }) - } else selector.classList.remove("hide") + + Array.prototype.forEach.call(elements, function (el) { + if (el) { + el.classList.remove('hide'); + } + }); + } else { + selector.classList.remove('hide'); + } } function setFieldVisibilities(context, item) { - item.Path && !1 !== item.EnableMediaSourceDisplay ? showElement("#fldPath", context) : hideElement("#fldPath", context), "Series" === item.Type || "Movie" === item.Type || "Trailer" === item.Type ? showElement("#fldOriginalName", context) : hideElement("#fldOriginalName", context), "Series" === item.Type ? showElement("#fldSeriesRuntime", context) : hideElement("#fldSeriesRuntime", context), "Series" === item.Type || "Person" === item.Type ? showElement("#fldEndDate", context) : hideElement("#fldEndDate", context), "MusicAlbum" === item.Type ? showElement("#albumAssociationMessage", context) : hideElement("#albumAssociationMessage", context), "Movie" === item.Type || "Trailer" === item.Type ? showElement("#fldCriticRating", context) : hideElement("#fldCriticRating", context), "Series" === item.Type ? (showElement("#fldStatus", context), showElement("#fldAirDays", context), showElement("#fldAirTime", context)) : (hideElement("#fldStatus", context), hideElement("#fldAirDays", context), hideElement("#fldAirTime", context)), "Video" === item.MediaType && "TvChannel" !== item.Type ? showElement("#fld3dFormat", context) : hideElement("#fld3dFormat", context), "Audio" === item.Type ? showElement("#fldAlbumArtist", context) : hideElement("#fldAlbumArtist", context), "Audio" === item.Type || "MusicVideo" === item.Type ? (showElement("#fldArtist", context), showElement("#fldAlbum", context)) : (hideElement("#fldArtist", context), hideElement("#fldAlbum", context)), "Episode" === item.Type && 0 === item.ParentIndexNumber ? showElement("#collapsibleSpecialEpisodeInfo", context) : hideElement("#collapsibleSpecialEpisodeInfo", context), "Person" === item.Type || "Genre" === item.Type || "Studio" === item.Type || "GameGenre" === item.Type || "MusicGenre" === item.Type || "TvChannel" === item.Type || "Book" === item.Type ? hideElement("#peopleCollapsible", context) : showElement("#peopleCollapsible", context), "Person" === item.Type || "Genre" === item.Type || "Studio" === item.Type || "GameGenre" === item.Type || "MusicGenre" === item.Type || "TvChannel" === item.Type ? (hideElement("#fldCommunityRating", context), hideElement("#genresCollapsible", context), hideElement("#studiosCollapsible", context), "TvChannel" === item.Type ? showElement("#fldOfficialRating", context) : hideElement("#fldOfficialRating", context), hideElement("#fldCustomRating", context)) : (showElement("#fldCommunityRating", context), showElement("#genresCollapsible", context), showElement("#studiosCollapsible", context), showElement("#fldOfficialRating", context), showElement("#fldCustomRating", context)), showElement("#tagsCollapsible", context), "TvChannel" === item.Type ? (hideElement("#metadataSettingsCollapsible", context), hideElement("#fldPremiereDate", context), hideElement("#fldDateAdded", context), hideElement("#fldYear", context)) : (showElement("#metadataSettingsCollapsible", context), showElement("#fldPremiereDate", context), showElement("#fldDateAdded", context), showElement("#fldYear", context)), "TvChannel" === item.Type ? hideElement(".overviewContainer", context) : showElement(".overviewContainer", context), "Person" === item.Type ? (context.querySelector("#txtProductionYear").label(globalize.translate("sharedcomponents#LabelBirthYear")), context.querySelector("#txtPremiereDate").label(globalize.translate("sharedcomponents#LabelBirthDate")), context.querySelector("#txtEndDate").label(globalize.translate("sharedcomponents#LabelDeathDate")), showElement("#fldPlaceOfBirth")) : (context.querySelector("#txtProductionYear").label(globalize.translate("sharedcomponents#LabelYear")), context.querySelector("#txtPremiereDate").label(globalize.translate("sharedcomponents#LabelReleaseDate")), context.querySelector("#txtEndDate").label(globalize.translate("sharedcomponents#LabelEndDate")), hideElement("#fldPlaceOfBirth")), "Video" === item.MediaType && "TvChannel" !== item.Type ? showElement("#fldOriginalAspectRatio") : hideElement("#fldOriginalAspectRatio"), "Audio" === item.Type || "Episode" === item.Type || "Season" === item.Type ? (showElement("#fldIndexNumber"), "Episode" === item.Type ? context.querySelector("#txtIndexNumber").label(globalize.translate("sharedcomponents#LabelEpisodeNumber")) : "Season" === item.Type ? context.querySelector("#txtIndexNumber").label(globalize.translate("sharedcomponents#LabelSeasonNumber")) : "Audio" === item.Type ? context.querySelector("#txtIndexNumber").label(globalize.translate("sharedcomponents#LabelTrackNumber")) : context.querySelector("#txtIndexNumber").label(globalize.translate("sharedcomponents#LabelNumber"))) : hideElement("#fldIndexNumber"), "Audio" === item.Type || "Episode" === item.Type ? (showElement("#fldParentIndexNumber"), "Episode" === item.Type ? context.querySelector("#txtParentIndexNumber").label(globalize.translate("sharedcomponents#LabelSeasonNumber")) : "Audio" === item.Type ? context.querySelector("#txtParentIndexNumber").label(globalize.translate("sharedcomponents#LabelDiscNumber")) : context.querySelector("#txtParentIndexNumber").label(globalize.translate("sharedcomponents#LabelParentNumber"))) : hideElement("#fldParentIndexNumber", context), "BoxSet" === item.Type ? (showElement("#fldDisplayOrder", context), hideElement(".seriesDisplayOrderDescription", context), context.querySelector("#selectDisplayOrder").innerHTML = '") : "Series" === item.Type ? (showElement("#fldDisplayOrder", context), showElement(".seriesDisplayOrderDescription", context), context.querySelector("#selectDisplayOrder").innerHTML = '') : (context.querySelector("#selectDisplayOrder").innerHTML = "", hideElement("#fldDisplayOrder", context)) + if (item.Path && item.EnableMediaSourceDisplay !== false) { + showElement('#fldPath', context); + } else { + hideElement('#fldPath', context); + } + + if (item.Type === "Series" || item.Type === "Movie" || item.Type === "Trailer") { + showElement('#fldOriginalName', context); + } else { + hideElement('#fldOriginalName', context); + } + + if (item.Type === "Series") { + showElement('#fldSeriesRuntime', context); + } else { + hideElement('#fldSeriesRuntime', context); + } + + if (item.Type === "Series" || item.Type === "Person") { + showElement('#fldEndDate', context); + } else { + hideElement('#fldEndDate', context); + } + + if (item.Type === "MusicAlbum") { + showElement('#albumAssociationMessage', context); + } else { + hideElement('#albumAssociationMessage', context); + } + + if (item.Type === "Movie" || item.Type === "Trailer") { + showElement('#fldCriticRating', context); + } else { + hideElement('#fldCriticRating', context); + } + + if (item.Type === "Series") { + showElement('#fldStatus', context); + showElement('#fldAirDays', context); + showElement('#fldAirTime', context); + } else { + hideElement('#fldStatus', context); + hideElement('#fldAirDays', context); + hideElement('#fldAirTime', context); + } + + if (item.MediaType === "Video" && item.Type !== "TvChannel") { + showElement('#fld3dFormat', context); + } else { + hideElement('#fld3dFormat', context); + } + + if (item.Type === "Audio") { + showElement('#fldAlbumArtist', context); + } else { + hideElement('#fldAlbumArtist', context); + } + + if (item.Type === "Audio" || item.Type === "MusicVideo") { + showElement('#fldArtist', context); + showElement('#fldAlbum', context); + } else { + hideElement('#fldArtist', context); + hideElement('#fldAlbum', context); + } + + if (item.Type === "Episode" && item.ParentIndexNumber === 0) { + showElement('#collapsibleSpecialEpisodeInfo', context); + } else { + hideElement('#collapsibleSpecialEpisodeInfo', context); + } + + if (item.Type === "Person" || + item.Type === "Genre" || + item.Type === "Studio" || + item.Type === "GameGenre" || + item.Type === "MusicGenre" || + item.Type === "TvChannel" || + item.Type === "Book") { + hideElement('#peopleCollapsible', context); + } else { + showElement('#peopleCollapsible', context); + } + + if (item.Type === "Person" || item.Type === "Genre" || item.Type === "Studio" || item.Type === "GameGenre" || item.Type === "MusicGenre" || item.Type === "TvChannel") { + hideElement('#fldCommunityRating', context); + hideElement('#genresCollapsible', context); + hideElement('#studiosCollapsible', context); + + if (item.Type === "TvChannel") { + showElement('#fldOfficialRating', context); + } else { + hideElement('#fldOfficialRating', context); + } + hideElement('#fldCustomRating', context); + } else { + showElement('#fldCommunityRating', context); + showElement('#genresCollapsible', context); + showElement('#studiosCollapsible', context); + showElement('#fldOfficialRating', context); + showElement('#fldCustomRating', context); + } + + showElement('#tagsCollapsible', context); + + if (item.Type === "TvChannel") { + hideElement('#metadataSettingsCollapsible', context); + hideElement('#fldPremiereDate', context); + hideElement('#fldDateAdded', context); + hideElement('#fldYear', context); + } else { + showElement('#metadataSettingsCollapsible', context); + showElement('#fldPremiereDate', context); + showElement('#fldDateAdded', context); + showElement('#fldYear', context); + } + + if (item.Type === "TvChannel") { + hideElement('.overviewContainer', context); + } else { + showElement('.overviewContainer', context); + } + + if (item.Type === "Person") { + //todo + context.querySelector('#txtProductionYear').label(globalize.translate('sharedcomponents#LabelBirthYear')); + context.querySelector("#txtPremiereDate").label(globalize.translate('sharedcomponents#LabelBirthDate')); + context.querySelector("#txtEndDate").label(globalize.translate('sharedcomponents#LabelDeathDate')); + showElement('#fldPlaceOfBirth'); + } else { + context.querySelector('#txtProductionYear').label(globalize.translate('sharedcomponents#LabelYear')); + context.querySelector("#txtPremiereDate").label(globalize.translate('sharedcomponents#LabelReleaseDate')); + context.querySelector("#txtEndDate").label(globalize.translate('sharedcomponents#LabelEndDate')); + hideElement('#fldPlaceOfBirth'); + } + + if (item.MediaType === "Video" && item.Type !== "TvChannel") { + showElement('#fldOriginalAspectRatio'); + } else { + hideElement('#fldOriginalAspectRatio'); + } + + if (item.Type === "Audio" || item.Type === "Episode" || item.Type === "Season") { + showElement('#fldIndexNumber'); + + if (item.Type === "Episode") { + context.querySelector('#txtIndexNumber').label(globalize.translate('sharedcomponents#LabelEpisodeNumber')); + } else if (item.Type === "Season") { + context.querySelector('#txtIndexNumber').label(globalize.translate('sharedcomponents#LabelSeasonNumber')); + } else if (item.Type === "Audio") { + context.querySelector('#txtIndexNumber').label(globalize.translate('sharedcomponents#LabelTrackNumber')); + } else { + context.querySelector('#txtIndexNumber').label(globalize.translate('sharedcomponents#LabelNumber')); + } + } else { + hideElement('#fldIndexNumber'); + } + + if (item.Type === "Audio" || item.Type === "Episode") { + showElement('#fldParentIndexNumber'); + + if (item.Type === "Episode") { + context.querySelector('#txtParentIndexNumber').label(globalize.translate('sharedcomponents#LabelSeasonNumber')); + } else if (item.Type === "Audio") { + context.querySelector('#txtParentIndexNumber').label(globalize.translate('sharedcomponents#LabelDiscNumber')); + } else { + context.querySelector('#txtParentIndexNumber').label(globalize.translate('sharedcomponents#LabelParentNumber')); + } + } else { + hideElement('#fldParentIndexNumber', context); + } + + if (item.Type === "BoxSet") { + showElement('#fldDisplayOrder', context); + hideElement('.seriesDisplayOrderDescription', context); + + context.querySelector('#selectDisplayOrder').innerHTML = ''; + } else if (item.Type === "Series") { + showElement('#fldDisplayOrder', context); + showElement('.seriesDisplayOrderDescription', context); + + context.querySelector('#selectDisplayOrder').innerHTML = ''; + } else { + context.querySelector('#selectDisplayOrder').innerHTML = ''; + hideElement('#fldDisplayOrder', context); + } } function fillItemInfo(context, item, parentalRatingOptions) { - var select = context.querySelector("#selectOfficialRating"); - populateRatings(parentalRatingOptions, select, item.OfficialRating), select.value = item.OfficialRating || "", select = context.querySelector("#selectCustomRating"), populateRatings(parentalRatingOptions, select, item.CustomRating), select.value = item.CustomRating || ""; - var selectStatus = context.querySelector("#selectStatus"); - populateStatus(selectStatus), selectStatus.value = item.Status || "", context.querySelector("#select3dFormat", context).value = item.Video3DFormat || "", Array.prototype.forEach.call(context.querySelectorAll(".chkAirDay", context), function(el) { - el.checked = -1 !== (item.AirDays || []).indexOf(el.getAttribute("data-day")) - }), populateListView(context.querySelector("#listGenres"), item.Genres), populatePeople(context, item.People || []), populateListView(context.querySelector("#listStudios"), (item.Studios || []).map(function(element) { - return element.Name || "" - })), populateListView(context.querySelector("#listTags"), item.Tags); - var lockData = item.LockData || !1, - chkLockData = context.querySelector("#chkLockData"); - chkLockData.checked = lockData, chkLockData.checked ? hideElement(".providerSettingsContainer", context) : showElement(".providerSettingsContainer", context), fillMetadataSettings(context, item, item.LockedFields), context.querySelector("#txtPath").value = item.Path || "", context.querySelector("#txtName").value = item.Name || "", context.querySelector("#txtOriginalName").value = item.OriginalTitle || "", context.querySelector("#txtOverview").value = item.Overview || "", context.querySelector("#txtTagline").value = item.Taglines && item.Taglines.length ? item.Taglines[0] : "", context.querySelector("#txtSortName").value = item.ForcedSortName || "", context.querySelector("#txtCommunityRating").value = item.CommunityRating || "", context.querySelector("#txtCriticRating").value = item.CriticRating || "", context.querySelector("#txtIndexNumber").value = null == item.IndexNumber ? "" : item.IndexNumber, context.querySelector("#txtParentIndexNumber").value = null == item.ParentIndexNumber ? "" : item.ParentIndexNumber, context.querySelector("#txtAirsBeforeSeason").value = "AirsBeforeSeasonNumber" in item ? item.AirsBeforeSeasonNumber : "", context.querySelector("#txtAirsAfterSeason").value = "AirsAfterSeasonNumber" in item ? item.AirsAfterSeasonNumber : "", context.querySelector("#txtAirsBeforeEpisode").value = "AirsBeforeEpisodeNumber" in item ? item.AirsBeforeEpisodeNumber : "", context.querySelector("#txtAlbum").value = item.Album || "", context.querySelector("#txtAlbumArtist").value = (item.AlbumArtists || []).map(function(a) { - return a.Name - }).join(";"), item.Type, context.querySelector("#selectDisplayOrder").value = item.DisplayOrder || "", context.querySelector("#txtArtist").value = (item.ArtistItems || []).map(function(a) { - return a.Name - }).join(";"); + + var select = context.querySelector('#selectOfficialRating'); + + populateRatings(parentalRatingOptions, select, item.OfficialRating); + + select.value = item.OfficialRating || ""; + + select = context.querySelector('#selectCustomRating'); + + populateRatings(parentalRatingOptions, select, item.CustomRating); + + select.value = item.CustomRating || ""; + + var selectStatus = context.querySelector('#selectStatus'); + populateStatus(selectStatus); + selectStatus.value = item.Status || ""; + + context.querySelector('#select3dFormat', context).value = item.Video3DFormat || ""; + + Array.prototype.forEach.call(context.querySelectorAll('.chkAirDay', context), function (el) { + el.checked = (item.AirDays || []).indexOf(el.getAttribute('data-day')) !== -1; + }); + + populateListView(context.querySelector('#listGenres'), item.Genres); + populatePeople(context, item.People || []); + + populateListView(context.querySelector('#listStudios'), (item.Studios || []).map(function (element) { return element.Name || ''; })); + + populateListView(context.querySelector('#listTags'), item.Tags); + + var lockData = (item.LockData || false); + var chkLockData = context.querySelector("#chkLockData"); + chkLockData.checked = lockData; + if (chkLockData.checked) { + hideElement('.providerSettingsContainer', context); + } else { + showElement('.providerSettingsContainer', context); + } + fillMetadataSettings(context, item, item.LockedFields); + + context.querySelector('#txtPath').value = item.Path || ''; + context.querySelector('#txtName').value = item.Name || ""; + context.querySelector('#txtOriginalName').value = item.OriginalTitle || ""; + context.querySelector('#txtOverview').value = item.Overview || ''; + context.querySelector('#txtTagline').value = (item.Taglines && item.Taglines.length ? item.Taglines[0] : ''); + context.querySelector('#txtSortName').value = item.ForcedSortName || ""; + context.querySelector('#txtCommunityRating').value = item.CommunityRating || ""; + + context.querySelector('#txtCriticRating').value = item.CriticRating || ""; + + context.querySelector('#txtIndexNumber').value = item.IndexNumber == null ? '' : item.IndexNumber; + context.querySelector('#txtParentIndexNumber').value = item.ParentIndexNumber == null ? '' : item.ParentIndexNumber; + + context.querySelector('#txtAirsBeforeSeason').value = ('AirsBeforeSeasonNumber' in item) ? item.AirsBeforeSeasonNumber : ""; + context.querySelector('#txtAirsAfterSeason').value = ('AirsAfterSeasonNumber' in item) ? item.AirsAfterSeasonNumber : ""; + context.querySelector('#txtAirsBeforeEpisode').value = ('AirsBeforeEpisodeNumber' in item) ? item.AirsBeforeEpisodeNumber : ""; + + context.querySelector('#txtAlbum').value = item.Album || ""; + + context.querySelector('#txtAlbumArtist').value = (item.AlbumArtists || []).map(function (a) { + return a.Name; + }).join(';'); + + if (item.Type === 'Series') { + context.querySelector('#selectDisplayOrder').value = item.DisplayOrder || ''; + } + else { + context.querySelector('#selectDisplayOrder').value = item.DisplayOrder || ''; + } + + context.querySelector('#txtArtist').value = (item.ArtistItems || []).map(function (a) { + return a.Name; + }).join(';'); + var date; - if (item.DateCreated) try { - date = datetime.parseISO8601Date(item.DateCreated, !0), context.querySelector("#txtDateAdded").value = date.toISOString().slice(0, 10) - } catch (e) { - context.querySelector("#txtDateAdded").value = "" - } else context.querySelector("#txtDateAdded").value = ""; - if (item.PremiereDate) try { - date = datetime.parseISO8601Date(item.PremiereDate, !0), context.querySelector("#txtPremiereDate").value = date.toISOString().slice(0, 10) - } catch (e) { - context.querySelector("#txtPremiereDate").value = "" - } else context.querySelector("#txtPremiereDate").value = ""; - if (item.EndDate) try { - date = datetime.parseISO8601Date(item.EndDate, !0), context.querySelector("#txtEndDate").value = date.toISOString().slice(0, 10) - } catch (e) { - context.querySelector("#txtEndDate").value = "" - } else context.querySelector("#txtEndDate").value = ""; - context.querySelector("#txtProductionYear").value = item.ProductionYear || "", context.querySelector("#txtAirTime").value = item.AirTime || ""; - var placeofBirth = item.ProductionLocations && item.ProductionLocations.length ? item.ProductionLocations[0] : ""; - if (context.querySelector("#txtPlaceOfBirth").value = placeofBirth, context.querySelector("#txtOriginalAspectRatio").value = item.AspectRatio || "", context.querySelector("#selectLanguage").value = item.PreferredMetadataLanguage || "", context.querySelector("#selectCountry").value = item.PreferredMetadataCountryCode || "", item.RunTimeTicks) { - var minutes = item.RunTimeTicks / 6e8; - context.querySelector("#txtSeriesRuntime").value = Math.round(minutes) - } else context.querySelector("#txtSeriesRuntime", context).value = "" + + if (item.DateCreated) { + try { + date = datetime.parseISO8601Date(item.DateCreated, true); + + context.querySelector('#txtDateAdded').value = date.toISOString().slice(0, 10); + } catch (e) { + context.querySelector('#txtDateAdded').value = ''; + } + } else { + context.querySelector('#txtDateAdded').value = ''; + } + + if (item.PremiereDate) { + try { + date = datetime.parseISO8601Date(item.PremiereDate, true); + + context.querySelector('#txtPremiereDate').value = date.toISOString().slice(0, 10); + } catch (e) { + context.querySelector('#txtPremiereDate').value = ''; + } + } else { + context.querySelector('#txtPremiereDate').value = ''; + } + + if (item.EndDate) { + try { + date = datetime.parseISO8601Date(item.EndDate, true); + + context.querySelector('#txtEndDate').value = date.toISOString().slice(0, 10); + } catch (e) { + context.querySelector('#txtEndDate').value = ''; + } + } else { + context.querySelector('#txtEndDate').value = ''; + } + + context.querySelector('#txtProductionYear').value = item.ProductionYear || ""; + + context.querySelector('#txtAirTime').value = item.AirTime || ''; + + var placeofBirth = item.ProductionLocations && item.ProductionLocations.length ? item.ProductionLocations[0] : ''; + context.querySelector('#txtPlaceOfBirth').value = placeofBirth; + + context.querySelector('#txtOriginalAspectRatio').value = item.AspectRatio || ""; + + context.querySelector('#selectLanguage').value = item.PreferredMetadataLanguage || ""; + context.querySelector('#selectCountry').value = item.PreferredMetadataCountryCode || ""; + + if (item.RunTimeTicks) { + + var minutes = item.RunTimeTicks / 600000000; + + context.querySelector('#txtSeriesRuntime').value = Math.round(minutes); + } else { + context.querySelector('#txtSeriesRuntime', context).value = ""; + } } function populateRatings(allParentalRatings, select, currentValue) { + var html = ""; + html += ""; - var i, length, rating, ratings = [], - currentValueFound = !1; - for (i = 0, length = allParentalRatings.length; i < length; i++) rating = allParentalRatings[i], ratings.push({ - Name: rating.Name, - Value: rating.Name - }), rating.Name === currentValue && (currentValueFound = !0); - for (currentValue && !currentValueFound && ratings.push({ - Name: currentValue, - Value: currentValue - }), i = 0, length = ratings.length; i < length; i++) rating = ratings[i], html += ""; - select.innerHTML = html + + var ratings = []; + var i, length, rating; + + var currentValueFound = false; + + for (i = 0, length = allParentalRatings.length; i < length; i++) { + + rating = allParentalRatings[i]; + + ratings.push({ Name: rating.Name, Value: rating.Name }); + + if (rating.Name === currentValue) { + currentValueFound = true; + } + } + + if (currentValue && !currentValueFound) { + ratings.push({ Name: currentValue, Value: currentValue }); + } + + for (i = 0, length = ratings.length; i < length; i++) { + + rating = ratings[i]; + + html += ""; + } + + select.innerHTML = html; } function populateStatus(select) { var html = ""; - html += "", html += "", html += "", select.innerHTML = html + + html += ""; + html += ""; + html += ""; + select.innerHTML = html; } function populateListView(list, items, sortCallback) { - items = items || [], void 0 === sortCallback ? items.sort(function(a, b) { - return a.toLowerCase().localeCompare(b.toLowerCase()) - }) : items = sortCallback(items); - for (var html = "", i = 0; i < items.length; i++) html += '
', html += 'live_tv', html += '
', html += '
', html += items[i], html += "
", html += "
", html += '', html += "
"; - list.innerHTML = html + + items = items || []; + if (typeof (sortCallback) === 'undefined') { + items.sort(function (a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); }); + } else { + items = sortCallback(items); + } + var html = ''; + for (var i = 0; i < items.length; i++) { + html += '
'; + + html += 'live_tv'; + + html += '
'; + + html += '
'; + html += items[i]; + html += '
'; + + html += '
'; + + html += ''; + + html += '
'; + } + + list.innerHTML = html; } function populatePeople(context, people) { - for (var html = "", elem = context.querySelector("#peopleList"), i = 0, length = people.length; i < length; i++) { + + var lastType = ''; + var html = ''; + + var elem = context.querySelector('#peopleList'); + + for (var i = 0, length = people.length; i < length; i++) { + var person = people[i]; - html += '
', html += 'person', html += '
', html += '", html += "
", html += '', html += "
" + + html += '
'; + + html += 'person'; + + html += '
'; + html += ''; + html += '
'; + + html += ''; + + html += '
'; } - elem.innerHTML = html + + elem.innerHTML = html; } function getLockedFieldsHtml(fields, currentFields) { - for (var html = "", i = 0; i < fields.length; i++) { - var field = fields[i], - name = field.name, - value = field.value || field.name, - checkedHtml = -1 === currentFields.indexOf(value) ? " checked" : ""; - html += "" + + var html = ''; + for (var i = 0; i < fields.length; i++) { + + var field = fields[i]; + var name = field.name; + var value = field.value || field.name; + var checkedHtml = currentFields.indexOf(value) === -1 ? ' checked' : ''; + html += ''; } - return html + return html; } function fillMetadataSettings(context, item, lockedFields) { - var container = context.querySelector(".providerSettingsContainer"); + var container = context.querySelector('.providerSettingsContainer'); lockedFields = lockedFields || []; - var lockedFieldsList = [{ - name: globalize.translate("sharedcomponents#Name"), - value: "Name" - }, { - name: globalize.translate("sharedcomponents#Overview"), - value: "Overview" - }, { - name: globalize.translate("sharedcomponents#Genres"), - value: "Genres" - }, { - name: globalize.translate("sharedcomponents#ParentalRating"), - value: "OfficialRating" - }, { - name: globalize.translate("sharedcomponents#People"), - value: "Cast" - }]; - "Person" === item.Type ? lockedFieldsList.push({ - name: globalize.translate("sharedcomponents#BirthLocation"), - value: "ProductionLocations" - }) : lockedFieldsList.push({ - name: globalize.translate("sharedcomponents#ProductionLocations"), - value: "ProductionLocations" - }), "Series" === item.Type && lockedFieldsList.push({ - name: globalize.translate("Runtime"), - value: "Runtime" - }), lockedFieldsList.push({ - name: globalize.translate("sharedcomponents#Studios"), - value: "Studios" - }), lockedFieldsList.push({ - name: globalize.translate("sharedcomponents#Tags"), - value: "Tags" - }); - var html = ""; - html += "

" + globalize.translate("sharedcomponents#HeaderEnabledFields") + "

", html += "

" + globalize.translate("sharedcomponents#HeaderEnabledFieldsHelp") + "

", html += getLockedFieldsHtml(lockedFieldsList, lockedFields), container.innerHTML = html + + var lockedFieldsList = [ + { name: globalize.translate('sharedcomponents#Name'), value: "Name" }, + { name: globalize.translate('sharedcomponents#Overview'), value: "Overview" }, + { name: globalize.translate('sharedcomponents#Genres'), value: "Genres" }, + { name: globalize.translate('sharedcomponents#ParentalRating'), value: "OfficialRating" }, + { name: globalize.translate('sharedcomponents#People'), value: "Cast" } + ]; + + if (item.Type === "Person") { + lockedFieldsList.push({ name: globalize.translate('sharedcomponents#BirthLocation'), value: "ProductionLocations" }); + } else { + lockedFieldsList.push({ name: globalize.translate('sharedcomponents#ProductionLocations'), value: "ProductionLocations" }); + } + + if (item.Type === "Series") { + lockedFieldsList.push({ name: globalize.translate('Runtime'), value: "Runtime" }); + } + + lockedFieldsList.push({ name: globalize.translate('sharedcomponents#Studios'), value: "Studios" }); + lockedFieldsList.push({ name: globalize.translate('sharedcomponents#Tags'), value: "Tags" }); + + var html = ''; + + html += "

" + globalize.translate('sharedcomponents#HeaderEnabledFields') + "

"; + html += "

" + globalize.translate('sharedcomponents#HeaderEnabledFieldsHelp') + "

"; + html += getLockedFieldsHtml(lockedFieldsList, lockedFields); + container.innerHTML = html; } function reload(context, itemId, serverId) { - loading.show(), Promise.all([getItem(itemId, serverId), getEditorConfig(itemId, serverId)]).then(function(responses) { + + loading.show(); + + Promise.all([getItem(itemId, serverId), getEditorConfig(itemId, serverId)]).then(function (responses) { + var item = responses[0]; - metadataEditorInfo = responses[1], currentItem = item; - var languages = metadataEditorInfo.Cultures, - countries = metadataEditorInfo.Countries; - renderContentTypeOptions(context, metadataEditorInfo), loadExternalIds(context, item, metadataEditorInfo.ExternalIdInfos), populateLanguages(context.querySelector("#selectLanguage"), languages), populateCountries(context.querySelector("#selectCountry"), countries), setFieldVisibilities(context, item), fillItemInfo(context, item, metadataEditorInfo.ParentalRatingOptions), "Video" === item.MediaType && "Episode" !== item.Type && "TvChannel" !== item.Type ? showElement("#fldTagline", context) : hideElement("#fldTagline", context), loading.hide() - }) + metadataEditorInfo = responses[1]; + + currentItem = item; + + var languages = metadataEditorInfo.Cultures; + var countries = metadataEditorInfo.Countries; + + renderContentTypeOptions(context, metadataEditorInfo); + + loadExternalIds(context, item, metadataEditorInfo.ExternalIdInfos); + + populateLanguages(context.querySelector('#selectLanguage'), languages); + populateCountries(context.querySelector('#selectCountry'), countries); + + setFieldVisibilities(context, item); + fillItemInfo(context, item, metadataEditorInfo.ParentalRatingOptions); + + if (item.MediaType === "Video" && item.Type !== "Episode" && item.Type !== "TvChannel") { + showElement('#fldTagline', context); + } else { + hideElement('#fldTagline', context); + } + + loading.hide(); + }); } function centerFocus(elem, horiz, on) { - require(["scrollHelper"], function(scrollHelper) { - var fn = on ? "on" : "off"; - scrollHelper.centerFocus[fn](elem, horiz) - }) + require(['scrollHelper'], function (scrollHelper) { + var fn = on ? 'on' : 'off'; + scrollHelper.centerFocus[fn](elem, horiz); + }); } function show(itemId, serverId, resolve, reject) { - loading.show(), require(["text!./metadataeditor.template.html"], function(template) { + loading.show(); + + require(['text!./metadataeditor.template.html'], function (template) { + var dialogOptions = { - removeOnClose: !0, - scrollY: !1 + removeOnClose: true, + scrollY: false }; - layoutManager.tv ? dialogOptions.size = "fullscreen" : dialogOptions.size = "medium-tall"; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'medium-tall'; + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"); - var html = ""; - html += globalize.translateDocument(template, "sharedcomponents"), dlg.innerHTML = html, layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !0), dialogHelper.open(dlg), dlg.addEventListener("close", function() { - layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !1), resolve() - }), currentContext = dlg, init(dlg, connectionManager.getApiClient(serverId)), reload(dlg, itemId, serverId) - }) + + dlg.classList.add('formDialog'); + + var html = ''; + + html += globalize.translateDocument(template, 'sharedcomponents'); + + dlg.innerHTML = html; + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, true); + } + + dialogHelper.open(dlg); + + dlg.addEventListener('close', function () { + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, false); + } + + resolve(); + }); + + currentContext = dlg; + + init(dlg, connectionManager.getApiClient(serverId)); + + reload(dlg, itemId, serverId); + }); } - var currentContext, metadataEditorInfo, currentItem; + return { - show: function(itemId, serverId) { - return new Promise(function(resolve, reject) { - return show(itemId, serverId, resolve, reject) - }) + show: function (itemId, serverId) { + return new Promise(function (resolve, reject) { + return show(itemId, serverId, resolve, reject); + }); }, - embed: function(elem, itemId, serverId) { - return new Promise(function(resolve, reject) { - loading.show(), require(["text!./metadataeditor.template.html"], function(template) { - elem.innerHTML = globalize.translateDocument(template, "sharedcomponents"), elem.querySelector(".formDialogFooter").classList.remove("formDialogFooter"), elem.querySelector(".btnHeaderSave").classList.remove("hide"), elem.querySelector(".btnCancel").classList.add("hide"), currentContext = elem, init(elem, connectionManager.getApiClient(serverId)), reload(elem, itemId, serverId), focusManager.autoFocus(elem) - }) - }) + + embed: function (elem, itemId, serverId) { + return new Promise(function (resolve, reject) { + + loading.show(); + + require(['text!./metadataeditor.template.html'], function (template) { + + elem.innerHTML = globalize.translateDocument(template, 'sharedcomponents'); + + elem.querySelector('.formDialogFooter').classList.remove('formDialogFooter'); + elem.querySelector('.btnHeaderSave').classList.remove('hide'); + elem.querySelector('.btnCancel').classList.add('hide'); + + currentContext = elem; + + init(elem, connectionManager.getApiClient(serverId)); + reload(elem, itemId, serverId); + + focusManager.autoFocus(elem); + }); + }); } - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/metadataeditor/personeditor.js b/src/bower_components/emby-webcomponents/metadataeditor/personeditor.js index 4bca9a2adc..7cfee43005 100644 --- a/src/bower_components/emby-webcomponents/metadataeditor/personeditor.js +++ b/src/bower_components/emby-webcomponents/metadataeditor/personeditor.js @@ -1,40 +1,99 @@ -define(["dialogHelper", "layoutManager", "globalize", "require", "paper-icon-button-light", "emby-input", "emby-select", "css!./../formdialog"], function(dialogHelper, layoutManager, globalize, require) { - "use strict"; +define(['dialogHelper', 'layoutManager', 'globalize', 'require', 'paper-icon-button-light', 'emby-input', 'emby-select', 'css!./../formdialog'], function (dialogHelper, layoutManager, globalize, require) { + 'use strict'; function centerFocus(elem, horiz, on) { - require(["scrollHelper"], function(scrollHelper) { - var fn = on ? "on" : "off"; - scrollHelper.centerFocus[fn](elem, horiz) - }) + require(['scrollHelper'], function (scrollHelper) { + var fn = on ? 'on' : 'off'; + scrollHelper.centerFocus[fn](elem, horiz); + }); } function show(person) { - return new Promise(function(resolve, reject) { - require(["text!./personeditor.template.html"], function(template) { + return new Promise(function (resolve, reject) { + + require(['text!./personeditor.template.html'], function (template) { + var dialogOptions = { - removeOnClose: !0, - scrollY: !1 + removeOnClose: true, + scrollY: false }; - layoutManager.tv ? dialogOptions.size = "fullscreen" : dialogOptions.size = "medium-tall"; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'medium-tall'; + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"); - var html = "", - submitted = !1; - html += globalize.translateDocument(template, "sharedcomponents"), dlg.innerHTML = html, dlg.querySelector(".txtPersonName", dlg).value = person.Name || "", dlg.querySelector(".selectPersonType", dlg).value = person.Type || "", dlg.querySelector(".txtPersonRole", dlg).value = person.Role || "", layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !0), dialogHelper.open(dlg), dlg.addEventListener("close", function() { - layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !1), submitted ? resolve(person) : reject() - }), dlg.querySelector(".selectPersonType").addEventListener("change", function(e) { - "Actor" === this.value ? dlg.querySelector(".fldRole").classList.remove("hide") : dlg.querySelector(".fldRole").classList.add("hide") - }), dlg.querySelector(".btnCancel").addEventListener("click", function(e) { - dialogHelper.close(dlg) - }), dlg.querySelector("form").addEventListener("submit", function(e) { - return submitted = !0, person.Name = dlg.querySelector(".txtPersonName", dlg).value, person.Type = dlg.querySelector(".selectPersonType", dlg).value, person.Role = dlg.querySelector(".txtPersonRole", dlg).value || null, dialogHelper.close(dlg), e.preventDefault(), !1 - }), dlg.querySelector(".selectPersonType").dispatchEvent(new CustomEvent("change", { - bubbles: !0 - })) - }) - }) + + dlg.classList.add('formDialog'); + + var html = ''; + var submitted = false; + + html += globalize.translateDocument(template, 'sharedcomponents'); + + dlg.innerHTML = html; + + dlg.querySelector('.txtPersonName', dlg).value = person.Name || ''; + dlg.querySelector('.selectPersonType', dlg).value = person.Type || ''; + dlg.querySelector('.txtPersonRole', dlg).value = person.Role || ''; + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, true); + } + + dialogHelper.open(dlg); + + dlg.addEventListener('close', function () { + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, false); + } + + if (submitted) { + resolve(person); + } else { + reject(); + } + }); + + dlg.querySelector('.selectPersonType').addEventListener('change', function (e) { + + if (this.value === 'Actor') { + dlg.querySelector('.fldRole').classList.remove('hide'); + } else { + dlg.querySelector('.fldRole').classList.add('hide'); + } + }); + + dlg.querySelector('.btnCancel').addEventListener('click', function (e) { + + dialogHelper.close(dlg); + }); + + dlg.querySelector('form').addEventListener('submit', function (e) { + + submitted = true; + + person.Name = dlg.querySelector('.txtPersonName', dlg).value; + person.Type = dlg.querySelector('.selectPersonType', dlg).value; + person.Role = dlg.querySelector('.txtPersonRole', dlg).value || null; + + dialogHelper.close(dlg); + + e.preventDefault(); + return false; + }); + + dlg.querySelector('.selectPersonType').dispatchEvent(new CustomEvent('change', { + bubbles: true + })); + }); + }); } + return { show: show - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/multidownload.js b/src/bower_components/emby-webcomponents/multidownload.js index c7554877ea..a1881b8db9 100644 --- a/src/bower_components/emby-webcomponents/multidownload.js +++ b/src/bower_components/emby-webcomponents/multidownload.js @@ -1,35 +1,66 @@ -define(["browser"], function(browser) { - "use strict"; +define(['browser'], function (browser) { + 'use strict'; function fallback(urls) { var i = 0; - ! function createIframe() { - var frame = document.createElement("iframe"); - frame.style.display = "none", frame.src = urls[i++], document.documentElement.appendChild(frame); - var interval = setInterval(function() { - "complete" !== frame.contentWindow.document.readyState && "interactive" !== frame.contentWindow.document.readyState || (clearInterval(interval), setTimeout(function() { - frame.parentNode.removeChild(frame) - }, 1e3), i < urls.length && createIframe()) - }, 100) - }() + + (function createIframe() { + var frame = document.createElement('iframe'); + frame.style.display = 'none'; + frame.src = urls[i++]; + document.documentElement.appendChild(frame); + + // the download init has to be sequential otherwise IE only use the first + var interval = setInterval(function () { + if (frame.contentWindow.document.readyState === 'complete' || frame.contentWindow.document.readyState === 'interactive') { + clearInterval(interval); + + // Safari needs a timeout + setTimeout(function () { + frame.parentNode.removeChild(frame); + }, 1000); + + if (i < urls.length) { + createIframe(); + } + } + }, 100); + })(); } function sameDomain(url) { - var a = document.createElement("a"); - return a.href = url, location.hostname === a.hostname && location.protocol === a.protocol + var a = document.createElement('a'); + a.href = url; + + return location.hostname === a.hostname && location.protocol === a.protocol; } function download(url) { - var a = document.createElement("a"); - a.download = "", a.href = url, a.dispatchEvent(new MouseEvent("click")) + var a = document.createElement('a'); + a.download = ''; + a.href = url; + // firefox doesn't support `a.click()`... + a.dispatchEvent(new MouseEvent('click')); } - return function(urls) { - if (!urls) throw new Error("`urls` required"); - if (void 0 === document.createElement("a").download) return fallback(urls); + + return function (urls) { + if (!urls) { + throw new Error('`urls` required'); + } + + if (typeof document.createElement('a').download === 'undefined') { + return fallback(urls); + } + var delay = 0; - urls.forEach(function(url) { - if (browser.firefox && !sameDomain(url)) return setTimeout(download.bind(null, url), 100 * ++delay); - download(url) - }) - } + + urls.forEach(function (url) { + // the download init has to be sequential for firefox if the urls are not on the same domain + if (browser.firefox && !sameDomain(url)) { + return setTimeout(download.bind(null, url), 100 * ++delay); + } + + download(url); + }); + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/multiselect/multiselect.css b/src/bower_components/emby-webcomponents/multiselect/multiselect.css index 42be0e86bf..2026f8aa8b 100644 --- a/src/bower_components/emby-webcomponents/multiselect/multiselect.css +++ b/src/bower_components/emby-webcomponents/multiselect/multiselect.css @@ -1,11 +1,11 @@ -.itemSelectionPanel { +.itemSelectionPanel { position: absolute; bottom: 0; left: 0; right: 0; top: 0; background-color: rgba(0, 0, 0, .3); - z-index: 99998 + z-index: 99998; } .selectionCommandsPanel { @@ -14,27 +14,22 @@ left: 0; right: 0; padding: 1em .5em; - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - z-index: 99999 + z-index: 99999; } .itemSelectionCount { vertical-align: middle; color: #fff !important; - margin: 0 + margin: 0; } .multiSelectCheckboxOutline { top: 0 !important; - -webkit-border-radius: 0 !important; - border-radius: 0 !important + border-radius: 0 !important; } .withMultiSelect { - position: relative -} \ No newline at end of file + position: relative; +} diff --git a/src/bower_components/emby-webcomponents/multiselect/multiselect.js b/src/bower_components/emby-webcomponents/multiselect/multiselect.js index e22f031eb1..e720a005ae 100644 --- a/src/bower_components/emby-webcomponents/multiselect/multiselect.js +++ b/src/bower_components/emby-webcomponents/multiselect/multiselect.js @@ -1,350 +1,632 @@ -define(["browser", "appStorage", "apphost", "loading", "connectionManager", "globalize", "appRouter", "dom", "css!./multiselect"], function(browser, appStorage, appHost, loading, connectionManager, globalize, appRouter, dom) { - "use strict"; +define(['browser', 'appStorage', 'apphost', 'loading', 'connectionManager', 'globalize', 'appRouter', 'dom', 'css!./multiselect'], function (browser, appStorage, appHost, loading, connectionManager, globalize, appRouter, dom) { + 'use strict'; + + var selectedItems = []; + var selectedElements = []; + var currentSelectionCommandsPanel; function hideSelections() { + var selectionCommandsPanel = currentSelectionCommandsPanel; if (selectionCommandsPanel) { - selectionCommandsPanel.parentNode.removeChild(selectionCommandsPanel), currentSelectionCommandsPanel = null, selectedItems = [], selectedElements = []; - for (var elems = document.querySelectorAll(".itemSelectionPanel"), i = 0, length = elems.length; i < length; i++) { + + selectionCommandsPanel.parentNode.removeChild(selectionCommandsPanel); + currentSelectionCommandsPanel = null; + + selectedItems = []; + selectedElements = []; + var elems = document.querySelectorAll('.itemSelectionPanel'); + for (var i = 0, length = elems.length; i < length; i++) { + var parent = elems[i].parentNode; - parent.removeChild(elems[i]), parent.classList.remove("withMultiSelect") + parent.removeChild(elems[i]); + parent.classList.remove('withMultiSelect'); } } } function onItemSelectionPanelClick(e, itemSelectionPanel) { - if (!dom.parentWithClass(e.target, "chkItemSelect")) { - var chkItemSelect = itemSelectionPanel.querySelector(".chkItemSelect"); - if (chkItemSelect) - if (chkItemSelect.classList.contains("checkedInitial")) chkItemSelect.classList.remove("checkedInitial"); - else { + + // toggle the checkbox, if it wasn't clicked on + if (!dom.parentWithClass(e.target, 'chkItemSelect')) { + var chkItemSelect = itemSelectionPanel.querySelector('.chkItemSelect'); + + if (chkItemSelect) { + + if (chkItemSelect.classList.contains('checkedInitial')) { + chkItemSelect.classList.remove('checkedInitial'); + } else { var newValue = !chkItemSelect.checked; - chkItemSelect.checked = newValue, updateItemSelection(chkItemSelect, newValue) + chkItemSelect.checked = newValue; + updateItemSelection(chkItemSelect, newValue); } + } } - return e.preventDefault(), e.stopPropagation(), !1 + + e.preventDefault(); + e.stopPropagation(); + return false; } function updateItemSelection(chkItemSelect, selected) { - var id = dom.parentWithAttribute(chkItemSelect, "data-id").getAttribute("data-id"); + + var id = dom.parentWithAttribute(chkItemSelect, 'data-id').getAttribute('data-id'); + if (selected) { - selectedItems.filter(function(i) { - return i === id - }).length || (selectedItems.push(id), selectedElements.push(chkItemSelect)) - } else selectedItems = selectedItems.filter(function(i) { - return i !== id - }), selectedElements = selectedElements.filter(function(i) { - return i !== chkItemSelect - }); + + var current = selectedItems.filter(function (i) { + return i === id; + }); + + if (!current.length) { + selectedItems.push(id); + selectedElements.push(chkItemSelect); + } + + } else { + selectedItems = selectedItems.filter(function (i) { + return i !== id; + }); + selectedElements = selectedElements.filter(function (i) { + return i !== chkItemSelect; + }); + } + if (selectedItems.length) { - var itemSelectionCount = document.querySelector(".itemSelectionCount"); - itemSelectionCount && (itemSelectionCount.innerHTML = selectedItems.length) - } else hideSelections() + var itemSelectionCount = document.querySelector('.itemSelectionCount'); + if (itemSelectionCount) { + itemSelectionCount.innerHTML = selectedItems.length; + } + } else { + hideSelections(); + } } function onSelectionChange(e) { - updateItemSelection(this, this.checked) + updateItemSelection(this, this.checked); } function showSelection(item, isChecked) { - var itemSelectionPanel = item.querySelector(".itemSelectionPanel"); + + var itemSelectionPanel = item.querySelector('.itemSelectionPanel'); + if (!itemSelectionPanel) { - itemSelectionPanel = document.createElement("div"), itemSelectionPanel.classList.add("itemSelectionPanel"); - var parent = item.querySelector(".cardBox") || item.querySelector(".cardContent"); - parent.classList.add("withMultiSelect"), parent.appendChild(itemSelectionPanel); - var cssClass = "chkItemSelect"; - isChecked && !browser.firefox && (cssClass += " checkedInitial"); - var checkedAttribute = isChecked ? " checked" : ""; - itemSelectionPanel.innerHTML = '"; - itemSelectionPanel.querySelector(".chkItemSelect").addEventListener("change", onSelectionChange) + + itemSelectionPanel = document.createElement('div'); + itemSelectionPanel.classList.add('itemSelectionPanel'); + + var parent = item.querySelector('.cardBox') || item.querySelector('.cardContent'); + parent.classList.add('withMultiSelect'); + parent.appendChild(itemSelectionPanel); + + var cssClass = 'chkItemSelect'; + if (isChecked && !browser.firefox) { + // In firefox, the initial tap hold doesnt' get treated as a click + // In other browsers it does, so we need to make sure that initial click is ignored + cssClass += ' checkedInitial'; + } + var checkedAttribute = isChecked ? ' checked' : ''; + itemSelectionPanel.innerHTML = ''; + var chkItemSelect = itemSelectionPanel.querySelector('.chkItemSelect'); + chkItemSelect.addEventListener('change', onSelectionChange); } } function showSelectionCommands() { + var selectionCommandsPanel = currentSelectionCommandsPanel; + if (!selectionCommandsPanel) { - selectionCommandsPanel = document.createElement("div"), selectionCommandsPanel.classList.add("selectionCommandsPanel"), document.body.appendChild(selectionCommandsPanel), currentSelectionCommandsPanel = selectionCommandsPanel; - var html = ""; - html += '', html += '

'; - html += '', selectionCommandsPanel.innerHTML = html, selectionCommandsPanel.querySelector(".btnCloseSelectionPanel").addEventListener("click", hideSelections); - var btnSelectionPanelOptions = selectionCommandsPanel.querySelector(".btnSelectionPanelOptions"); - dom.addEventListener(btnSelectionPanelOptions, "click", showMenuForSelectedItems, { - passive: !0 - }) + + selectionCommandsPanel = document.createElement('div'); + selectionCommandsPanel.classList.add('selectionCommandsPanel'); + + document.body.appendChild(selectionCommandsPanel); + currentSelectionCommandsPanel = selectionCommandsPanel; + + var html = ''; + + html += ''; + html += '

'; + + var moreIcon = ''; + html += ''; + + selectionCommandsPanel.innerHTML = html; + + selectionCommandsPanel.querySelector('.btnCloseSelectionPanel').addEventListener('click', hideSelections); + + var btnSelectionPanelOptions = selectionCommandsPanel.querySelector('.btnSelectionPanelOptions'); + + dom.addEventListener(btnSelectionPanelOptions, 'click', showMenuForSelectedItems, { passive: true }); } } function alertText(options) { - return new Promise(function(resolve, reject) { - require(["alert"], function(alert) { - alert(options).then(resolve, resolve) - }) - }) + + return new Promise(function (resolve, reject) { + + require(['alert'], function (alert) { + alert(options).then(resolve, resolve); + }); + }); } function deleteItems(apiClient, itemIds) { - return new Promise(function(resolve, reject) { - var msg = globalize.translate("sharedcomponents#ConfirmDeleteItem"), - title = globalize.translate("sharedcomponents#HeaderDeleteItem"); - itemIds.length > 1 && (msg = globalize.translate("sharedcomponents#ConfirmDeleteItems"), title = globalize.translate("sharedcomponents#HeaderDeleteItems")), require(["confirm"], function(confirm) { - confirm(msg, title).then(function() { - var promises = itemIds.map(function(itemId) { - apiClient.deleteItem(itemId) + + return new Promise(function (resolve, reject) { + + var msg = globalize.translate('sharedcomponents#ConfirmDeleteItem'); + var title = globalize.translate('sharedcomponents#HeaderDeleteItem'); + + if (itemIds.length > 1) { + msg = globalize.translate('sharedcomponents#ConfirmDeleteItems'); + title = globalize.translate('sharedcomponents#HeaderDeleteItems'); + } + + require(['confirm'], function (confirm) { + + confirm(msg, title).then(function () { + var promises = itemIds.map(function (itemId) { + apiClient.deleteItem(itemId); }); - Promise.all(promises).then(resolve, function() { - alertText(globalize.translate("sharedcomponents#ErrorDeletingItem")).then(reject, reject) - }) - }, reject) - }) - }) + + Promise.all(promises).then(resolve, function () { + + alertText(globalize.translate('sharedcomponents#ErrorDeletingItem')).then(reject, reject); + }); + }, reject); + + }); + }); } function showMenuForSelectedItems(e) { + var apiClient = connectionManager.currentApiClient(); - apiClient.getCurrentUser().then(function(user) { + + apiClient.getCurrentUser().then(function (user) { + var menuItems = []; + menuItems.push({ - name: globalize.translate("sharedcomponents#AddToCollection"), - id: "addtocollection", - ironIcon: "add" - }), menuItems.push({ - name: globalize.translate("sharedcomponents#AddToPlaylist"), - id: "playlist", - ironIcon: "playlist-add" - }), user.Policy.EnableContentDeletion && menuItems.push({ - name: globalize.translate("sharedcomponents#Delete"), - id: "delete", - ironIcon: "delete" - }), user.Policy.EnableContentDownloading && appHost.supports("filedownload"), user.Policy.EnableContentDownloading && appHost.supports("sync") && menuItems.push({ - name: globalize.translate("sharedcomponents#Download"), - id: "synclocal" - }), menuItems.push({ - name: globalize.translate("sharedcomponents#GroupVersions"), - id: "groupvideos", - ironIcon: "call-merge" - }), menuItems.push({ - name: globalize.translate("sharedcomponents#MarkPlayed"), - id: "markplayed" - }), menuItems.push({ - name: globalize.translate("sharedcomponents#MarkUnplayed"), - id: "markunplayed" - }), menuItems.push({ - name: globalize.translate("sharedcomponents#RefreshMetadata"), - id: "refresh" - }), user.Policy.EnableContentDownloading && menuItems.push({ - name: globalize.translate("sharedcomponents#Sync"), - id: "sync" - }), require(["actionsheet"], function(actionsheet) { + name: globalize.translate('sharedcomponents#AddToCollection'), + id: 'addtocollection', + ironIcon: 'add' + }); + + menuItems.push({ + name: globalize.translate('sharedcomponents#AddToPlaylist'), + id: 'playlist', + ironIcon: 'playlist-add' + }); + + // TODO: Be more dynamic based on what is selected + if (user.Policy.EnableContentDeletion) { + menuItems.push({ + name: globalize.translate('sharedcomponents#Delete'), + id: 'delete', + ironIcon: 'delete' + }); + } + + if (user.Policy.EnableContentDownloading && appHost.supports('filedownload')) { + //items.push({ + // name: Globalize.translate('ButtonDownload'), + // id: 'download', + // ironIcon: 'file-download' + //}); + } + + if (user.Policy.EnableContentDownloading && appHost.supports('sync')) { + menuItems.push({ + name: globalize.translate('sharedcomponents#Download'), + id: 'synclocal' + }); + } + + menuItems.push({ + name: globalize.translate('sharedcomponents#GroupVersions'), + id: 'groupvideos', + ironIcon: 'call-merge' + }); + + menuItems.push({ + name: globalize.translate('sharedcomponents#MarkPlayed'), + id: 'markplayed' + }); + + menuItems.push({ + name: globalize.translate('sharedcomponents#MarkUnplayed'), + id: 'markunplayed' + }); + + menuItems.push({ + name: globalize.translate('sharedcomponents#RefreshMetadata'), + id: 'refresh' + }); + + if (user.Policy.EnableContentDownloading) { + menuItems.push({ + name: globalize.translate('sharedcomponents#Sync'), + id: 'sync' + }); + } + + require(['actionsheet'], function (actionsheet) { + actionsheet.show({ items: menuItems, positionTo: e.target, - callback: function(id) { - var items = selectedItems.slice(0), - serverId = apiClient.serverInfo().Id; + callback: function (id) { + + var items = selectedItems.slice(0); + var serverId = apiClient.serverInfo().Id; + switch (id) { - case "addtocollection": - require(["collectionEditor"], function(collectionEditor) { - (new collectionEditor).show({ + + case 'addtocollection': + require(['collectionEditor'], function (collectionEditor) { + + new collectionEditor().show({ items: items, serverId: serverId - }) - }), hideSelections(), dispatchNeedsRefresh(); + }); + }); + hideSelections(); + dispatchNeedsRefresh(); break; - case "playlist": - require(["playlistEditor"], function(playlistEditor) { - (new playlistEditor).show({ + case 'playlist': + require(['playlistEditor'], function (playlistEditor) { + new playlistEditor().show({ items: items, serverId: serverId - }) - }), hideSelections(), dispatchNeedsRefresh(); + }); + }); + hideSelections(); + dispatchNeedsRefresh(); break; - case "delete": - deleteItems(apiClient, items).then(dispatchNeedsRefresh), hideSelections(), dispatchNeedsRefresh(); + case 'delete': + deleteItems(apiClient, items).then(dispatchNeedsRefresh); + hideSelections(); + dispatchNeedsRefresh(); break; - case "groupvideos": + case 'groupvideos': combineVersions(apiClient, items); break; - case "markplayed": - items.forEach(function(itemId) { - apiClient.markPlayed(apiClient.getCurrentUserId(), itemId) - }), hideSelections(), dispatchNeedsRefresh(); + case 'markplayed': + items.forEach(function (itemId) { + apiClient.markPlayed(apiClient.getCurrentUserId(), itemId); + }); + hideSelections(); + dispatchNeedsRefresh(); break; - case "markunplayed": - items.forEach(function(itemId) { - apiClient.markUnplayed(apiClient.getCurrentUserId(), itemId) - }), hideSelections(), dispatchNeedsRefresh(); + case 'markunplayed': + items.forEach(function (itemId) { + apiClient.markUnplayed(apiClient.getCurrentUserId(), itemId); + }); + hideSelections(); + dispatchNeedsRefresh(); break; - case "refresh": - require(["refreshDialog"], function(refreshDialog) { + case 'refresh': + require(['refreshDialog'], function (refreshDialog) { new refreshDialog({ itemIds: items, serverId: serverId - }).show() - }), hideSelections(), dispatchNeedsRefresh(); + }).show(); + }); + hideSelections(); + dispatchNeedsRefresh(); break; - case "sync": - require(["syncDialog"], function(syncDialog) { + case 'sync': + require(['syncDialog'], function (syncDialog) { syncDialog.showMenu({ - items: items.map(function(i) { + items: items.map(function (i) { return { Id: i - } + }; }), serverId: serverId - }) - }), hideSelections(), dispatchNeedsRefresh(); + }); + }); + hideSelections(); + dispatchNeedsRefresh(); break; - case "synclocal": - require(["syncDialog"], function(syncDialog) { + case 'synclocal': + require(['syncDialog'], function (syncDialog) { syncDialog.showMenu({ - items: items.map(function(i) { + items: items.map(function (i) { return { Id: i - } + }; }), - isLocalSync: !0, + isLocalSync: true, serverId: serverId - }) - }), hideSelections(), dispatchNeedsRefresh() + }); + }); + hideSelections(); + dispatchNeedsRefresh(); + break; + default: + break; } } - }) - }) - }) + }); + + }); + }); } function dispatchNeedsRefresh() { + var elems = []; - [].forEach.call(selectedElements, function(i) { - var container = dom.parentWithAttribute(i, "is", "emby-itemscontainer"); - container && -1 === elems.indexOf(container) && elems.push(container) + + [].forEach.call(selectedElements, function (i) { + + var container = dom.parentWithAttribute(i, 'is', 'emby-itemscontainer'); + + if (container && elems.indexOf(container) === -1) { + elems.push(container); + } }); - for (var i = 0, length = elems.length; i < length; i++) elems[i].notifyRefreshNeeded(!0) + + for (var i = 0, length = elems.length; i < length; i++) { + elems[i].notifyRefreshNeeded(true); + } } function combineVersions(apiClient, selection) { - if (selection.length < 2) return void require(["alert"], function(alert) { - alert({ - text: globalize.translate("sharedcomponents#PleaseSelectTwoItems") - }) - }); - loading.show(), apiClient.ajax({ + + if (selection.length < 2) { + + require(['alert'], function (alert) { + alert({ + text: globalize.translate('sharedcomponents#PleaseSelectTwoItems') + }); + }); + return; + } + + loading.show(); + + apiClient.ajax({ + type: "POST", - url: apiClient.getUrl("Videos/MergeVersions", { - Ids: selection.join(",") - }) - }).then(function() { - loading.hide(), hideSelections(), dispatchNeedsRefresh() - }) + url: apiClient.getUrl("Videos/MergeVersions", { Ids: selection.join(',') }) + + }).then(function () { + + loading.hide(); + hideSelections(); + dispatchNeedsRefresh(); + }); } function showSelections(initialCard) { - require(["emby-checkbox"], function() { - for (var cards = document.querySelectorAll(".card"), i = 0, length = cards.length; i < length; i++) showSelection(cards[i], initialCard === cards[i]); - showSelectionCommands(), updateItemSelection(initialCard, !0) - }) + + require(['emby-checkbox'], function () { + var cards = document.querySelectorAll('.card'); + for (var i = 0, length = cards.length; i < length; i++) { + showSelection(cards[i], initialCard === cards[i]); + } + + showSelectionCommands(); + updateItemSelection(initialCard, true); + }); } function onContainerClick(e) { + var target = e.target; + if (selectedItems.length) { - var card = dom.parentWithClass(target, "card"); + + var card = dom.parentWithClass(target, 'card'); if (card) { - var itemSelectionPanel = card.querySelector(".itemSelectionPanel"); - if (itemSelectionPanel) return onItemSelectionPanelClick(e, itemSelectionPanel) + var itemSelectionPanel = card.querySelector('.itemSelectionPanel'); + if (itemSelectionPanel) { + return onItemSelectionPanelClick(e, itemSelectionPanel); + } } - return e.preventDefault(), e.stopPropagation(), !1 + + e.preventDefault(); + e.stopPropagation(); + return false; } } - var currentSelectionCommandsPanel, selectedItems = [], - selectedElements = []; - return document.addEventListener("viewbeforehide", hideSelections), - function(options) { - function onTapHold(e) { - var card = dom.parentWithClass(e.target, "card"); - return card && showSelections(card), e.preventDefault(), e.stopPropagation && e.stopPropagation(), !1 + + document.addEventListener('viewbeforehide', hideSelections); + + return function (options) { + + var self = this; + + var container = options.container; + + function onTapHold(e) { + + var card = dom.parentWithClass(e.target, 'card'); + + if (card) { + + showSelections(card); } - function getTouches(e) { - return e.changedTouches || e.targetTouches || e.touches + e.preventDefault(); + // It won't have this if it's a hammer event + if (e.stopPropagation) { + e.stopPropagation(); } + return false; + } - function onTouchStart(e) { - var touch = getTouches(e)[0]; - if (touchTarget = null, touchStartX = 0, touchStartY = 0, touch) { - touchStartX = touch.clientX, touchStartY = touch.clientY; - var element = touch.target; - if (element) { - var card = dom.parentWithClass(element, "card"); - card && (touchStartTimeout && (clearTimeout(touchStartTimeout), touchStartTimeout = null), touchTarget = card, touchStartTimeout = setTimeout(onTouchStartTimerFired, 550)) + function getTouches(e) { + + return e.changedTouches || e.targetTouches || e.touches; + } + + var touchTarget; + var touchStartTimeout; + var touchStartX; + var touchStartY; + function onTouchStart(e) { + + var touch = getTouches(e)[0]; + touchTarget = null; + touchStartX = 0; + touchStartY = 0; + + if (touch) { + touchStartX = touch.clientX; + touchStartY = touch.clientY; + var element = touch.target; + + if (element) { + var card = dom.parentWithClass(element, 'card'); + + if (card) { + + if (touchStartTimeout) { + clearTimeout(touchStartTimeout); + touchStartTimeout = null; + } + + touchTarget = card; + touchStartTimeout = setTimeout(onTouchStartTimerFired, 550); } } } + } - function onTouchMove(e) { - if (touchTarget) { - var deltaX, deltaY, touch = getTouches(e)[0]; - if (touch) { - var touchEndX = touch.clientX || 0, - touchEndY = touch.clientY || 0; - deltaX = Math.abs(touchEndX - (touchStartX || 0)), deltaY = Math.abs(touchEndY - (touchStartY || 0)) - } else deltaX = 100, deltaY = 100; - (deltaX >= 5 || deltaY >= 5) && onMouseOut(e) + function onTouchMove(e) { + + if (touchTarget) { + var touch = getTouches(e)[0]; + var deltaX; + var deltaY; + + if (touch) { + var touchEndX = touch.clientX || 0; + var touchEndY = touch.clientY || 0; + deltaX = Math.abs(touchEndX - (touchStartX || 0)); + deltaY = Math.abs(touchEndY - (touchStartY || 0)); + } else { + deltaX = 100; + deltaY = 100; } - } - - function onTouchEnd(e) { - onMouseOut(e) - } - - function onMouseDown(e) { - touchStartTimeout && (clearTimeout(touchStartTimeout), touchStartTimeout = null), touchTarget = e.target, touchStartTimeout = setTimeout(onTouchStartTimerFired, 550) - } - - function onMouseOut(e) { - touchStartTimeout && (clearTimeout(touchStartTimeout), touchStartTimeout = null), touchTarget = null - } - - function onTouchStartTimerFired() { - if (touchTarget) { - var card = dom.parentWithClass(touchTarget, "card"); - touchTarget = null, card && showSelections(card) + if (deltaX >= 5 || deltaY >= 5) { + onMouseOut(e); } } - var touchTarget, touchStartTimeout, touchStartX, touchStartY, self = this, - container = options.container; - ! function(element) { - browser.touch && !browser.safari ? element.addEventListener("contextmenu", onTapHold) : (dom.addEventListener(element, "touchstart", onTouchStart, { - passive: !0 - }), dom.addEventListener(element, "touchmove", onTouchMove, { - passive: !0 - }), dom.addEventListener(element, "touchend", onTouchEnd, { - passive: !0 - }), dom.addEventListener(element, "touchcancel", onTouchEnd, { - passive: !0 - }), dom.addEventListener(element, "mousedown", onMouseDown, { - passive: !0 - }), dom.addEventListener(element, "mouseleave", onMouseOut, { - passive: !0 - }), dom.addEventListener(element, "mouseup", onMouseOut, { - passive: !0 - })) - }(container), !1 !== options.bindOnClick && container.addEventListener("click", onContainerClick), self.onContainerClick = onContainerClick, self.destroy = function() { - container.removeEventListener("click", onContainerClick), container.removeEventListener("contextmenu", onTapHold); - var element = container; - dom.removeEventListener(element, "touchstart", onTouchStart, { - passive: !0 - }), dom.removeEventListener(element, "touchmove", onTouchMove, { - passive: !0 - }), dom.removeEventListener(element, "touchend", onTouchEnd, { - passive: !0 - }), dom.removeEventListener(element, "mousedown", onMouseDown, { - passive: !0 - }), dom.removeEventListener(element, "mouseleave", onMouseOut, { - passive: !0 - }), dom.removeEventListener(element, "mouseup", onMouseOut, { - passive: !0 - }) - } } + + function onTouchEnd(e) { + + onMouseOut(e); + } + + function onMouseDown(e) { + + if (touchStartTimeout) { + clearTimeout(touchStartTimeout); + touchStartTimeout = null; + } + + touchTarget = e.target; + touchStartTimeout = setTimeout(onTouchStartTimerFired, 550); + } + + function onMouseOut(e) { + + if (touchStartTimeout) { + clearTimeout(touchStartTimeout); + touchStartTimeout = null; + } + touchTarget = null; + } + + function onTouchStartTimerFired() { + + if (!touchTarget) { + return; + } + + var card = dom.parentWithClass(touchTarget, 'card'); + touchTarget = null; + + if (card) { + + showSelections(card); + } + } + + function initTapHold(element) { + + // mobile safari doesn't allow contextmenu override + if (browser.touch && !browser.safari) { + element.addEventListener('contextmenu', onTapHold); + } else { + dom.addEventListener(element, 'touchstart', onTouchStart, { + passive: true + }); + dom.addEventListener(element, 'touchmove', onTouchMove, { + passive: true + }); + dom.addEventListener(element, 'touchend', onTouchEnd, { + passive: true + }); + dom.addEventListener(element, 'touchcancel', onTouchEnd, { + passive: true + }); + dom.addEventListener(element, 'mousedown', onMouseDown, { + passive: true + }); + dom.addEventListener(element, 'mouseleave', onMouseOut, { + passive: true + }); + dom.addEventListener(element, 'mouseup', onMouseOut, { + passive: true + }); + } + } + + initTapHold(container); + + if (options.bindOnClick !== false) { + container.addEventListener('click', onContainerClick); + } + + self.onContainerClick = onContainerClick; + + self.destroy = function () { + + container.removeEventListener('click', onContainerClick); + container.removeEventListener('contextmenu', onTapHold); + + var element = container; + + dom.removeEventListener(element, 'touchstart', onTouchStart, { + passive: true + }); + dom.removeEventListener(element, 'touchmove', onTouchMove, { + passive: true + }); + dom.removeEventListener(element, 'touchend', onTouchEnd, { + passive: true + }); + // this fires in safari due to magnifying class + //dom.removeEventListener(element, 'touchcancel', onTouchEnd, { + // passive: true + //}); + dom.removeEventListener(element, 'mousedown', onMouseDown, { + passive: true + }); + dom.removeEventListener(element, 'mouseleave', onMouseOut, { + passive: true + }); + dom.removeEventListener(element, 'mouseup', onMouseOut, { + passive: true + }); + }; + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/native-promise-only/lib/npo.src.js b/src/bower_components/emby-webcomponents/native-promise-only/lib/npo.src.js index 888d602450..1c8cb07867 100644 --- a/src/bower_components/emby-webcomponents/native-promise-only/lib/npo.src.js +++ b/src/bower_components/emby-webcomponents/native-promise-only/lib/npo.src.js @@ -1,166 +1,373 @@ -! function(name, context, definition) { - context[name] = definition(), "undefined" != typeof module && module.exports ? module.exports = context[name] : "function" == typeof define && define.amd && define(function() { - return context[name] - }) -}("Promise", "undefined" != typeof global ? global : this, function() { - "use strict"; +/*! Native Promise Only + v0.8.0-a (c) Kyle Simpson + MIT License: http://getify.mit-license.org +*/ - function schedule(fn, self) { - scheduling_queue.add(fn, self), cycle || (cycle = timer(scheduling_queue.drain)) - } +(function UMD(name,context,definition){ + // special form of UMD for polyfilling across evironments + context[name] = definition(); + if (typeof module != "undefined" && module.exports) { module.exports = context[name]; } + else if (typeof define == "function" && define.amd) { define(function $AMD$(){ return context[name]; }); } +})("Promise",typeof global != "undefined" ? global : this,function DEF(){ + /*jshint validthis:true */ + "use strict"; - function isThenable(o) { - var _then, o_type = typeof o; - return null == o || "object" != o_type && "function" != o_type || (_then = o.then), "function" == typeof _then && _then - } + var builtInProp, cycle, scheduling_queue, + ToString = Object.prototype.toString, + timer = (typeof setImmediate != "undefined") ? + function timer(fn) { return setImmediate(fn); } : + setTimeout + ; - function notify() { - for (var i = 0; i < this.chain.length; i++) notifyIsolated(this, 1 === this.state ? this.chain[i].success : this.chain[i].failure, this.chain[i]); - this.chain.length = 0 - } + // dammit, IE8. + try { + Object.defineProperty({},"x",{}); + builtInProp = function builtInProp(obj,name,val,config) { + return Object.defineProperty(obj,name,{ + value: val, + writable: true, + configurable: config !== false + }); + }; + } + catch (err) { + builtInProp = function builtInProp(obj,name,val) { + obj[name] = val; + return obj; + }; + } - function notifyIsolated(self, cb, chain) { - var ret, _then; - try { - !1 === cb ? chain.reject(self.msg) : (ret = !0 === cb ? self.msg : cb.call(void 0, self.msg), ret === chain.promise ? chain.reject(TypeError("Promise-chain cycle")) : (_then = isThenable(ret)) ? _then.call(ret, chain.resolve, chain.reject) : chain.resolve(ret)) - } catch (err) { - chain.reject(err) - } - } + // Note: using a queue instead of array for efficiency + scheduling_queue = (function Queue() { + var first, last, item; - function resolve(msg) { - var _then, self = this; - if (!self.triggered) { - self.triggered = !0, self.def && (self = self.def); - try { - (_then = isThenable(msg)) ? schedule(function() { - var def_wrapper = new MakeDefWrapper(self); - try { - _then.call(msg, function() { - resolve.apply(def_wrapper, arguments) - }, function() { - reject.apply(def_wrapper, arguments) - }) - } catch (err) { - reject.call(def_wrapper, err) - } - }): (self.msg = msg, self.state = 1, self.chain.length > 0 && schedule(notify, self)) - } catch (err) { - reject.call(new MakeDefWrapper(self), err) - } - } - } + function Item(fn,self) { + this.fn = fn; + this.self = self; + this.next = void 0; + } - function reject(msg) { - var self = this; - self.triggered || (self.triggered = !0, self.def && (self = self.def), self.msg = msg, self.state = 2, self.chain.length > 0 && schedule(notify, self)) - } + return { + add: function add(fn,self) { + item = new Item(fn,self); + if (last) { + last.next = item; + } + else { + first = item; + } + last = item; + item = void 0; + }, + drain: function drain() { + var f = first; + first = last = cycle = void 0; - function iteratePromises(Constructor, arr, resolver, rejecter) { - for (var idx = 0; idx < arr.length; idx++) ! function(idx) { - Constructor.resolve(arr[idx]).then(function(msg) { - resolver(idx, msg) - }, rejecter) - }(idx) - } + while (f) { + f.fn.call(f.self); + f = f.next; + } + } + }; + })(); - function MakeDefWrapper(self) { - this.def = self, this.triggered = !1 - } + function schedule(fn,self) { + scheduling_queue.add(fn,self); + if (!cycle) { + cycle = timer(scheduling_queue.drain); + } + } - function MakeDef(self) { - this.promise = self, this.state = 0, this.triggered = !1, this.chain = [], this.msg = void 0 - } + // promise duck typing + function isThenable(o) { + var _then, o_type = typeof o; - function Promise(executor) { - if ("function" != typeof executor) throw TypeError("Not a function"); - if (0 !== this.__NPO__) throw TypeError("Not a promise"); - this.__NPO__ = 1; - var def = new MakeDef(this); - this.then = function(success, failure) { - var o = { - success: "function" != typeof success || success, - failure: "function" == typeof failure && failure - }; - return o.promise = new this.constructor(function(resolve, reject) { - if ("function" != typeof resolve || "function" != typeof reject) throw TypeError("Not a function"); - o.resolve = resolve, o.reject = reject - }), def.chain.push(o), 0 !== def.state && schedule(notify, def), o.promise - }, this.catch = function(failure) { - return this.then(void 0, failure) - }; - try { - executor.call(void 0, function(msg) { - resolve.call(def, msg) - }, function(msg) { - reject.call(def, msg) - }) - } catch (err) { - reject.call(def, err) - } - } - var builtInProp, cycle, scheduling_queue, ToString = Object.prototype.toString, - timer = "undefined" != typeof setImmediate ? function(fn) { - return setImmediate(fn) - } : setTimeout; - try { - Object.defineProperty({}, "x", {}), builtInProp = function(obj, name, val, config) { - return Object.defineProperty(obj, name, { - value: val, - writable: !0, - configurable: !1 !== config - }) - } - } catch (err) { - builtInProp = function(obj, name, val) { - return obj[name] = val, obj - } - } - scheduling_queue = function() { - function Item(fn, self) { - this.fn = fn, this.self = self, this.next = void 0 - } - var first, last, item; - return { - add: function(fn, self) { - item = new Item(fn, self), last ? last.next = item : first = item, last = item, item = void 0 - }, - drain: function() { - var f = first; - for (first = last = cycle = void 0; f;) f.fn.call(f.self), f = f.next - } - } - }(); - var PromisePrototype = builtInProp({}, "constructor", Promise, !1); - return Promise.prototype = PromisePrototype, builtInProp(PromisePrototype, "__NPO__", 0, !1), builtInProp(Promise, "resolve", function(msg) { - var Constructor = this; - return msg && "object" == typeof msg && 1 === msg.__NPO__ ? msg : new Constructor(function(resolve, reject) { - if ("function" != typeof resolve || "function" != typeof reject) throw TypeError("Not a function"); - resolve(msg) - }) - }), builtInProp(Promise, "reject", function(msg) { - return new this(function(resolve, reject) { - if ("function" != typeof resolve || "function" != typeof reject) throw TypeError("Not a function"); - reject(msg) - }) - }), builtInProp(Promise, "all", function(arr) { - var Constructor = this; - return "[object Array]" != ToString.call(arr) ? Constructor.reject(TypeError("Not an array")) : 0 === arr.length ? Constructor.resolve([]) : new Constructor(function(resolve, reject) { - if ("function" != typeof resolve || "function" != typeof reject) throw TypeError("Not a function"); - var len = arr.length, - msgs = Array(len), - count = 0; - iteratePromises(Constructor, arr, function(idx, msg) { - msgs[idx] = msg, ++count === len && resolve(msgs) - }, reject) - }) - }), builtInProp(Promise, "race", function(arr) { - var Constructor = this; - return "[object Array]" != ToString.call(arr) ? Constructor.reject(TypeError("Not an array")) : new Constructor(function(resolve, reject) { - if ("function" != typeof resolve || "function" != typeof reject) throw TypeError("Not a function"); - iteratePromises(Constructor, arr, function(idx, msg) { - resolve(msg) - }, reject) - }) - }), Promise -}); \ No newline at end of file + if (o != null && + ( + o_type == "object" || o_type == "function" + ) + ) { + _then = o.then; + } + return typeof _then == "function" ? _then : false; + } + + function notify() { + for (var i=0; i 0) { + schedule(notify,self); + } + } + } + catch (err) { + reject.call(new MakeDefWrapper(self),err); + } + } + + function reject(msg) { + var self = this; + + // already triggered? + if (self.triggered) { return; } + + self.triggered = true; + + // unwrap + if (self.def) { + self = self.def; + } + + self.msg = msg; + self.state = 2; + if (self.chain.length > 0) { + schedule(notify,self); + } + } + + function iteratePromises(Constructor,arr,resolver,rejecter) { + for (var idx=0; idx 12 && (newItems.length = 12), apiClient.getItems(apiClient.getCurrentUserId(), { - Recursive: !0, + + if (!newItems.length) { + return; + } + + // Don't put a massive number of Id's onto the query string + if (newItems.length > 12) { + newItems.length = 12; + } + + apiClient.getItems(apiClient.getCurrentUserId(), { + + Recursive: true, Limit: 3, Filters: "IsNotFolder", SortBy: "DateCreated", SortOrder: "Descending", - Ids: newItems.join(","), + Ids: newItems.join(','), MediaTypes: "Audio,Video", - EnableTotalRecordCount: !1 - }).then(function(result) { - for (var items = result.Items, i = 0, length = items.length; i < length; i++) showNewItemNotification(items[i], apiClient) - })) + EnableTotalRecordCount: false + + }).then(function (result) { + + var items = result.Items; + + for (var i = 0, length = items.length ; i < length; i++) { + + showNewItemNotification(items[i], apiClient); + } + }); } function getIconUrl(name) { - return name = name || "notificationicon.png", require.toUrl(".").split("?")[0] + "/" + name + + name = name || 'notificationicon.png'; + + return require.toUrl('.').split('?')[0] + '/' + name; } function showPackageInstallNotification(apiClient, installation, status) { - apiClient.getCurrentUser().then(function(user) { - if (user.Policy.IsAdministrator) { - var notification = { - tag: "install" + installation.Id, - data: {} - }; - if ("completed" === status ? (notification.title = globalize.translate("sharedcomponents#PackageInstallCompleted").replace("{0}", installation.Name + " " + installation.Version), notification.vibrate = !0) : "cancelled" === status ? notification.title = globalize.translate("sharedcomponents#PackageInstallCancelled").replace("{0}", installation.Name + " " + installation.Version) : "failed" === status ? (notification.title = globalize.translate("sharedcomponents#PackageInstallFailed").replace("{0}", installation.Name + " " + installation.Version), notification.vibrate = !0) : "progress" === status && (notification.title = globalize.translate("sharedcomponents#InstallingPackage").replace("{0}", installation.Name + " " + installation.Version), notification.actions = [{ - action: "cancel-install", - title: globalize.translate("sharedcomponents#ButtonCancel"), - icon: getIconUrl() - }], notification.data.id = installation.id), "progress" === status) { - var percentComplete = Math.round(installation.PercentComplete || 0); - notification.body = percentComplete + "% complete." - } - showNotification(notification, "cancelled" === status ? 5e3 : 0, apiClient) + + apiClient.getCurrentUser().then(function (user) { + + if (!user.Policy.IsAdministrator) { + return; } - }) - } - document.addEventListener("click", onOneDocumentClick), document.addEventListener("keydown", onOneDocumentClick); - var serviceWorkerRegistration; - resetRegistration(), events.on(serverNotifications, "LibraryChanged", function(e, apiClient, data) { - onLibraryChanged(data, apiClient) - }), events.on(serverNotifications, "PackageInstallationCompleted", function(e, apiClient, data) { - showPackageInstallNotification(apiClient, data, "completed") - }), events.on(serverNotifications, "PackageInstallationFailed", function(e, apiClient, data) { - showPackageInstallNotification(apiClient, data, "failed") - }), events.on(serverNotifications, "PackageInstallationCancelled", function(e, apiClient, data) { - showPackageInstallNotification(apiClient, data, "cancelled") - }), events.on(serverNotifications, "PackageInstalling", function(e, apiClient, data) { - showPackageInstallNotification(apiClient, data, "progress") - }), events.on(serverNotifications, "ServerShuttingDown", function(e, apiClient, data) { - showNotification({ - tag: "restart" + apiClient.serverInfo().Id, - title: globalize.translate("sharedcomponents#ServerNameIsShuttingDown", apiClient.serverInfo().Name) - }, 0, apiClient) - }), events.on(serverNotifications, "ServerRestarting", function(e, apiClient, data) { - showNotification({ - tag: "restart" + apiClient.serverInfo().Id, - title: globalize.translate("sharedcomponents#ServerNameIsRestarting", apiClient.serverInfo().Name) - }, 0, apiClient) - }), events.on(serverNotifications, "RestartRequired", function(e, apiClient) { - var serverId = apiClient.serverInfo().Id, - notification = { - tag: "restart" + serverId, - title: globalize.translate("sharedcomponents#PleaseRestartServerName", apiClient.serverInfo().Name) + + var notification = { + tag: "install" + installation.Id, + data: {} }; - notification.actions = [{ - action: "restart", - title: globalize.translate("sharedcomponents#ButtonRestart"), - icon: getIconUrl() - }], showNotification(notification, 0, apiClient) - }) + + if (status === 'completed') { + notification.title = globalize.translate('sharedcomponents#PackageInstallCompleted').replace('{0}', installation.Name + ' ' + installation.Version); + notification.vibrate = true; + } + else if (status === 'cancelled') { + notification.title = globalize.translate('sharedcomponents#PackageInstallCancelled').replace('{0}', installation.Name + ' ' + installation.Version); + } + else if (status === 'failed') { + notification.title = globalize.translate('sharedcomponents#PackageInstallFailed').replace('{0}', installation.Name + ' ' + installation.Version); + notification.vibrate = true; + } + else if (status === 'progress') { + notification.title = globalize.translate('sharedcomponents#InstallingPackage').replace('{0}', installation.Name + ' ' + installation.Version); + + notification.actions = + [ + { + action: 'cancel-install', + title: globalize.translate('sharedcomponents#ButtonCancel'), + icon: getIconUrl() + } + ]; + + notification.data.id = installation.id; + } + + if (status === 'progress') { + + var percentComplete = Math.round(installation.PercentComplete || 0); + + notification.body = percentComplete + '% complete.'; + } + + var timeout = status === 'cancelled' ? 5000 : 0; + + showNotification(notification, timeout, apiClient); + }); + } + + events.on(serverNotifications, 'LibraryChanged', function (e, apiClient, data) { + onLibraryChanged(data, apiClient); + }); + + events.on(serverNotifications, 'PackageInstallationCompleted', function (e, apiClient, data) { + showPackageInstallNotification(apiClient, data, "completed"); + }); + + events.on(serverNotifications, 'PackageInstallationFailed', function (e, apiClient, data) { + showPackageInstallNotification(apiClient, data, "failed"); + }); + + events.on(serverNotifications, 'PackageInstallationCancelled', function (e, apiClient, data) { + showPackageInstallNotification(apiClient, data, "cancelled"); + }); + + events.on(serverNotifications, 'PackageInstalling', function (e, apiClient, data) { + showPackageInstallNotification(apiClient, data, "progress"); + }); + + events.on(serverNotifications, 'ServerShuttingDown', function (e, apiClient, data) { + var serverId = apiClient.serverInfo().Id; + var notification = { + tag: "restart" + serverId, + title: globalize.translate('sharedcomponents#ServerNameIsShuttingDown', apiClient.serverInfo().Name) + }; + showNotification(notification, 0, apiClient); + }); + + events.on(serverNotifications, 'ServerRestarting', function (e, apiClient, data) { + var serverId = apiClient.serverInfo().Id; + var notification = { + tag: "restart" + serverId, + title: globalize.translate('sharedcomponents#ServerNameIsRestarting', apiClient.serverInfo().Name) + }; + showNotification(notification, 0, apiClient); + }); + + events.on(serverNotifications, 'RestartRequired', function (e, apiClient) { + + var serverId = apiClient.serverInfo().Id; + var notification = { + tag: "restart" + serverId, + title: globalize.translate('sharedcomponents#PleaseRestartServerName', apiClient.serverInfo().Name) + }; + + notification.actions = + [ + { + action: 'restart', + title: globalize.translate('sharedcomponents#ButtonRestart'), + icon: getIconUrl() + } + ]; + + showNotification(notification, 0, apiClient); + }); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/nowplayingbar/nowplayingbar.css b/src/bower_components/emby-webcomponents/nowplayingbar/nowplayingbar.css index 1d799ff06d..1a6972e049 100644 --- a/src/bower_components/emby-webcomponents/nowplayingbar/nowplayingbar.css +++ b/src/bower_components/emby-webcomponents/nowplayingbar/nowplayingbar.css @@ -1,103 +1,74 @@ -.nowPlayingBarInfoContainer { - display: -webkit-box; - display: -webkit-flex; +.nowPlayingBarInfoContainer { display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; height: 100%; - -webkit-box-flex: 1; - -webkit-flex-grow: 1; flex-grow: 1; - overflow: hidden + overflow: hidden; } +/* Now playing bar */ .nowPlayingBar { + /* Above everything, except for the video player and popup overlays */ text-align: center; will-change: transform; contain: layout style; - -webkit-transition: -webkit-transform .2s ease-out; - -o-transition: transform .2s ease-out; - transition: transform .2s ease-out + transition: transform 200ms ease-out; } .nowPlayingBar-hidden { - -webkit-transform: translate3d(0, 100%, 0); - transform: translate3d(0, 100%, 0) + transform: translate3d(0,100%,0); } .nowPlayingBarTop { - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -webkit-flex-direction: row; flex-direction: row; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; height: 4.2em; position: relative; - -webkit-box-pack: center; - -webkit-justify-content: center; - justify-content: center + justify-content: center; } -.mediaButton, -.nowPlayingBarUserDataButtons .btnUserItemRating { +.mediaButton, .nowPlayingBarUserDataButtons .btnUserItemRating { vertical-align: middle; margin: 0; - text-align: center + text-align: center; } .mediaButton { - font-size: 120% + font-size: 120%; } .nowPlayingBar .nowPlayingImage { background-position: center center; background-repeat: no-repeat; - -webkit-background-size: contain; background-size: contain; height: 70%; width: 4.2em; - -webkit-flex-shrink: 0; - flex-shrink: 0 + flex-shrink: 0; } .nowPlayingBarText { overflow: hidden; white-space: nowrap; - -o-text-overflow: ellipsis; text-overflow: ellipsis; vertical-align: middle; text-align: left; - -webkit-box-flex: 1; - -webkit-flex-grow: 1; flex-grow: 1; font-size: 92%; margin-right: 2.4em; - margin-left: 1em + margin-left: 1em; } .nowPlayingBarCenter { vertical-align: middle; text-align: center; + /* Need this to make sure it's on top of nowPlayingBarPositionContainer so that buttons are fully clickable */ z-index: 2; - -webkit-box-flex: 1; - -webkit-flex-grow: 1; flex-grow: 1; - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; - position: absolute + position: absolute; } .nowPlayingBarPositionContainer { @@ -105,81 +76,81 @@ left: 0; top: -.56em; right: 0; - z-index: 1 + z-index: 1; +} + +.headroom--unpinned .nowPlayingBarPositionContainer { + display: none; } -.headroom--unpinned .nowPlayingBarPositionContainer, .noMediaProgress .nowPlayingBarPositionContainer { - display: none + display: none; } .nowPlayingBarRight { position: relative; margin: 0 .5em 0 auto; + /* Need this to make sure it's on top of nowPlayingBarPositionContainer so that buttons are fully clickable */ z-index: 2; - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - -webkit-flex-shrink: 0; - flex-shrink: 0 + flex-shrink: 0; } .nowPlayingBarCurrentTime { vertical-align: middle; text-align: center; display: inline-block; - padding-left: 1.5em + padding-left: 1.5em; } .nowPlayingBarVolumeSliderContainer { - margin-right: 2em + margin-right: 2em; } .nowPlayingBarUserDataButtons { display: inline-block; margin-left: 1em; - margin-right: 1em + margin-right: 1em; } .nowPlayingBarPositionSlider::-webkit-slider-thumb { width: 1.2em !important; - height: 1.2em !important + height: 1.2em !important; } -@media all and (max-width:87.5em) { +@media all and (max-width: 87.5em) { + .nowPlayingBarUserDataButtons { - display: none + display: none; } } -@media all and (max-width:68.75em) { +@media all and (max-width: 68.75em) { - .nowPlayingBar .muteButton, - .nowPlayingBar .unmuteButton, - .nowPlayingBarVolumeSliderContainer { - display: none !important + .nowPlayingBarVolumeSliderContainer, .nowPlayingBar .muteButton, .nowPlayingBar .unmuteButton { + display: none !important; } } -@media all and (max-width:50em) { +@media all and (max-width: 50em) { + .nowPlayingBarCenter { - display: none !important + display: none !important; } .toggleRepeatButton { - display: none + display: none; } } -@media all and (min-width:50em) { +@media all and (min-width: 50em) { + .nowPlayingBarRight .playPauseButton { - display: none + display: none; } .nowPlayingBarInfoContainer { - max-width: 40% + max-width: 40%; } -} \ No newline at end of file +} diff --git a/src/bower_components/emby-webcomponents/nowplayingbar/nowplayingbar.js b/src/bower_components/emby-webcomponents/nowplayingbar/nowplayingbar.js index feded12a2a..3d55a342e5 100644 --- a/src/bower_components/emby-webcomponents/nowplayingbar/nowplayingbar.js +++ b/src/bower_components/emby-webcomponents/nowplayingbar/nowplayingbar.js @@ -1,270 +1,782 @@ -define(["require", "datetime", "itemHelper", "events", "browser", "imageLoader", "layoutManager", "playbackManager", "nowPlayingHelper", "apphost", "dom", "connectionManager", "paper-icon-button-light", "emby-ratingbutton"], function(require, datetime, itemHelper, events, browser, imageLoader, layoutManager, playbackManager, nowPlayingHelper, appHost, dom, connectionManager) { - "use strict"; +define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', 'layoutManager', 'playbackManager', 'nowPlayingHelper', 'apphost', 'dom', 'connectionManager', 'paper-icon-button-light', 'emby-ratingbutton'], function (require, datetime, itemHelper, events, browser, imageLoader, layoutManager, playbackManager, nowPlayingHelper, appHost, dom, connectionManager) { + 'use strict'; + + var currentPlayer; + var currentPlayerSupportedCommands = []; + + var currentTimeElement; + var nowPlayingImageElement; + var nowPlayingTextElement; + var nowPlayingUserData; + var muteButton; + var volumeSlider; + var volumeSliderContainer; + var playPauseButtons; + var positionSlider; + var toggleRepeatButton; + var toggleRepeatButtonIcon; + + var lastUpdateTime = 0; + var lastPlayerState = {}; + var isEnabled; + var currentRuntimeTicks = 0; + + var isVisibilityAllowed = true; function getNowPlayingBarHtml() { - var html = ""; - return html += '
', html += '
', html += '
', html += '', html += "
", html += '
', html += '
', html += '
', html += "
", html += '
', html += '', html += '', html += '', html += '', html += '
', html += "
", html += '
', html += '', html += '
', html += '', html += "
", html += '', html += '
', html += "
", html += '', html += '', html += "
", html += "
", html += "
" + + var html = ''; + + html += '
'; + + html += '
'; + html += '
'; + html += ''; + html += '
'; + + html += '
'; + html += '
'; + html += '
'; + html += '
'; + + // The onclicks are needed due to the return false above + html += '
'; + + html += ''; + + html += ''; + + html += ''; + html += ''; + + html += '
'; + html += '
'; + + html += '
'; + + html += ''; + + html += '
'; + html += ''; + html += '
'; + + html += ''; + + html += '
'; + html += '
'; + + html += ''; + html += ''; + + html += '
'; + html += '
'; + + html += '
'; + + return html; } function onSlideDownComplete() { - this.classList.add("hide") + + this.classList.add('hide'); } function slideDown(elem) { - elem.offsetWidth, elem.classList.add("nowPlayingBar-hidden"), dom.addEventListener(elem, dom.whichTransitionEvent(), onSlideDownComplete, { - once: !0 - }) + + // trigger reflow + void elem.offsetWidth; + + elem.classList.add('nowPlayingBar-hidden'); + + dom.addEventListener(elem, dom.whichTransitionEvent(), onSlideDownComplete, { + once: true + }); } function slideUp(elem) { + dom.removeEventListener(elem, dom.whichTransitionEvent(), onSlideDownComplete, { - once: !0 - }), elem.classList.remove("hide"), elem.offsetWidth, elem.classList.remove("nowPlayingBar-hidden") + once: true + }); + + elem.classList.remove('hide'); + + // trigger reflow + void elem.offsetWidth; + + elem.classList.remove('nowPlayingBar-hidden'); } function onPlayPauseClick() { - playbackManager.playPause(currentPlayer) + playbackManager.playPause(currentPlayer); } function bindEvents(elem) { - currentTimeElement = elem.querySelector(".nowPlayingBarCurrentTime"), nowPlayingImageElement = elem.querySelector(".nowPlayingImage"), nowPlayingTextElement = elem.querySelector(".nowPlayingBarText"), nowPlayingUserData = elem.querySelector(".nowPlayingBarUserDataButtons"), muteButton = elem.querySelector(".muteButton"), muteButton.addEventListener("click", function() { - currentPlayer && playbackManager.toggleMute(currentPlayer) - }), elem.querySelector(".stopButton").addEventListener("click", function() { - currentPlayer && playbackManager.stop(currentPlayer) - }); - var i, length; - for (playPauseButtons = elem.querySelectorAll(".playPauseButton"), i = 0, length = playPauseButtons.length; i < length; i++) playPauseButtons[i].addEventListener("click", onPlayPauseClick); - elem.querySelector(".nextTrackButton").addEventListener("click", function() { - currentPlayer && playbackManager.nextTrack(currentPlayer) - }), elem.querySelector(".previousTrackButton").addEventListener("click", function() { - currentPlayer && playbackManager.previousTrack(currentPlayer) - }), elem.querySelector(".remoteControlButton").addEventListener("click", showRemoteControl), toggleRepeatButton = elem.querySelector(".toggleRepeatButton"), toggleRepeatButton.addEventListener("click", function() { - if (currentPlayer) switch (playbackManager.getRepeatMode(currentPlayer)) { - case "RepeatAll": - playbackManager.setRepeatMode("RepeatOne", currentPlayer); - break; - case "RepeatOne": - playbackManager.setRepeatMode("RepeatNone", currentPlayer); - break; - default: - playbackManager.setRepeatMode("RepeatAll", currentPlayer) - } - }), toggleRepeatButtonIcon = toggleRepeatButton.querySelector("i"), volumeSlider = elem.querySelector(".nowPlayingBarVolumeSlider"), volumeSliderContainer = elem.querySelector(".nowPlayingBarVolumeSliderContainer"), appHost.supports("physicalvolumecontrol") ? volumeSliderContainer.classList.add("hide") : volumeSliderContainer.classList.remove("hide"), volumeSlider.addEventListener("change", function() { - currentPlayer && currentPlayer.setVolume(this.value) - }), positionSlider = elem.querySelector(".nowPlayingBarPositionSlider"), positionSlider.addEventListener("change", function() { + + currentTimeElement = elem.querySelector('.nowPlayingBarCurrentTime'); + nowPlayingImageElement = elem.querySelector('.nowPlayingImage'); + nowPlayingTextElement = elem.querySelector('.nowPlayingBarText'); + nowPlayingUserData = elem.querySelector('.nowPlayingBarUserDataButtons'); + + muteButton = elem.querySelector('.muteButton'); + muteButton.addEventListener('click', function () { + if (currentPlayer) { - var newPercent = parseFloat(this.value); - playbackManager.seekPercent(newPercent, currentPlayer) + playbackManager.toggleMute(currentPlayer); } - }), positionSlider.getBubbleText = function(value) { + + }); + + elem.querySelector('.stopButton').addEventListener('click', function () { + + if (currentPlayer) { + playbackManager.stop(currentPlayer); + } + }); + + var i, length; + playPauseButtons = elem.querySelectorAll('.playPauseButton'); + for (i = 0, length = playPauseButtons.length; i < length; i++) { + playPauseButtons[i].addEventListener('click', onPlayPauseClick); + } + + elem.querySelector('.nextTrackButton').addEventListener('click', function () { + + if (currentPlayer) { + playbackManager.nextTrack(currentPlayer); + } + }); + + elem.querySelector('.previousTrackButton').addEventListener('click', function () { + + if (currentPlayer) { + playbackManager.previousTrack(currentPlayer); + } + }); + + elem.querySelector('.remoteControlButton').addEventListener('click', showRemoteControl); + + toggleRepeatButton = elem.querySelector('.toggleRepeatButton'); + toggleRepeatButton.addEventListener('click', function () { + + if (currentPlayer) { + + switch (playbackManager.getRepeatMode(currentPlayer)) { + case 'RepeatAll': + playbackManager.setRepeatMode('RepeatOne', currentPlayer); + break; + case 'RepeatOne': + playbackManager.setRepeatMode('RepeatNone', currentPlayer); + break; + default: + playbackManager.setRepeatMode('RepeatAll', currentPlayer); + break; + } + } + }); + + toggleRepeatButtonIcon = toggleRepeatButton.querySelector('i'); + + volumeSlider = elem.querySelector('.nowPlayingBarVolumeSlider'); + volumeSliderContainer = elem.querySelector('.nowPlayingBarVolumeSliderContainer'); + + if (appHost.supports('physicalvolumecontrol')) { + volumeSliderContainer.classList.add('hide'); + } else { + volumeSliderContainer.classList.remove('hide'); + } + + volumeSlider.addEventListener('change', function () { + + if (currentPlayer) { + currentPlayer.setVolume(this.value); + } + + }); + + positionSlider = elem.querySelector('.nowPlayingBarPositionSlider'); + positionSlider.addEventListener('change', function () { + + if (currentPlayer) { + + var newPercent = parseFloat(this.value); + + playbackManager.seekPercent(newPercent, currentPlayer); + } + + }); + + positionSlider.getBubbleText = function (value) { + var state = lastPlayerState; - if (!state || !state.NowPlayingItem || !currentRuntimeTicks) return "--:--"; + + if (!state || !state.NowPlayingItem || !currentRuntimeTicks) { + return '--:--'; + } + var ticks = currentRuntimeTicks; - return ticks /= 100, ticks *= value, datetime.getDisplayRunningTime(ticks) - }, elem.addEventListener("click", function(e) { - dom.parentWithTag(e.target, ["BUTTON", "INPUT", "A"]) || showRemoteControl() - }) + ticks /= 100; + ticks *= value; + + return datetime.getDisplayRunningTime(ticks); + }; + + elem.addEventListener('click', function (e) { + + if (!dom.parentWithTag(e.target, ['BUTTON', 'INPUT', 'A'])) { + showRemoteControl(0); + } + }); } function showRemoteControl() { - require(["appRouter"], function(appRouter) { - appRouter.showNowPlaying() - }) + + require(['appRouter'], function (appRouter) { + appRouter.showNowPlaying(); + }); } + var nowPlayingBarElement; function getNowPlayingBar() { - return nowPlayingBarElement ? Promise.resolve(nowPlayingBarElement) : new Promise(function(resolve, reject) { - require(["appFooter-shared", "itemShortcuts", "css!./nowplayingbar.css", "emby-slider"], function(appfooter, itemShortcuts) { + + if (nowPlayingBarElement) { + return Promise.resolve(nowPlayingBarElement); + } + + return new Promise(function (resolve, reject) { + + require(['appFooter-shared', 'itemShortcuts', 'css!./nowplayingbar.css', 'emby-slider'], function (appfooter, itemShortcuts) { + var parentContainer = appfooter.element; - if (nowPlayingBarElement = parentContainer.querySelector(".nowPlayingBar")) return void resolve(nowPlayingBarElement); - parentContainer.insertAdjacentHTML("afterbegin", getNowPlayingBarHtml()), nowPlayingBarElement = parentContainer.querySelector(".nowPlayingBar"), browser.safari && browser.slow && nowPlayingBarElement.classList.add("noMediaProgress"), itemShortcuts.on(nowPlayingBarElement), bindEvents(nowPlayingBarElement), resolve(nowPlayingBarElement) - }) - }) + nowPlayingBarElement = parentContainer.querySelector('.nowPlayingBar'); + + if (nowPlayingBarElement) { + resolve(nowPlayingBarElement); + return; + } + + parentContainer.insertAdjacentHTML('afterbegin', getNowPlayingBarHtml()); + nowPlayingBarElement = parentContainer.querySelector('.nowPlayingBar'); + + if (browser.safari && browser.slow) { + // Not handled well here. The wrong elements receive events, bar doesn't update quickly enough, etc. + nowPlayingBarElement.classList.add('noMediaProgress'); + } + + itemShortcuts.on(nowPlayingBarElement); + + bindEvents(nowPlayingBarElement); + resolve(nowPlayingBarElement); + }); + }); } function showButton(button) { - button.classList.remove("hide") + button.classList.remove('hide'); } function hideButton(button) { - button.classList.add("hide") + button.classList.add('hide'); } function updatePlayPauseState(isPaused) { + var i, length; - if (playPauseButtons) - if (isPaused) - for (i = 0, length = playPauseButtons.length; i < length; i++) playPauseButtons[i].querySelector("i").innerHTML = "play_arrow"; - else - for (i = 0, length = playPauseButtons.length; i < length; i++) playPauseButtons[i].querySelector("i").innerHTML = "pause" + + if (playPauseButtons) { + if (isPaused) { + + for (i = 0, length = playPauseButtons.length; i < length; i++) { + playPauseButtons[i].querySelector('i').innerHTML = 'play_arrow'; + } + + } else { + + for (i = 0, length = playPauseButtons.length; i < length; i++) { + playPauseButtons[i].querySelector('i').innerHTML = 'pause'; + } + } + } } function updatePlayerStateInternal(event, state, player) { - showNowPlayingBar(), lastPlayerState = state; - var playerInfo = playbackManager.getPlayerInfo(), - playState = state.PlayState || {}; + + showNowPlayingBar(); + + lastPlayerState = state; + + var playerInfo = playbackManager.getPlayerInfo(); + + var playState = state.PlayState || {}; + updatePlayPauseState(playState.IsPaused); + var supportedCommands = playerInfo.supportedCommands; - if (currentPlayerSupportedCommands = supportedCommands, -1 === supportedCommands.indexOf("SetRepeatMode") ? toggleRepeatButton.classList.add("hide") : toggleRepeatButton.classList.remove("hide"), updateRepeatModeDisplay(playState.RepeatMode), updatePlayerVolumeState(playState.IsMuted, playState.VolumeLevel), positionSlider && !positionSlider.dragging) { - positionSlider.disabled = !playState.CanSeek; - var isProgressClear = state.MediaSource && null == state.MediaSource.RunTimeTicks; - positionSlider.setIsClear(isProgressClear) + currentPlayerSupportedCommands = supportedCommands; + + if (supportedCommands.indexOf('SetRepeatMode') === -1) { + toggleRepeatButton.classList.add('hide'); + } else { + toggleRepeatButton.classList.remove('hide'); } - updateTimeDisplay(playState.PositionTicks, (state.NowPlayingItem || {}).RunTimeTicks, playbackManager.getBufferedRanges(player)), updateNowPlayingInfo(state) + + updateRepeatModeDisplay(playState.RepeatMode); + + updatePlayerVolumeState(playState.IsMuted, playState.VolumeLevel); + + if (positionSlider && !positionSlider.dragging) { + positionSlider.disabled = !playState.CanSeek; + + // determines if both forward and backward buffer progress will be visible + var isProgressClear = state.MediaSource && state.MediaSource.RunTimeTicks == null; + positionSlider.setIsClear(isProgressClear); + } + + var nowPlayingItem = state.NowPlayingItem || {}; + updateTimeDisplay(playState.PositionTicks, nowPlayingItem.RunTimeTicks, playbackManager.getBufferedRanges(player)); + + updateNowPlayingInfo(state); } function updateRepeatModeDisplay(repeatMode) { - "RepeatAll" === repeatMode ? (toggleRepeatButtonIcon.innerHTML = "repeat", toggleRepeatButton.classList.add("repeatButton-active")) : "RepeatOne" === repeatMode ? (toggleRepeatButtonIcon.innerHTML = "repeat_one", toggleRepeatButton.classList.add("repeatButton-active")) : (toggleRepeatButtonIcon.innerHTML = "repeat", toggleRepeatButton.classList.remove("repeatButton-active")) + + if (repeatMode === 'RepeatAll') { + toggleRepeatButtonIcon.innerHTML = "repeat"; + toggleRepeatButton.classList.add('repeatButton-active'); + } + else if (repeatMode === 'RepeatOne') { + toggleRepeatButtonIcon.innerHTML = "repeat_one"; + toggleRepeatButton.classList.add('repeatButton-active'); + } else { + toggleRepeatButtonIcon.innerHTML = "repeat"; + toggleRepeatButton.classList.remove('repeatButton-active'); + } } function updateTimeDisplay(positionTicks, runtimeTicks, bufferedRanges) { - if (positionSlider && !positionSlider.dragging) + + // See bindEvents for why this is necessary + if (positionSlider && !positionSlider.dragging) { if (runtimeTicks) { + var pct = positionTicks / runtimeTicks; - pct *= 100, positionSlider.value = pct - } else positionSlider.value = 0; - if (positionSlider && positionSlider.setBufferedRanges(bufferedRanges, runtimeTicks, positionTicks), currentTimeElement) { - var timeText = null == positionTicks ? "--:--" : datetime.getDisplayRunningTime(positionTicks); - runtimeTicks && (timeText += " / " + datetime.getDisplayRunningTime(runtimeTicks)), currentTimeElement.innerHTML = timeText + pct *= 100; + + positionSlider.value = pct; + + } else { + + positionSlider.value = 0; + } + } + + if (positionSlider) { + positionSlider.setBufferedRanges(bufferedRanges, runtimeTicks, positionTicks); + } + + if (currentTimeElement) { + + var timeText = positionTicks == null ? '--:--' : datetime.getDisplayRunningTime(positionTicks); + + if (runtimeTicks) { + timeText += " / " + datetime.getDisplayRunningTime(runtimeTicks); + } + + currentTimeElement.innerHTML = timeText; } } function updatePlayerVolumeState(isMuted, volumeLevel) { - var supportedCommands = currentPlayerSupportedCommands, - showMuteButton = !0, - showVolumeSlider = !0; - 1 === supportedCommands.indexOf("ToggleMute") && (showMuteButton = !1), muteButton.querySelector("i").innerHTML = isMuted ? "" : "", -1 === supportedCommands.indexOf("SetVolume") && (showVolumeSlider = !1), currentPlayer.isLocalPlayer && appHost.supports("physicalvolumecontrol") && (showMuteButton = !1, showVolumeSlider = !1), showMuteButton ? showButton(muteButton) : hideButton(muteButton), volumeSlider && (showVolumeSlider ? volumeSliderContainer.classList.remove("hide") : volumeSliderContainer.classList.add("hide"), volumeSlider.dragging || (volumeSlider.value = volumeLevel || 0)) + + var supportedCommands = currentPlayerSupportedCommands; + + var showMuteButton = true; + var showVolumeSlider = true; + + if (supportedCommands.indexOf('ToggleMute') === -1) { + showMuteButton = false; + } + + if (isMuted) { + muteButton.querySelector('i').innerHTML = ''; + } else { + muteButton.querySelector('i').innerHTML = ''; + } + + if (supportedCommands.indexOf('SetVolume') === -1) { + showVolumeSlider = false; + } + + if (currentPlayer.isLocalPlayer && appHost.supports('physicalvolumecontrol')) { + showMuteButton = false; + showVolumeSlider = false; + } + + if (showMuteButton) { + showButton(muteButton); + } else { + hideButton(muteButton); + } + + // See bindEvents for why this is necessary + if (volumeSlider) { + + if (showVolumeSlider) { + volumeSliderContainer.classList.remove('hide'); + } else { + volumeSliderContainer.classList.add('hide'); + } + + if (!volumeSlider.dragging) { + volumeSlider.value = volumeLevel || 0; + } + } } function getTextActionButton(item, text, serverId) { - text || (text = itemHelper.getDisplayName(item)); + + if (!text) { + text = itemHelper.getDisplayName(item); + } + var html = '" + html += text; + html += ''; + + return html; } function seriesImageUrl(item, options) { - if (!item) throw new Error("item cannot be null!"); - if ("Episode" !== item.Type) return null; - if (options = options || {}, options.type = options.type || "Primary", "Primary" === options.type && item.SeriesPrimaryImageTag) return options.tag = item.SeriesPrimaryImageTag, connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); - if ("Thumb" === options.type) { - if (item.SeriesThumbImageTag) return options.tag = item.SeriesThumbImageTag, connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); - if (item.ParentThumbImageTag) return options.tag = item.ParentThumbImageTag, connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.ParentThumbItemId, options) + + if (!item) { + throw new Error('item cannot be null!'); } - return null + + if (item.Type !== 'Episode') { + return null; + } + + options = options || {}; + options.type = options.type || "Primary"; + + if (options.type === 'Primary') { + + if (item.SeriesPrimaryImageTag) { + + options.tag = item.SeriesPrimaryImageTag; + + return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); + } + } + + if (options.type === 'Thumb') { + + if (item.SeriesThumbImageTag) { + + options.tag = item.SeriesThumbImageTag; + + return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); + } + if (item.ParentThumbImageTag) { + + options.tag = item.ParentThumbImageTag; + + return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.ParentThumbItemId, options); + } + } + + return null; } function imageUrl(item, options) { - if (!item) throw new Error("item cannot be null!"); - return options = options || {}, options.type = options.type || "Primary", item.ImageTags && item.ImageTags[options.type] ? (options.tag = item.ImageTags[options.type], connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.PrimaryImageItemId || item.Id, options)) : item.AlbumId && item.AlbumPrimaryImageTag ? (options.tag = item.AlbumPrimaryImageTag, connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.AlbumId, options)) : null + + if (!item) { + throw new Error('item cannot be null!'); + } + + options = options || {}; + options.type = options.type || "Primary"; + + if (item.ImageTags && item.ImageTags[options.type]) { + + options.tag = item.ImageTags[options.type]; + return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.PrimaryImageItemId || item.Id, options); + } + + if (item.AlbumId && item.AlbumPrimaryImageTag) { + + options.tag = item.AlbumPrimaryImageTag; + return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.AlbumId, options); + } + + return null; } + var currentImgUrl; function updateNowPlayingInfo(state) { - var nowPlayingItem = state.NowPlayingItem, - textLines = nowPlayingItem ? nowPlayingHelper.getNowPlayingNames(nowPlayingItem) : []; - textLines.length > 1 && (textLines[1].secondary = !0); + + var nowPlayingItem = state.NowPlayingItem; + + var textLines = nowPlayingItem ? nowPlayingHelper.getNowPlayingNames(nowPlayingItem) : []; + if (textLines.length > 1) { + textLines[1].secondary = true; + } var serverId = nowPlayingItem ? nowPlayingItem.ServerId : null; - nowPlayingTextElement.innerHTML = textLines.map(function(nowPlayingName) { - var cssClass = nowPlayingName.secondary ? ' class="nowPlayingBarSecondaryText"' : ""; - return nowPlayingName.item ? "" + getTextActionButton(nowPlayingName.item, nowPlayingName.text, serverId) + "
" : "" + nowPlayingName.text + "
" - }).join(""); - var url = nowPlayingItem ? seriesImageUrl(nowPlayingItem, { - height: 70 - }) || imageUrl(nowPlayingItem, { - height: 70 - }) : null, - isRefreshing = !1; - if (url !== currentImgUrl && (currentImgUrl = url, isRefreshing = !0, url ? imageLoader.lazyImage(nowPlayingImageElement, url) : nowPlayingImageElement.style.backgroundImage = ""), nowPlayingItem.Id) { - if (isRefreshing) { - var apiClient = connectionManager.getApiClient(nowPlayingItem.ServerId); - apiClient.getItem(apiClient.getCurrentUserId(), nowPlayingItem.Id).then(function(item) { - var userData = item.UserData || {}, - likes = null == userData.Likes ? "" : userData.Likes; - nowPlayingUserData.innerHTML = '' - }) + nowPlayingTextElement.innerHTML = textLines.map(function (nowPlayingName) { + + var cssClass = nowPlayingName.secondary ? ' class="nowPlayingBarSecondaryText"' : ''; + + if (nowPlayingName.item) { + return '' + getTextActionButton(nowPlayingName.item, nowPlayingName.text, serverId) + '
'; } - } else nowPlayingUserData.innerHTML = "" + + return '' + nowPlayingName.text + '
'; + + }).join(''); + + var imgHeight = 70; + + var url = nowPlayingItem ? (seriesImageUrl(nowPlayingItem, { + height: imgHeight + }) || imageUrl(nowPlayingItem, { + height: imgHeight + })) : null; + + var isRefreshing = false; + + if (url !== currentImgUrl) { + currentImgUrl = url; + isRefreshing = true; + + if (url) { + imageLoader.lazyImage(nowPlayingImageElement, url); + } else { + nowPlayingImageElement.style.backgroundImage = ''; + } + } + + if (nowPlayingItem.Id) { + if (isRefreshing) { + + var apiClient = connectionManager.getApiClient(nowPlayingItem.ServerId); + + apiClient.getItem(apiClient.getCurrentUserId(), nowPlayingItem.Id).then(function (item) { + + var userData = item.UserData || {}; + var likes = userData.Likes == null ? '' : userData.Likes; + + nowPlayingUserData.innerHTML = ''; + }); + + } + } else { + nowPlayingUserData.innerHTML = ''; + } } function onPlaybackStart(e, state) { + + //console.log('nowplaying event: ' + e.type); + var player = this; - onStateChanged.call(player, e, state) + + onStateChanged.call(player, e, state); } function onRepeatModeChange(e) { - if (isEnabled) { - var player = this; - updateRepeatModeDisplay(playbackManager.getRepeatMode(player)) + + if (!isEnabled) { + return; } + + var player = this; + + updateRepeatModeDisplay(playbackManager.getRepeatMode(player)); } function showNowPlayingBar() { - if (!isVisibilityAllowed) return void hideNowPlayingBar(); - getNowPlayingBar().then(slideUp) + + if (!isVisibilityAllowed) { + hideNowPlayingBar(); + return; + } + + getNowPlayingBar().then(slideUp); } function hideNowPlayingBar() { - isEnabled = !1; - var elem = document.getElementsByClassName("nowPlayingBar")[0]; - elem && slideDown(elem) - } - function onPlaybackStopped(e, state) { - this.isLocalPlayer ? "Audio" !== state.NextMediaType && hideNowPlayingBar() : state.NextMediaType || hideNowPlayingBar() - } + isEnabled = false; - function onPlayPauseStateChanged(e) { - if (isEnabled) { - updatePlayPauseState(this.paused()) + // Use a timeout to prevent the bar from hiding and showing quickly + // in the event of a stop->play command + + // Don't call getNowPlayingBar here because we don't want to end up creating it just to hide it + var elem = document.getElementsByClassName('nowPlayingBar')[0]; + if (elem) { + + slideDown(elem); } } - function onStateChanged(event, state) { - var player = this; - return !state.NowPlayingItem || layoutManager.tv ? void hideNowPlayingBar() : player.isLocalPlayer && state.NowPlayingItem && "Video" === state.NowPlayingItem.MediaType ? void hideNowPlayingBar() : (isEnabled = !0, nowPlayingBarElement ? void updatePlayerStateInternal(event, state, player) : void getNowPlayingBar().then(function() { - updatePlayerStateInternal(event, state, player) - })) - } + function onPlaybackStopped(e, state) { - function onTimeUpdate(e) { - if (isEnabled) { - var now = (new Date).getTime(); - if (!(now - lastUpdateTime < 700)) { - lastUpdateTime = now; - var player = this; - currentRuntimeTicks = playbackManager.duration(player), updateTimeDisplay(playbackManager.currentTime(player), currentRuntimeTicks, playbackManager.getBufferedRanges(player)) + //console.log('nowplaying event: ' + e.type); + var player = this; + + if (player.isLocalPlayer) { + if (state.NextMediaType !== 'Audio') { + hideNowPlayingBar(); + } + } else { + if (!state.NextMediaType) { + hideNowPlayingBar(); } } } - function releaseCurrentPlayer() { - var player = currentPlayer; - player && (events.off(player, "playbackstart", onPlaybackStart), events.off(player, "statechange", onPlaybackStart), events.off(player, "repeatmodechange", onRepeatModeChange), events.off(player, "playbackstop", onPlaybackStopped), events.off(player, "volumechange", onVolumeChanged), events.off(player, "pause", onPlayPauseStateChanged), events.off(player, "unpause", onPlayPauseStateChanged), events.off(player, "timeupdate", onTimeUpdate), currentPlayer = null, hideNowPlayingBar()) + function onPlayPauseStateChanged(e) { + + if (!isEnabled) { + return; + } + + var player = this; + updatePlayPauseState(player.paused()); } - function onVolumeChanged(e) { - if (isEnabled) { - var player = this; - updatePlayerVolumeState(player.isMuted(), player.getVolume()) + function onStateChanged(event, state) { + + //console.log('nowplaying event: ' + e.type); + var player = this; + + if (!state.NowPlayingItem || layoutManager.tv) { + hideNowPlayingBar(); + return; + } + + if (player.isLocalPlayer && state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Video') { + hideNowPlayingBar(); + return; + } + + isEnabled = true; + + if (nowPlayingBarElement) { + updatePlayerStateInternal(event, state, player); + return; + } + + getNowPlayingBar().then(function () { + updatePlayerStateInternal(event, state, player); + }); + } + + function onTimeUpdate(e) { + + if (!isEnabled) { + return; + } + + // Try to avoid hammering the document with changes + var now = new Date().getTime(); + if ((now - lastUpdateTime) < 700) { + + return; + } + lastUpdateTime = now; + + var player = this; + currentRuntimeTicks = playbackManager.duration(player); + updateTimeDisplay(playbackManager.currentTime(player), currentRuntimeTicks, playbackManager.getBufferedRanges(player)); + } + + function releaseCurrentPlayer() { + + var player = currentPlayer; + + if (player) { + events.off(player, 'playbackstart', onPlaybackStart); + events.off(player, 'statechange', onPlaybackStart); + events.off(player, 'repeatmodechange', onRepeatModeChange); + events.off(player, 'playbackstop', onPlaybackStopped); + events.off(player, 'volumechange', onVolumeChanged); + events.off(player, 'pause', onPlayPauseStateChanged); + events.off(player, 'unpause', onPlayPauseStateChanged); + events.off(player, 'timeupdate', onTimeUpdate); + + currentPlayer = null; + hideNowPlayingBar(); } } + function onVolumeChanged(e) { + + if (!isEnabled) { + return; + } + + var player = this; + + updatePlayerVolumeState(player.isMuted(), player.getVolume()); + } + function refreshFromPlayer(player) { + var state = playbackManager.getPlayerState(player); - onStateChanged.call(player, { - type: "init" - }, state) + + onStateChanged.call(player, { type: 'init' }, state); } function bindToPlayer(player) { - player !== currentPlayer && (releaseCurrentPlayer(), currentPlayer = player, player && (refreshFromPlayer(player), events.on(player, "playbackstart", onPlaybackStart), events.on(player, "statechange", onPlaybackStart), events.on(player, "repeatmodechange", onRepeatModeChange), events.on(player, "playbackstop", onPlaybackStopped), events.on(player, "volumechange", onVolumeChanged), events.on(player, "pause", onPlayPauseStateChanged), events.on(player, "unpause", onPlayPauseStateChanged), events.on(player, "timeupdate", onTimeUpdate))) + + if (player === currentPlayer) { + return; + } + + releaseCurrentPlayer(); + + currentPlayer = player; + + if (!player) { + return; + } + + refreshFromPlayer(player); + + events.on(player, 'playbackstart', onPlaybackStart); + events.on(player, 'statechange', onPlaybackStart); + events.on(player, 'repeatmodechange', onRepeatModeChange); + events.on(player, 'playbackstop', onPlaybackStopped); + events.on(player, 'volumechange', onVolumeChanged); + events.on(player, 'pause', onPlayPauseStateChanged); + events.on(player, 'unpause', onPlayPauseStateChanged); + events.on(player, 'timeupdate', onTimeUpdate); } - var currentPlayer, currentTimeElement, nowPlayingImageElement, nowPlayingTextElement, nowPlayingUserData, muteButton, volumeSlider, volumeSliderContainer, playPauseButtons, positionSlider, toggleRepeatButton, toggleRepeatButtonIcon, isEnabled, nowPlayingBarElement, currentImgUrl, currentPlayerSupportedCommands = [], - lastUpdateTime = 0, - lastPlayerState = {}, - currentRuntimeTicks = 0, - isVisibilityAllowed = !0; - events.on(playbackManager, "playerchange", function() { - bindToPlayer(playbackManager.getCurrentPlayer()) - }), bindToPlayer(playbackManager.getCurrentPlayer()), document.addEventListener("viewbeforeshow", function(e) { - e.detail.options.enableMediaControl ? isVisibilityAllowed || (isVisibilityAllowed = !0, currentPlayer ? refreshFromPlayer(currentPlayer) : hideNowPlayingBar()) : isVisibilityAllowed && (isVisibilityAllowed = !1, hideNowPlayingBar()) - }) + + events.on(playbackManager, 'playerchange', function () { + bindToPlayer(playbackManager.getCurrentPlayer()); + }); + + bindToPlayer(playbackManager.getCurrentPlayer()); + + document.addEventListener('viewbeforeshow', function (e) { + + if (!e.detail.options.enableMediaControl) { + + if (isVisibilityAllowed) { + isVisibilityAllowed = false; + hideNowPlayingBar(); + } + + } else if (!isVisibilityAllowed) { + + isVisibilityAllowed = true; + if (currentPlayer) { + refreshFromPlayer(currentPlayer); + } else { + hideNowPlayingBar(); + } + } + }); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/packagemanager.js b/src/bower_components/emby-webcomponents/packagemanager.js index e697028f5c..03ed1006c1 100644 --- a/src/bower_components/emby-webcomponents/packagemanager.js +++ b/src/bower_components/emby-webcomponents/packagemanager.js @@ -1,74 +1,152 @@ -define(["appSettings", "pluginManager"], function(appSettings, pluginManager) { - "use strict"; +define(['appSettings', 'pluginManager'], function (appSettings, pluginManager) { + 'use strict'; + + var settingsKey = 'installedpackages1'; function addPackage(packageManager, pkg) { - packageManager.packagesList = packageManager.packagesList.filter(function(p) { - return p.name !== pkg.name - }), packageManager.packagesList.push(pkg) + + packageManager.packagesList = packageManager.packagesList.filter(function (p) { + + return p.name !== pkg.name; + }); + + packageManager.packagesList.push(pkg); } function removeUrl(url) { - var manifestUrls = JSON.parse(appSettings.get(settingsKey) || "[]"); - manifestUrls = manifestUrls.filter(function(i) { - return i !== url - }), appSettings.set(settingsKey, JSON.stringify(manifestUrls)) + + var manifestUrls = JSON.parse(appSettings.get(settingsKey) || '[]'); + + manifestUrls = manifestUrls.filter(function (i) { + return i !== url; + }); + + appSettings.set(settingsKey, JSON.stringify(manifestUrls)); } function loadPackage(packageManager, url, throwError) { - return new Promise(function(resolve, reject) { - var xhr = new XMLHttpRequest, - originalUrl = url; - url += -1 === url.indexOf("?") ? "?" : "&", url += "t=" + (new Date).getTime(), xhr.open("GET", url, !0); - var onError = function() { - !0 === throwError ? reject() : (removeUrl(originalUrl), resolve()) + + return new Promise(function (resolve, reject) { + + var xhr = new XMLHttpRequest(); + var originalUrl = url; + url += url.indexOf('?') === -1 ? '?' : '&'; + url += 't=' + new Date().getTime(); + + xhr.open('GET', url, true); + + var onError = function () { + + if (throwError === true) { + reject(); + } else { + removeUrl(originalUrl); + resolve(); + } }; - xhr.onload = function(e) { + + xhr.onload = function (e) { if (this.status < 400) { + var pkg = JSON.parse(this.response); - pkg.url = originalUrl, addPackage(packageManager, pkg); + pkg.url = originalUrl; + + addPackage(packageManager, pkg); + var plugins = pkg.plugins || []; - pkg.plugin && plugins.push(pkg.plugin); - var promises = plugins.map(function(pluginUrl) { - return pluginManager.loadPlugin(packageManager.mapPath(pkg, pluginUrl)) + if (pkg.plugin) { + plugins.push(pkg.plugin); + } + var promises = plugins.map(function (pluginUrl) { + return pluginManager.loadPlugin(packageManager.mapPath(pkg, pluginUrl)); }); - Promise.all(promises).then(resolve, resolve) - } else onError() - }, xhr.onerror = onError, xhr.send() - }) + Promise.all(promises).then(resolve, resolve); + + } else { + onError(); + } + }; + + xhr.onerror = onError; + + xhr.send(); + }); } function PackageManager() { - this.packagesList = [] + + this.packagesList = []; } - var settingsKey = "installedpackages1"; - return PackageManager.prototype.init = function() { - var manifestUrls = JSON.parse(appSettings.get(settingsKey) || "[]"), - instance = this; - return Promise.all(manifestUrls.map(function(u) { - return loadPackage(instance, u) - })).then(function() { - return Promise.resolve() - }, function() { - return Promise.resolve() - }) - }, PackageManager.prototype.packages = function() { - return this.packagesList.slice(0) - }, PackageManager.prototype.install = function(url) { - return loadPackage(this, url, !0).then(function(pkg) { - var manifestUrls = JSON.parse(appSettings.get(settingsKey) || "[]"); - return -1 === manifestUrls.indexOf(url) && (manifestUrls.push(url), appSettings.set(settingsKey, JSON.stringify(manifestUrls))), pkg - }) - }, PackageManager.prototype.uninstall = function(name) { - var pkg = this.packagesList.filter(function(p) { - return p.name === name + + PackageManager.prototype.init = function () { + var manifestUrls = JSON.parse(appSettings.get(settingsKey) || '[]'); + + var instance = this; + return Promise.all(manifestUrls.map(function (u) { + + return loadPackage(instance, u); + + })).then(function () { + return Promise.resolve(); + }, function () { + return Promise.resolve(); + }); + }; + + PackageManager.prototype.packages = function () { + return this.packagesList.slice(0); + }; + + PackageManager.prototype.install = function (url) { + + return loadPackage(this, url, true).then(function (pkg) { + + var manifestUrls = JSON.parse(appSettings.get(settingsKey) || '[]'); + + if (manifestUrls.indexOf(url) === -1) { + manifestUrls.push(url); + appSettings.set(settingsKey, JSON.stringify(manifestUrls)); + } + + return pkg; + }); + }; + + PackageManager.prototype.uninstall = function (name) { + + var pkg = this.packagesList.filter(function (p) { + + return p.name === name; })[0]; - return pkg && (this.packagesList = this.packagesList.filter(function(p) { - return p.name !== name - }), removeUrl(pkg.url)), Promise.resolve() - }, PackageManager.prototype.mapPath = function(pkg, pluginUrl) { + + if (pkg) { + + this.packagesList = this.packagesList.filter(function (p) { + + return p.name !== name; + }); + + removeUrl(pkg.url); + } + + return Promise.resolve(); + }; + + PackageManager.prototype.mapPath = function (pkg, pluginUrl) { + var urlLower = pluginUrl.toLowerCase(); - if (0 === urlLower.indexOf("http:") || 0 === urlLower.indexOf("https:") || 0 === urlLower.indexOf("file:")) return pluginUrl; + if (urlLower.indexOf('http:') === 0 || urlLower.indexOf('https:') === 0 || urlLower.indexOf('file:') === 0) { + return pluginUrl; + } + var packageUrl = pkg.url; - return packageUrl = packageUrl.substring(0, packageUrl.lastIndexOf("/")), packageUrl += "/", packageUrl += pluginUrl - }, new PackageManager + packageUrl = packageUrl.substring(0, packageUrl.lastIndexOf('/')); + + packageUrl += '/'; + packageUrl += pluginUrl; + + return packageUrl; + }; + + return new PackageManager(); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/pagejs/page.js b/src/bower_components/emby-webcomponents/pagejs/page.js index 17b29a434a..ce96fd185e 100644 --- a/src/bower_components/emby-webcomponents/pagejs/page.js +++ b/src/bower_components/emby-webcomponents/pagejs/page.js @@ -1,288 +1,1077 @@ -define([], function() { - "use strict"; +define([], function () { + + 'use strict'; + + /** + * Detect click event + */ + var clickEvent = ('undefined' !== typeof document) && document.ontouchstart ? 'touchstart' : 'click'; + + /** + * To work properly with the URL + * history.location generated polyfill in https://github.com/devote/HTML5-History-API + */ + + var location = ('undefined' !== typeof window) && (window.history.location || window.location); + + /** + * Perform initial dispatch. + */ + + var dispatch = true; + + + /** + * Decode URL components (query string, pathname, hash). + * Accommodates both regular percent encoding and x-www-form-urlencoded format. + */ + var decodeURLComponents = true; + + /** + * Base path. + */ + + var base = ''; + + /** + * Running flag. + */ + + var running; + + /** + * HashBang option + */ + + var hashbang = false; + + var enableHistory = false; + + /** + * Previous context, for capturing + * page exit events. + */ + + var prevContext; + + var prevPageContext; + + /** + * Register `path` with callback `fn()`, + * or route `path`, or redirection, + * or `page.start()`. + * + * page(fn); + * page('*', fn); + * page('/user/:id', load, user); + * page('/user/' + user.id, { some: 'thing' }); + * page('/user/' + user.id); + * page('/from', '/to') + * page(); + * + * @param {String|Function} path + * @param {Function} fn... + * @api public + */ function page(path, fn) { - if ("function" == typeof path) return page("*", path); - if ("function" == typeof fn) - for (var route = new Route(path), i = 1; i < arguments.length; ++i) page.callbacks.push(route.middleware(arguments[i])); - else "string" == typeof path ? page["string" == typeof fn ? "redirect" : "show"](path, fn) : page.start(path) - } + // + if ('function' === typeof path) { + return page('*', path); + } - function unhandled(ctx) { - if (!ctx.handled) { - var current; - current = hashbang ? base + location.hash.replace("#!", "") : location.pathname + location.search, current !== ctx.canonicalPath && (page.stop(), ctx.handled = !1, location.href = ctx.canonicalPath) + // route to + if ('function' === typeof fn) { + var route = new Route(path); + for (var i = 1; i < arguments.length; ++i) { + page.callbacks.push(route.middleware(arguments[i])); + } + // show with [state] + } else if ('string' === typeof path) { + page['string' === typeof fn ? 'redirect' : 'show'](path, fn); + // start [options] + } else { + page.start(path); } } - function decodeURLEncodedURIComponent(val) { - return "string" != typeof val ? val : decodeURLComponents ? decodeURIComponent(val.replace(/\+/g, " ")) : val - } + /** + * Callback functions. + */ - function Context(path, state) { - "/" === path[0] && 0 !== path.indexOf(base) && (path = base + (hashbang ? "#!" : "") + path); - var i = path.indexOf("?"); - if (this.canonicalPath = path, this.path = path.replace(base, "") || "/", hashbang && (this.path = this.path.replace("#!", "") || "/"), this.title = document.title, this.state = state || {}, this.state.path = path, this.querystring = ~i ? decodeURLEncodedURIComponent(path.slice(i + 1)) : "", this.pathname = decodeURLEncodedURIComponent(~i ? path.slice(0, i) : path), this.params = {}, this.hash = "", !hashbang) { - if (!~this.path.indexOf("#")) return; - var parts = this.path.split("#"); - this.path = parts[0], this.hash = decodeURLEncodedURIComponent(parts[1]) || "", this.querystring = this.querystring.split("#")[0] + page.callbacks = []; + page.exits = []; + + /** + * Current path being processed + * @type {String} + */ + page.current = ''; + + /** + * Number of pages navigated to. + * @type {number} + * + * page.len == 0; + * page('/login'); + * page.len == 1; + */ + + page.len = 0; + + /** + * Get or set basepath to `path`. + * + * @param {String} path + * @api public + */ + + page.base = function (path) { + if (0 === arguments.length) { + return base; } - } + base = path; + }; - function Route(path, options) { - options = options || {}, this.path = "*" === path ? "(.*)" : path, this.method = "GET", this.regexp = pathToRegexp(this.path, this.keys = [], options.sensitive, options.strict) - } + /** + * Bind with the given `options`. + * + * Options: + * + * - `click` bind to click events [true] + * - `popstate` bind to popstate [true] + * - `dispatch` perform initial dispatch [true] + * + * @param {Object} options + * @api public + */ - function ignorePopState(event) { - var state = event.state || {}; - return !1 === previousPopState.navigate ? (previousPopState = state, !0) : (previousPopState = state, !1) - } + page.start = function (options) { + options = options || {}; + if (running) { + return; + } + running = true; + if (false === options.dispatch) { + dispatch = false; + } + if (false === options.decodeURLComponents) { + decodeURLComponents = false; + } + if (false !== options.popstate) { + window.addEventListener('popstate', onpopstate, false); + } + if (false !== options.click) { + document.addEventListener(clickEvent, onclick, false); + } + if (options.enableHistory != null) { + enableHistory = options.enableHistory; + } + if (true === options.hashbang) { + hashbang = true; + } + if (!dispatch) { + return; + } - function onclick(e, checkWhich) { - if ((1 === which(e) || !1 === checkWhich) && !(e.metaKey || e.ctrlKey || e.shiftKey || e.defaultPrevented)) { - for (var el = e.target; el && "A" !== el.nodeName;) el = el.parentNode; - if (el && "A" === el.nodeName && !el.hasAttribute("download") && "external" !== el.getAttribute("rel")) { - var link = el.getAttribute("href"); - if ("#" === link) return void e.preventDefault(); - if ((hashbang || el.pathname !== location.pathname || !el.hash && "#" !== link) && !el.target && sameOrigin(el.href)) { - var path = el.pathname + el.search + (el.hash || ""), - orig = path; - 0 === path.indexOf(base) && (path = path.substr(base.length)), hashbang && (path = path.replace("#!", "")), e.preventDefault(), page.show(orig) + var url; + + if (hashbang && ~location.hash.indexOf('#!')) { + + url = location.hash.substr(2); + + var href = location.href.toString(); + if (href.indexOf('?') >= href.indexOf('#!')) { + url += location.search; + } + } + else { + url = location.pathname + location.search + location.hash; + } + + page.replace(url, null, true, dispatch); + }; + + /** + * Unbind click and popstate event handlers. + * + * @api public + */ + + page.stop = function () { + if (!running) { + return; + } + page.current = ''; + page.len = 0; + running = false; + document.removeEventListener(clickEvent, onclick, false); + window.removeEventListener('popstate', onpopstate, false); + }; + + /** + * Show `path` with optional `state` object. + * + * @param {String} path + * @param {Object} state + * @param {Boolean} dispatch + * @return {Context} + * @api public + */ + + page.show = function (path, state, dispatch, push, isBack) { + var ctx = new Context(path, state); + ctx.isBack = isBack; + page.current = ctx.path; + if (false !== dispatch) { + page.dispatch(ctx); + } + if (false !== ctx.handled && false !== push) { + ctx.pushState(); + } + return ctx; + }; + + page.restorePreviousState = function () { + + prevContext = prevPageContext; + page.show(prevContext.pathname, prevContext.state, false, true, false); + }; + + /** + * Goes back in the history + * Back should always let the current route push state and then go back. + * + * @param {String} path - fallback path to go back if no more history exists, if undefined defaults to page.base + * @param {Object} [state] + * @api public + */ + + page.back = function (path, state) { + + if (enableHistory) { + // Keep it simple and mimic browser back + history.back(); + return; + } + + if (page.len > 0) { + // this may need more testing to see if all browsers + // wait for the next tick to go back in history + if (enableHistory) { + history.back(); + } else { + + if (backStack.length > 2) { + backStack.length--; + var previousState = backStack[backStack.length - 1]; + page.show(previousState.path, previousState.state, true, false, true); } } - } - } - - function which(e) { - return e = e || window.event, null === e.which ? e.button : e.which - } - - function sameOrigin(href) { - var origin = location.protocol + "//" + location.hostname; - return location.port && (origin += ":" + location.port), href && 0 === href.indexOf(origin) - } - - function parse(str) { - for (var res, tokens = [], key = 0, index = 0, path = ""; null != (res = PATH_REGEXP.exec(str));) { - var m = res[0], - escaped = res[1], - offset = res.index; - if (path += str.slice(index, offset), index = offset + m.length, escaped) path += escaped[1]; - else { - path && (tokens.push(path), path = ""); - var prefix = res[2], - name = res[3], - capture = res[4], - group = res[5], - suffix = res[6], - asterisk = res[7], - repeat = "+" === suffix || "*" === suffix, - optional = "?" === suffix || "*" === suffix, - delimiter = prefix || "/", - pattern = capture || group || (asterisk ? ".*" : "[^" + delimiter + "]+?"); - tokens.push({ - name: name || key++, - prefix: prefix || "", - delimiter: delimiter, - optional: optional, - repeat: repeat, - pattern: escapeGroup(pattern) - }) - } - } - return index < str.length && (path += str.substr(index)), path && tokens.push(path), tokens - } - - function escapeString(str) { - return str.replace(/([.+*?=^!:${}()[\]|\/])/g, "\\$1") - } - - function escapeGroup(group) { - return group.replace(/([=!:$\/()])/g, "\\$1") - } - - function attachKeys(re, keys) { - return re.keys = keys, re - } - - function flags(options) { - return options.sensitive ? "" : "i" - } - - function regexpToRegexp(path, keys) { - var groups = path.source.match(/\((?!\?)/g); - if (groups) - for (var i = 0; i < groups.length; i++) keys.push({ - name: i, - prefix: null, - delimiter: null, - optional: !1, - repeat: !1, - pattern: null + page.len--; + } else if (path) { + setTimeout(function () { + page.show(path, state); + }); + } else { + setTimeout(function () { + page.show(base, state); }); - return attachKeys(path, keys) - } - - function arrayToRegexp(path, keys, options) { - for (var parts = [], i = 0; i < path.length; i++) parts.push(pathToRegexp(path[i], keys, options).source); - return attachKeys(new RegExp("(?:" + parts.join("|") + ")", flags(options)), keys) - } - - function stringToRegexp(path, keys, options) { - for (var tokens = parse(path), re = tokensToRegExp(tokens, options), i = 0; i < tokens.length; i++) "string" != typeof tokens[i] && keys.push(tokens[i]); - return attachKeys(re, keys) - } - - function tokensToRegExp(tokens, options) { - options = options || {}; - for (var strict = options.strict, end = !1 !== options.end, route = "", lastToken = tokens[tokens.length - 1], endsWithSlash = "string" == typeof lastToken && /\/$/.test(lastToken), i = 0; i < tokens.length; i++) { - var token = tokens[i]; - if ("string" == typeof token) route += escapeString(token); - else { - var prefix = escapeString(token.prefix), - capture = token.pattern; - token.repeat && (capture += "(?:" + prefix + capture + ")*"), capture = token.optional ? prefix ? "(?:" + prefix + "(" + capture + "))?" : "(" + capture + ")?" : prefix + "(" + capture + ")", route += capture - } } - return strict || (route = (endsWithSlash ? route.slice(0, -2) : route) + "(?:\\/(?=$))?"), route += end ? "$" : strict && endsWithSlash ? "" : "(?=\\/|$)", new RegExp("^" + route, flags(options)) - } + }; - function pathToRegexp(path, keys, options) { - return keys = keys || [], isarray(keys) ? options || (options = {}) : (options = keys, keys = []), path instanceof RegExp ? regexpToRegexp(path, keys, options) : isarray(path) ? arrayToRegexp(path, keys, options) : stringToRegexp(path, keys, options) - } - var running, prevContext, prevPageContext, clickEvent = "undefined" != typeof document && document.ontouchstart ? "touchstart" : "click", - location = "undefined" != typeof window && (window.history.location || window.location), - dispatch = !0, - decodeURLComponents = !0, - base = "", - hashbang = !1, - enableHistory = !1; - page.callbacks = [], page.exits = [], page.current = "", page.len = 0, page.base = function(path) { - if (0 === arguments.length) return base; - base = path - }, page.start = function(options) { - if (options = options || {}, !running && (running = !0, !1 === options.dispatch && (dispatch = !1), !1 === options.decodeURLComponents && (decodeURLComponents = !1), !1 !== options.popstate && window.addEventListener("popstate", onpopstate, !1), !1 !== options.click && document.addEventListener(clickEvent, onclick, !1), null != options.enableHistory && (enableHistory = options.enableHistory), !0 === options.hashbang && (hashbang = !0), dispatch)) { - var url; - if (hashbang && ~location.hash.indexOf("#!")) { - url = location.hash.substr(2); - var href = location.href.toString(); - href.indexOf("?") >= href.indexOf("#!") && (url += location.search) - } else url = location.pathname + location.search + location.hash; - page.replace(url, null, !0, dispatch) + page.enableNativeHistory = function () { + return enableHistory; + }; + + page.canGoBack = function () { + if (enableHistory) { + return history.length > 1; } - }, page.stop = function() { - running && (page.current = "", page.len = 0, running = !1, document.removeEventListener(clickEvent, onclick, !1), window.removeEventListener("popstate", onpopstate, !1)) - }, page.show = function(path, state, dispatch, push, isBack) { + return (page.len || 0) > 0; + }; + + /** + * Register route to redirect from one path to other + * or just redirect to another route + * + * @param {String} from - if param 'to' is undefined redirects to 'from' + * @param {String} [to] + * @api public + */ + page.redirect = function (from, to) { + // Define route from a path to another + if ('string' === typeof from && 'string' === typeof to) { + page(from, function (e) { + setTimeout(function () { + page.replace(to); + }, 0); + }); + } + + // Wait for the push state and replace it with another + if ('string' === typeof from && 'undefined' === typeof to) { + setTimeout(function () { + page.replace(from); + }, 0); + } + }; + + /** + * Replace `path` with optional `state` object. + * + * @param {String} path + * @param {Object} state + * @return {Context} + * @api public + */ + + + page.replace = function (path, state, init, dispatch, isBack) { var ctx = new Context(path, state); - return ctx.isBack = isBack, page.current = ctx.path, !1 !== dispatch && page.dispatch(ctx), !1 !== ctx.handled && !1 !== push && ctx.pushState(), ctx - }, page.restorePreviousState = function() { - prevContext = prevPageContext, page.show(prevContext.pathname, prevContext.state, !1, !0, !1) - }, page.back = function(path, state) { - if (enableHistory) return void history.back(); - if (page.len > 0) { - if (enableHistory) history.back(); - else if (backStack.length > 2) { - backStack.length--; - var previousState = backStack[backStack.length - 1]; - page.show(previousState.path, previousState.state, !0, !1, !0) - } - page.len-- - } else path ? setTimeout(function() { - page.show(path, state) - }) : setTimeout(function() { - page.show(base, state) - }) - }, page.enableNativeHistory = function() { - return enableHistory - }, page.canGoBack = function() { - return enableHistory ? history.length > 1 : (page.len || 0) > 0 - }, page.redirect = function(from, to) { - "string" == typeof from && "string" == typeof to && page(from, function(e) { - setTimeout(function() { - page.replace(to) - }, 0) - }), "string" == typeof from && void 0 === to && setTimeout(function() { - page.replace(from) - }, 0) - }, page.replace = function(path, state, init, dispatch, isBack) { - var ctx = new Context(path, state); - return ctx.isBack = isBack, page.current = ctx.path, ctx.init = init, ctx.save(), !1 !== dispatch && page.dispatch(ctx), ctx - }, page.dispatch = function(ctx) { + ctx.isBack = isBack; + page.current = ctx.path; + ctx.init = init; + ctx.save(); // save before dispatching, which may redirect + if (false !== dispatch) { + page.dispatch(ctx); + } + return ctx; + }; + + /** + * Dispatch the given `ctx`. + * + * @param {Object} ctx + * @api private + */ + + page.dispatch = function (ctx) { + var prev = prevContext, + i = 0, + j = 0; + + prevPageContext = prevContext; + prevContext = ctx; + function nextExit() { var fn = page.exits[j++]; - if (!fn) return nextEnter(); - fn(prev, nextExit) + if (!fn) { + return nextEnter(); + } + fn(prev, nextExit); } function nextEnter() { var fn = page.callbacks[i++]; - return ctx.path !== page.current ? void(ctx.handled = !1) : fn ? void fn(ctx, nextEnter) : unhandled(ctx) + + if (ctx.path !== page.current) { + ctx.handled = false; + return; + } + if (!fn) { + return unhandled(ctx); + } + fn(ctx, nextEnter); } - var prev = prevContext, - i = 0, - j = 0; - prevPageContext = prevContext, prevContext = ctx, prev ? nextExit() : nextEnter() - }, page.exit = function(path, fn) { - if ("function" == typeof path) return page.exit("*", path); - for (var route = new Route(path), i = 1; i < arguments.length; ++i) page.exits.push(route.middleware(arguments[i])) - }, page.Context = Context; + + if (prev) { + nextExit(); + } else { + nextEnter(); + } + }; + + /** + * Unhandled `ctx`. When it's not the initial + * popstate then redirect. If you wish to handle + * 404s on your own use `page('*', callback)`. + * + * @param {Context} ctx + * @api private + */ + + function unhandled(ctx) { + if (ctx.handled) { + return; + } + var current; + + if (hashbang) { + current = base + location.hash.replace('#!', ''); + } else { + current = location.pathname + location.search; + } + + if (current === ctx.canonicalPath) { + return; + } + page.stop(); + ctx.handled = false; + location.href = ctx.canonicalPath; + } + + /** + * Register an exit route on `path` with + * callback `fn()`, which will be called + * on the previous context when a new + * page is visited. + */ + page.exit = function (path, fn) { + if (typeof path === 'function') { + return page.exit('*', path); + } + + var route = new Route(path); + for (var i = 1; i < arguments.length; ++i) { + page.exits.push(route.middleware(arguments[i])); + } + }; + + /** + * Remove URL encoding from the given `str`. + * Accommodates whitespace in both x-www-form-urlencoded + * and regular percent-encoded form. + * + * @param {str} URL component to decode + */ + function decodeURLEncodedURIComponent(val) { + if (typeof val !== 'string') { return val; } + return decodeURLComponents ? decodeURIComponent(val.replace(/\+/g, ' ')) : val; + } + + /** + * Initialize a new "request" `Context` + * with the given `path` and optional initial `state`. + * + * @param {String} path + * @param {Object} state + * @api public + */ + + function Context(path, state) { + if ('/' === path[0] && 0 !== path.indexOf(base)) { + path = base + (hashbang ? '#!' : '') + path; + } + var i = path.indexOf('?'); + + this.canonicalPath = path; + this.path = path.replace(base, '') || '/'; + if (hashbang) { + this.path = this.path.replace('#!', '') || '/'; + } + + this.title = document.title; + this.state = state || {}; + this.state.path = path; + this.querystring = ~i ? decodeURLEncodedURIComponent(path.slice(i + 1)) : ''; + this.pathname = decodeURLEncodedURIComponent(~i ? path.slice(0, i) : path); + this.params = {}; + + // fragment + this.hash = ''; + if (!hashbang) { + if (!~this.path.indexOf('#')) { + return; + } + var parts = this.path.split('#'); + this.path = parts[0]; + this.hash = decodeURLEncodedURIComponent(parts[1]) || ''; + this.querystring = this.querystring.split('#')[0]; + } + } + + /** + * Expose `Context`. + */ + + page.Context = Context; var backStack = []; - Context.prototype.pushState = function() { - page.len++, enableHistory ? history.pushState(this.state, this.title, hashbang && "/" !== this.path ? "#!" + this.path : this.canonicalPath) : backStack.push({ - state: this.state, - title: this.title, - url: hashbang && "/" !== this.path ? "#!" + this.path : this.canonicalPath, - path: this.path - }) - }, Context.prototype.save = function() { - enableHistory ? history.replaceState(this.state, this.title, hashbang && "/" !== this.path ? "#!" + this.path : this.canonicalPath) : backStack[page.len || 0] = { - state: this.state, - title: this.title, - url: hashbang && "/" !== this.path ? "#!" + this.path : this.canonicalPath, - path: this.path + + /** + * Push state. + * + * @api private + */ + + Context.prototype.pushState = function () { + page.len++; + + if (enableHistory) { + history.pushState(this.state, this.title, hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath); + } else { + backStack.push({ + state: this.state, + title: this.title, + url: (hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath), + path: this.path + }); } - }, page.Route = Route, Route.prototype.middleware = function(fn) { + }; + + /** + * Save the context state. + * + * @api public + */ + + Context.prototype.save = function () { + + if (enableHistory) { + history.replaceState(this.state, this.title, hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath); + } else { + backStack[page.len || 0] = { + state: this.state, + title: this.title, + url: (hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath), + path: this.path + }; + } + }; + + /** + * Initialize `Route` with the given HTTP `path`, + * and an array of `callbacks` and `options`. + * + * Options: + * + * - `sensitive` enable case-sensitive routes + * - `strict` enable strict matching for trailing slashes + * + * @param {String} path + * @param {Object} options. + * @api private + */ + + function Route(path, options) { + options = options || {}; + this.path = (path === '*') ? '(.*)' : path; + this.method = 'GET'; + this.regexp = pathToRegexp(this.path, + this.keys = [], + options.sensitive, + options.strict); + } + + /** + * Expose `Route`. + */ + + page.Route = Route; + + /** + * Return route middleware with + * the given callback `fn()`. + * + * @param {Function} fn + * @return {Function} + * @api public + */ + + Route.prototype.middleware = function (fn) { var self = this; - return function(ctx, next) { - if (self.match(ctx.path, ctx.params)) return fn(ctx, next); - next() - } - }, Route.prototype.match = function(path, params) { + return function (ctx, next) { + if (self.match(ctx.path, ctx.params)) { + return fn(ctx, next); + } + next(); + }; + }; + + /** + * Check if this route matches `path`, if so + * populate `params`. + * + * @param {String} path + * @param {Object} params + * @return {Boolean} + * @api private + */ + + Route.prototype.match = function (path, params) { var keys = this.keys, - qsIndex = path.indexOf("?"), + qsIndex = path.indexOf('?'), pathname = ~qsIndex ? path.slice(0, qsIndex) : path, m = this.regexp.exec(decodeURIComponent(pathname)); - if (!m) return !1; - for (var i = 1, len = m.length; i < len; ++i) { - var key = keys[i - 1], - val = decodeURLEncodedURIComponent(m[i]); - void 0 === val && hasOwnProperty.call(params, key.name) || (params[key.name] = val) + + if (!m) { + return false; } - return !0 - }; - var previousPopState = {}; - page.pushState = function(state, title, url) { - hashbang && (url = "#!" + url), history.pushState(state, title, url), previousPopState = state - }; - var onpopstate = function() { - var loaded = !1; - if ("undefined" != typeof window) return "complete" === document.readyState ? loaded = !0 : window.addEventListener("load", function() { - setTimeout(function() { - loaded = !0 - }, 0) - }), - function(e) { - if (loaded && !ignorePopState(e)) - if (e.state) { - var path = e.state.path; - page.replace(path, e.state, null, null, !0) - } else page.show(location.pathname + location.hash, void 0, void 0, !1, !0) + + for (var i = 1, len = m.length; i < len; ++i) { + var key = keys[i - 1]; + var val = decodeURLEncodedURIComponent(m[i]); + if (val !== undefined || !(hasOwnProperty.call(params, key.name))) { + params[key.name] = val; + } + } + + return true; + }; + + + var previousPopState = {}; + + function ignorePopState(event) { + + var state = event.state || {}; + + if (previousPopState.navigate === false) { + // Ignore + previousPopState = state; + return true; + } + + previousPopState = state; + return false; + } + + page.pushState = function (state, title, url) { + + if (hashbang) { + url = '#!' + url; + } + + history.pushState(state, title, url); + previousPopState = state; + }; + + /** + * Handle "populate" events. + */ + + var onpopstate = (function () { + var loaded = false; + if ('undefined' === typeof window) { + return; + } + if (document.readyState === 'complete') { + loaded = true; + } else { + window.addEventListener('load', function () { + setTimeout(function () { + loaded = true; + }, 0); + }); + } + return function onpopstate(e) { + if (!loaded) { + return; + } + if (ignorePopState(e)) { + return; + } + if (e.state) { + var path = e.state.path; + page.replace(path, e.state, null, null, true); + } else { + page.show(location.pathname + location.hash, undefined, undefined, false, true); } - }(); - page.handleAnchorClick = onclick, page.sameOrigin = sameOrigin; - var PATH_REGEXP = new RegExp(["(\\\\.)", "([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^()])+)\\))?|\\(((?:\\\\.|[^()])+)\\))([+*?])?|(\\*))"].join("|"), "g"), - isarray = Array.isArray || function(arr) { - return "[object Array]" === Object.prototype.toString.call(arr) }; - return page + })(); + /** + * Handle "click" events. + */ + + function onclick(e, checkWhich) { + + if (1 !== which(e) && checkWhich !== false) { + return; + } + + if (e.metaKey || e.ctrlKey || e.shiftKey) { + return; + } + if (e.defaultPrevented) { + return; + } + + + // ensure link + var el = e.target; + + while (el && 'A' !== el.nodeName) { + el = el.parentNode; + } + if (!el || 'A' !== el.nodeName) { + return; + } + + + // Ignore if tag has + // 1. "download" attribute + // 2. rel="external" attribute + if (el.hasAttribute('download') || el.getAttribute('rel') === 'external') { + return; + } + + // ensure non-hash for the same path + var link = el.getAttribute('href'); + if (link === '#') { + e.preventDefault(); + return; + } + + if (!hashbang && el.pathname === location.pathname && (el.hash || '#' === link)) { + return; + } + + // check target + if (el.target) { + return; + } + + // x-origin + if (!sameOrigin(el.href)) { + return; + } + + // rebuild path + var path = el.pathname + el.search + (el.hash || ''); + + // same page + var orig = path; + + if (path.indexOf(base) === 0) { + path = path.substr(base.length); + } + + if (hashbang) { + path = path.replace('#!', ''); + } + + if (base && orig === path) { + // This is causing navigation to be canceled in edge uwp + // If needed this can be changed to only be skipped when called via handleAnchorClick + //return; + } + + e.preventDefault(); + page.show(orig); + } + + page.handleAnchorClick = onclick; + + /** + * Event button. + */ + + function which(e) { + e = e || window.event; + return null === e.which ? e.button : e.which; + } + + /** + * Check if `href` is the same origin. + */ + + function sameOrigin(href) { + var origin = location.protocol + '//' + location.hostname; + if (location.port) { + origin += ':' + location.port; + } + return (href && (0 === href.indexOf(origin))); + } + + page.sameOrigin = sameOrigin; + + /** + * The main path matching regexp utility. + * + * @type {RegExp} + */ + var PATH_REGEXP = new RegExp([ + // Match escaped characters that would otherwise appear in future matches. + // This allows the user to escape special characters that won't transform. + '(\\\\.)', + // Match Express-style parameters and un-named parameters with a prefix + // and optional suffixes. Matches appear as: + // + // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined] + // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined] + // "/*" => ["/", undefined, undefined, undefined, undefined, "*"] + '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^()])+)\\))?|\\(((?:\\\\.|[^()])+)\\))([+*?])?|(\\*))' + ].join('|'), 'g'); + + /** + * Parse a string for the raw tokens. + * + * @param {String} str + * @return {Array} + */ + function parse(str) { + var tokens = []; + var key = 0; + var index = 0; + var path = ''; + var res; + + while ((res = PATH_REGEXP.exec(str)) != null) { + var m = res[0]; + var escaped = res[1]; + var offset = res.index; + path += str.slice(index, offset); + index = offset + m.length; + + // Ignore already escaped sequences. + if (escaped) { + path += escaped[1]; + continue; + } + + // Push the current path onto the tokens. + if (path) { + tokens.push(path); + path = ''; + } + + var prefix = res[2]; + var name = res[3]; + var capture = res[4]; + var group = res[5]; + var suffix = res[6]; + var asterisk = res[7]; + + var repeat = suffix === '+' || suffix === '*'; + var optional = suffix === '?' || suffix === '*'; + var delimiter = prefix || '/'; + var pattern = capture || group || (asterisk ? '.*' : '[^' + delimiter + ']+?'); + + tokens.push({ + name: name || key++, + prefix: prefix || '', + delimiter: delimiter, + optional: optional, + repeat: repeat, + pattern: escapeGroup(pattern) + }); + } + + // Match any characters still remaining. + if (index < str.length) { + path += str.substr(index); + } + + // If the path exists, push it onto the end. + if (path) { + tokens.push(path); + } + + return tokens; + } + + var isarray = Array.isArray || function (arr) { + return Object.prototype.toString.call(arr) === '[object Array]'; + }; + + /** + * Escape a regular expression string. + * + * @param {String} str + * @return {String} + */ + function escapeString(str) { + return str.replace(/([.+*?=^!:${}()[\]|\/])/g, '\\$1'); + } + + /** + * Escape the capturing group by escaping special characters and meaning. + * + * @param {String} group + * @return {String} + */ + function escapeGroup(group) { + return group.replace(/([=!:$\/()])/g, '\\$1'); + } + + /** + * Attach the keys as a property of the regexp. + * + * @param {RegExp} re + * @param {Array} keys + * @return {RegExp} + */ + function attachKeys(re, keys) { + re.keys = keys; + return re; + } + + /** + * Get the flags for a regexp from the options. + * + * @param {Object} options + * @return {String} + */ + function flags(options) { + return options.sensitive ? '' : 'i'; + } + + /** + * Pull out keys from a regexp. + * + * @param {RegExp} path + * @param {Array} keys + * @return {RegExp} + */ + function regexpToRegexp(path, keys) { + // Use a negative lookahead to match only capturing groups. + var groups = path.source.match(/\((?!\?)/g); + + if (groups) { + for (var i = 0; i < groups.length; i++) { + keys.push({ + name: i, + prefix: null, + delimiter: null, + optional: false, + repeat: false, + pattern: null + }); + } + } + + return attachKeys(path, keys); + } + + /** + * Transform an array into a regexp. + * + * @param {Array} path + * @param {Array} keys + * @param {Object} options + * @return {RegExp} + */ + function arrayToRegexp(path, keys, options) { + var parts = []; + + for (var i = 0; i < path.length; i++) { + parts.push(pathToRegexp(path[i], keys, options).source); + } + + var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options)); + + return attachKeys(regexp, keys); + } + + /** + * Create a path regexp from string input. + * + * @param {String} path + * @param {Array} keys + * @param {Object} options + * @return {RegExp} + */ + function stringToRegexp(path, keys, options) { + var tokens = parse(path); + var re = tokensToRegExp(tokens, options); + + // Attach keys back to the regexp. + for (var i = 0; i < tokens.length; i++) { + if (typeof tokens[i] !== 'string') { + keys.push(tokens[i]); + } + } + + return attachKeys(re, keys); + } + + /** + * Expose a function for taking tokens and returning a RegExp. + * + * @param {Array} tokens + * @param {Array} keys + * @param {Object} options + * @return {RegExp} + */ + function tokensToRegExp(tokens, options) { + options = options || {}; + + var strict = options.strict; + var end = options.end !== false; + var route = ''; + var lastToken = tokens[tokens.length - 1]; + var endsWithSlash = typeof lastToken === 'string' && /\/$/.test(lastToken); + + // Iterate over the tokens and create our regexp string. + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i]; + + if (typeof token === 'string') { + route += escapeString(token); + } else { + var prefix = escapeString(token.prefix); + var capture = token.pattern; + + if (token.repeat) { + capture += '(?:' + prefix + capture + ')*'; + } + + if (token.optional) { + if (prefix) { + capture = '(?:' + prefix + '(' + capture + '))?'; + } else { + capture = '(' + capture + ')?'; + } + } else { + capture = prefix + '(' + capture + ')'; + } + + route += capture; + } + } + + // In non-strict mode we allow a slash at the end of match. If the path to + // match already ends with a slash, we remove it for consistency. The slash + // is valid at the end of a path match, not in the middle. This is important + // in non-ending mode, where "/test/" shouldn't match "/test//route". + if (!strict) { + route = (endsWithSlash ? route.slice(0, -2) : route) + '(?:\\/(?=$))?'; + } + + if (end) { + route += '$'; + } else { + // In non-ending mode, we need the capturing groups to match as much as + // possible by using a positive lookahead to the end or next path segment. + route += strict && endsWithSlash ? '' : '(?=\\/|$)'; + } + + return new RegExp('^' + route, flags(options)); + } + + /** + * Normalize the given path string, returning a regular expression. + * + * An empty array can be passed in for the keys, which will hold the + * placeholder key descriptions. For example, using `/user/:id`, `keys` will + * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`. + * + * @param {(String|RegExp|Array)} path + * @param {Array} [keys] + * @param {Object} [options] + * @return {RegExp} + */ + function pathToRegexp(path, keys, options) { + keys = keys || []; + + if (!isarray(keys)) { + options = keys; + keys = []; + } else if (!options) { + options = {}; + } + + if (path instanceof RegExp) { + return regexpToRegexp(path, keys, options); + } + + if (isarray(path)) { + return arrayToRegexp(path, keys, options); + } + + return stringToRegexp(path, keys, options); + } + + return page; + }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/photoplayer/plugin.js b/src/bower_components/emby-webcomponents/photoplayer/plugin.js index 137a3520e1..6629fbfb0f 100644 --- a/src/bower_components/emby-webcomponents/photoplayer/plugin.js +++ b/src/bower_components/emby-webcomponents/photoplayer/plugin.js @@ -1,25 +1,46 @@ -define(["browser", "require", "events", "apphost", "loading", "dom", "playbackManager", "appRouter", "appSettings", "connectionManager"], function(browser, require, events, appHost, loading, dom, playbackManager, appRouter, appSettings, connectionManager) { +define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackManager', 'appRouter', 'appSettings', 'connectionManager'], function (browser, require, events, appHost, loading, dom, playbackManager, appRouter, appSettings, connectionManager) { "use strict"; function PhotoPlayer() { + var self = this; - self.name = "Photo Player", self.type = "mediaplayer", self.id = "photoplayer", self.priority = 1 + + self.name = 'Photo Player'; + self.type = 'mediaplayer'; + self.id = 'photoplayer'; + + // Let any players created by plugins take priority + self.priority = 1; } - return PhotoPlayer.prototype.play = function(options) { - return new Promise(function(resolve, reject) { - require(["slideshow"], function(slideshow) { + + PhotoPlayer.prototype.play = function (options) { + + return new Promise(function (resolve, reject) { + + require(['slideshow'], function (slideshow) { + var index = options.startIndex || 0; - new slideshow({ - showTitle: !1, - cover: !1, + + var newSlideShow = new slideshow({ + showTitle: false, + cover: false, items: options.items, startIndex: index, - interval: 11e3, - interactive: !0 - }).show(), resolve() - }) - }) - }, PhotoPlayer.prototype.canPlayMediaType = function(mediaType) { - return "photo" === (mediaType || "").toLowerCase() - }, PhotoPlayer + interval: 11000, + interactive: true + }); + + newSlideShow.show(); + + resolve(); + }); + }); + }; + + PhotoPlayer.prototype.canPlayMediaType = function (mediaType) { + + return (mediaType || '').toLowerCase() === 'photo'; + }; + + return PhotoPlayer; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/playback/autoplaydetect.js b/src/bower_components/emby-webcomponents/playback/autoplaydetect.js index 63b24cfeeb..7a7a73a538 100644 --- a/src/bower_components/emby-webcomponents/playback/autoplaydetect.js +++ b/src/bower_components/emby-webcomponents/playback/autoplaydetect.js @@ -1,28 +1,63 @@ -define([], function() { - "use strict"; +define([], function () { + 'use strict'; function supportsHtmlMediaAutoplay() { - return new Promise(function(resolve, reject) { - var timeout, elem = document.createElement("video"), - elemStyle = elem.style; - if (!("autoplay" in elem)) return void reject(); - elemStyle.position = "absolute", elemStyle.height = 0, elemStyle.width = 0, elem.setAttribute("autoplay", "autoplay"), elem.style.display = "none", document.body.appendChild(elem); - var testAutoplay = function(arg) { - clearTimeout(timeout), elem.removeEventListener("playing", testAutoplay), elem.removeEventListener("play", testAutoplay); - var supported = arg && "playing" === arg.type || arg && "play" === arg.type || 0 !== elem.currentTime; - elem.parentNode.removeChild(elem), supported ? resolve() : reject() - }; - elem.addEventListener("play", testAutoplay), elem.addEventListener("playing", testAutoplay); - try { - elem.src = "data:video/mp4;base64,AAAAHGZ0eXBtcDQyAAAAAG1wNDJpc29tYXZjMQAAAz5tb292AAAAbG12aGQAAAAAzaNacc2jWnEAAV+QAAFfkAABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAGGlvZHMAAAAAEICAgAcAT////3//AAACQ3RyYWsAAABcdGtoZAAAAAHNo1pxzaNacQAAAAEAAAAAAAFfkAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAEAAAABAAAAAAAd9tZGlhAAAAIG1kaGQAAAAAzaNacc2jWnEAAV+QAAFfkFXEAAAAAAAhaGRscgAAAAAAAAAAdmlkZQAAAAAAAAAAAAAAAAAAAAGWbWluZgAAABR2bWhkAAAAAQAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADHVybCAAAAABAAABVnN0YmwAAACpc3RzZAAAAAAAAAABAAAAmWF2YzEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAEAAQAEgAAABIAAAAAAAAAAEOSlZUL0FWQyBDb2RpbmcAAAAAAAAAAAAAAAAAAAAAAAAY//8AAAAxYXZjQwH0AAr/4QAZZ/QACq609NQYBBkAAAMAAQAAAwAKjxImoAEABWjOAa8gAAAAEmNvbHJuY2xjAAYAAQAGAAAAGHN0dHMAAAAAAAAAAQAAAAUAAEZQAAAAKHN0c3oAAAAAAAAAAAAAAAUAAAIqAAAACAAAAAgAAAAIAAAACAAAAChzdHNjAAAAAAAAAAIAAAABAAAABAAAAAEAAAACAAAAAQAAAAEAAAAYc3RjbwAAAAAAAAACAAADYgAABaQAAAAUc3RzcwAAAAAAAAABAAAAAQAAABFzZHRwAAAAAAREREREAAAAb3VkdGEAAABnbWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcgAAAAAAAAAAAAAAAAAAAAA6aWxzdAAAADKpdG9vAAAAKmRhdGEAAAABAAAAAEhhbmRCcmFrZSAwLjkuOCAyMDEyMDcxODAwAAACUm1kYXQAAAHkBgX/4NxF6b3m2Ui3lizYINkj7u94MjY0IC0gY29yZSAxMjAgLSBILjI2NC9NUEVHLTQgQVZDIGNvZGVjIC0gQ29weWxlZnQgMjAwMy0yMDExIC0gaHR0cDovL3d3dy52aWRlb2xhbi5vcmcveDI2NC5odG1sIC0gb3B0aW9uczogY2FiYWM9MCByZWY9MSBkZWJsb2NrPTE6MDowIGFuYWx5c2U9MHgxOjAgbWU9ZXNhIHN1Ym1lPTkgcHN5PTAgbWl4ZWRfcmVmPTAgbWVfcmFuZ2U9NCBjaHJvbWFfbWU9MSB0cmVsbGlzPTAgOHg4ZGN0PTAgY3FtPTAgZGVhZHpvbmU9MjEsMTEgZmFzdF9wc2tpcD0wIGNocm9tYV9xcF9vZmZzZXQ9MCB0aHJlYWRzPTYgc2xpY2VkX3RocmVhZHM9MCBucj0wIGRlY2ltYXRlPTEgaW50ZXJsYWNlZD0wIGJsdXJheV9jb21wYXQ9MCBjb25zdHJhaW5lZF9pbnRyYT0wIGJmcmFtZXM9MCB3ZWlnaHRwPTAga2V5aW50PTUwIGtleWludF9taW49NSBzY2VuZWN1dD00MCBpbnRyYV9yZWZyZXNoPTAgcmM9Y3FwIG1idHJlZT0wIHFwPTAAgAAAAD5liISscR8A+E4ACAACFoAAITAAAgsAAPgYCoKgoC+L4vi+KAvi+L4YfAEAACMzgABF9AAEUGUgABDJiXnf4AAAAARBmiKUAAAABEGaQpQAAAAEQZpilAAAAARBmoKU"; - var promise = elem.play(); - promise && promise.catch && promise.catch(reject), timeout = setTimeout(testAutoplay, 500) - } catch (e) { - return void reject() + + return new Promise(function (resolve, reject) { + + var timeout; + var elem = document.createElement('video'); + var elemStyle = elem.style; + //skip the test if video itself, or the autoplay + //element on it isn't supported + if (!('autoplay' in elem)) { + reject(); + return; } - }) + elemStyle.position = 'absolute'; + elemStyle.height = 0; + elemStyle.width = 0; + + elem.setAttribute('autoplay', 'autoplay'); + elem.style.display = 'none'; + document.body.appendChild(elem); + + var testAutoplay = function (arg) { + clearTimeout(timeout); + elem.removeEventListener('playing', testAutoplay); + elem.removeEventListener('play', testAutoplay); + var supported = (arg && arg.type === 'playing') || (arg && arg.type === 'play') || elem.currentTime !== 0; + elem.parentNode.removeChild(elem); + + if (supported) { + resolve(); + } else { + reject(); + } + }; + + // play needed for firefox + elem.addEventListener('play', testAutoplay); + elem.addEventListener('playing', testAutoplay); + + try { + elem.src = 'data:video/mp4;base64,AAAAHGZ0eXBtcDQyAAAAAG1wNDJpc29tYXZjMQAAAz5tb292AAAAbG12aGQAAAAAzaNacc2jWnEAAV+QAAFfkAABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAGGlvZHMAAAAAEICAgAcAT////3//AAACQ3RyYWsAAABcdGtoZAAAAAHNo1pxzaNacQAAAAEAAAAAAAFfkAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAEAAAABAAAAAAAd9tZGlhAAAAIG1kaGQAAAAAzaNacc2jWnEAAV+QAAFfkFXEAAAAAAAhaGRscgAAAAAAAAAAdmlkZQAAAAAAAAAAAAAAAAAAAAGWbWluZgAAABR2bWhkAAAAAQAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADHVybCAAAAABAAABVnN0YmwAAACpc3RzZAAAAAAAAAABAAAAmWF2YzEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAEAAQAEgAAABIAAAAAAAAAAEOSlZUL0FWQyBDb2RpbmcAAAAAAAAAAAAAAAAAAAAAAAAY//8AAAAxYXZjQwH0AAr/4QAZZ/QACq609NQYBBkAAAMAAQAAAwAKjxImoAEABWjOAa8gAAAAEmNvbHJuY2xjAAYAAQAGAAAAGHN0dHMAAAAAAAAAAQAAAAUAAEZQAAAAKHN0c3oAAAAAAAAAAAAAAAUAAAIqAAAACAAAAAgAAAAIAAAACAAAAChzdHNjAAAAAAAAAAIAAAABAAAABAAAAAEAAAACAAAAAQAAAAEAAAAYc3RjbwAAAAAAAAACAAADYgAABaQAAAAUc3RzcwAAAAAAAAABAAAAAQAAABFzZHRwAAAAAAREREREAAAAb3VkdGEAAABnbWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcgAAAAAAAAAAAAAAAAAAAAA6aWxzdAAAADKpdG9vAAAAKmRhdGEAAAABAAAAAEhhbmRCcmFrZSAwLjkuOCAyMDEyMDcxODAwAAACUm1kYXQAAAHkBgX/4NxF6b3m2Ui3lizYINkj7u94MjY0IC0gY29yZSAxMjAgLSBILjI2NC9NUEVHLTQgQVZDIGNvZGVjIC0gQ29weWxlZnQgMjAwMy0yMDExIC0gaHR0cDovL3d3dy52aWRlb2xhbi5vcmcveDI2NC5odG1sIC0gb3B0aW9uczogY2FiYWM9MCByZWY9MSBkZWJsb2NrPTE6MDowIGFuYWx5c2U9MHgxOjAgbWU9ZXNhIHN1Ym1lPTkgcHN5PTAgbWl4ZWRfcmVmPTAgbWVfcmFuZ2U9NCBjaHJvbWFfbWU9MSB0cmVsbGlzPTAgOHg4ZGN0PTAgY3FtPTAgZGVhZHpvbmU9MjEsMTEgZmFzdF9wc2tpcD0wIGNocm9tYV9xcF9vZmZzZXQ9MCB0aHJlYWRzPTYgc2xpY2VkX3RocmVhZHM9MCBucj0wIGRlY2ltYXRlPTEgaW50ZXJsYWNlZD0wIGJsdXJheV9jb21wYXQ9MCBjb25zdHJhaW5lZF9pbnRyYT0wIGJmcmFtZXM9MCB3ZWlnaHRwPTAga2V5aW50PTUwIGtleWludF9taW49NSBzY2VuZWN1dD00MCBpbnRyYV9yZWZyZXNoPTAgcmM9Y3FwIG1idHJlZT0wIHFwPTAAgAAAAD5liISscR8A+E4ACAACFoAAITAAAgsAAPgYCoKgoC+L4vi+KAvi+L4YfAEAACMzgABF9AAEUGUgABDJiXnf4AAAAARBmiKUAAAABEGaQpQAAAAEQZpilAAAAARBmoKU'; + var promise = elem.play(); + if (promise && promise.catch) { + promise.catch(reject); + } + + timeout = setTimeout(testAutoplay, 500); + } + + catch (e) { + reject(); + return; + } + }); } + return { supportsHtmlMediaAutoplay: supportsHtmlMediaAutoplay - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/playback/brightnessosd.js b/src/bower_components/emby-webcomponents/playback/brightnessosd.js index 9b53743217..4885d2e4e1 100644 --- a/src/bower_components/emby-webcomponents/playback/brightnessosd.js +++ b/src/bower_components/emby-webcomponents/playback/brightnessosd.js @@ -1,63 +1,165 @@ -define(["events", "playbackManager", "dom", "browser", "css!./iconosd", "material-icons"], function(events, playbackManager, dom, browser) { - "use strict"; +define(['events', 'playbackManager', 'dom', 'browser', 'css!./iconosd', 'material-icons'], function (events, playbackManager, dom, browser) { + 'use strict'; + + var currentPlayer; + var osdElement; + var iconElement; + var progressElement; + + var enableAnimation; function getOsdElementHtml() { - var html = ""; - return html += '', html += '
' + var html = ''; + + html += ''; + + html += '
'; + + return html; } function ensureOsdElement() { + var elem = osdElement; - elem || (enableAnimation = browser.supportsCssAnimation(), elem = document.createElement("div"), elem.classList.add("hide"), elem.classList.add("iconOsd"), elem.classList.add("iconOsd-hidden"), elem.classList.add("brightnessOsd"), elem.innerHTML = getOsdElementHtml(), iconElement = elem.querySelector("i"), progressElement = elem.querySelector(".iconOsdProgressInner"), document.body.appendChild(elem), osdElement = elem) + if (!elem) { + + enableAnimation = browser.supportsCssAnimation(); + + elem = document.createElement('div'); + elem.classList.add('hide'); + elem.classList.add('iconOsd'); + elem.classList.add('iconOsd-hidden'); + elem.classList.add('brightnessOsd'); + elem.innerHTML = getOsdElementHtml(); + + iconElement = elem.querySelector('i'); + progressElement = elem.querySelector('.iconOsdProgressInner'); + + document.body.appendChild(elem); + osdElement = elem; + } } function onHideComplete() { - this.classList.add("hide") + this.classList.add('hide'); } + var hideTimeout; function showOsd() { + clearHideTimeout(); + var elem = osdElement; + dom.removeEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { - once: !0 - }), elem.classList.remove("hide"), elem.offsetWidth, requestAnimationFrame(function() { - elem.classList.remove("iconOsd-hidden"), hideTimeout = setTimeout(hideOsd, 3e3) - }) + once: true + }); + + elem.classList.remove('hide'); + + // trigger reflow + void elem.offsetWidth; + + requestAnimationFrame(function () { + elem.classList.remove('iconOsd-hidden'); + + hideTimeout = setTimeout(hideOsd, 3000); + }); } function clearHideTimeout() { - hideTimeout && (clearTimeout(hideTimeout), hideTimeout = null) + if (hideTimeout) { + clearTimeout(hideTimeout); + hideTimeout = null; + } } function hideOsd() { + clearHideTimeout(); + var elem = osdElement; - elem && (enableAnimation ? (elem.offsetWidth, requestAnimationFrame(function() { - elem.classList.add("iconOsd-hidden"), dom.addEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { - once: !0 - }) - })) : onHideComplete.call(elem)) + if (elem) { + + if (enableAnimation) { + // trigger reflow + void elem.offsetWidth; + + requestAnimationFrame(function () { + elem.classList.add('iconOsd-hidden'); + + dom.addEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { + once: true + }); + }); + } else { + onHideComplete.call(elem); + } + } } function updateElementsFromPlayer(brightness) { - iconElement && (iconElement.innerHTML = brightness >= 80 ? "" : brightness >= 20 ? "" : ""), progressElement && (progressElement.style.width = (brightness || 0) + "%") + + if (iconElement) { + if (brightness >= 80) { + iconElement.innerHTML = ''; + } + else if (brightness >= 20) { + iconElement.innerHTML = ''; + } else { + iconElement.innerHTML = ''; + } + } + if (progressElement) { + progressElement.style.width = (brightness || 0) + '%'; + } } function releaseCurrentPlayer() { + var player = currentPlayer; - player && (events.off(player, "brightnesschange", onBrightnessChanged), events.off(player, "playbackstop", hideOsd), currentPlayer = null) + + if (player) { + events.off(player, 'brightnesschange', onBrightnessChanged); + events.off(player, 'playbackstop', hideOsd); + currentPlayer = null; + } } function onBrightnessChanged(e) { + var player = this; - ensureOsdElement(), updateElementsFromPlayer(playbackManager.getBrightness(player)), showOsd() + + ensureOsdElement(); + + updateElementsFromPlayer(playbackManager.getBrightness(player)); + + showOsd(); } function bindToPlayer(player) { - player !== currentPlayer && (releaseCurrentPlayer(), currentPlayer = player, player && (hideOsd(), events.on(player, "brightnesschange", onBrightnessChanged), events.on(player, "playbackstop", hideOsd))) + + if (player === currentPlayer) { + return; + } + + releaseCurrentPlayer(); + + currentPlayer = player; + + if (!player) { + return; + } + + hideOsd(); + events.on(player, 'brightnesschange', onBrightnessChanged); + events.on(player, 'playbackstop', hideOsd); } - var currentPlayer, osdElement, iconElement, progressElement, enableAnimation, hideTimeout; - events.on(playbackManager, "playerchange", function() { - bindToPlayer(playbackManager.getCurrentPlayer()) - }), bindToPlayer(playbackManager.getCurrentPlayer()) + + events.on(playbackManager, 'playerchange', function () { + bindToPlayer(playbackManager.getCurrentPlayer()); + }); + + bindToPlayer(playbackManager.getCurrentPlayer()); + }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/playback/experimentalwarnings.js b/src/bower_components/emby-webcomponents/playback/experimentalwarnings.js index dd43fe47db..17b1af0257 100644 --- a/src/bower_components/emby-webcomponents/playback/experimentalwarnings.js +++ b/src/bower_components/emby-webcomponents/playback/experimentalwarnings.js @@ -1,41 +1,97 @@ -define(["connectionManager", "globalize", "userSettings", "apphost"], function(connectionManager, globalize, userSettings, appHost) { +define(['connectionManager', 'globalize', 'userSettings', 'apphost'], function (connectionManager, globalize, userSettings, appHost) { "use strict"; + function getRequirePromise(deps) { + + return new Promise(function (resolve, reject) { + + require(deps, resolve); + }); + } + + // https://stackoverflow.com/questions/6117814/get-week-of-year-in-javascript-like-in-php function getWeek(date) { - var d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())), - dayNum = d.getUTCDay() || 7; + var d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())); + var dayNum = d.getUTCDay() || 7; d.setUTCDate(d.getUTCDate() + 4 - dayNum); var yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1)); - return Math.ceil(((d - yearStart) / 864e5 + 1) / 7) + return Math.ceil((((d - yearStart) / 86400000) + 1) / 7); } function showMessage(text, userSettingsKey, appHostFeature) { - if (appHost.supports(appHostFeature)) return Promise.resolve(); - var now = new Date; - return userSettingsKey += now.getFullYear() + "-w" + getWeek(now), "1" === userSettings.get(userSettingsKey, !1) ? Promise.resolve() : new Promise(function(resolve, reject) { - userSettings.set(userSettingsKey, "1", !1), require(["alert"], function(alert) { - return alert(text).then(resolve, resolve) - }) - }) + + if (appHost.supports(appHostFeature)) { + return Promise.resolve(); + } + + var now = new Date(); + + userSettingsKey += now.getFullYear() + '-w' + getWeek(now); + + if (userSettings.get(userSettingsKey, false) === '1') { + return Promise.resolve(); + } + + return new Promise(function (resolve, reject) { + + userSettings.set(userSettingsKey, '1', false); + + require(['alert'], function (alert) { + + return alert(text).then(resolve, resolve); + }); + }); } function showBlurayMessage() { - return showMessage("Playback of Bluray folders in this app is experimental. Some titles may not work at all. For a better experience, consider converting to mkv video files, or use an Jellyfin app with native Bluray folder support.", "blurayexpirementalinfo", "nativeblurayplayback") + + var message = + 'Playback of Bluray folders in this app is experimental. Some titles may not work at all. For a better experience, consider converting to mkv video files, or use an Jellyfin app with native Bluray folder support.'; + return showMessage(message, 'blurayexpirementalinfo', 'nativeblurayplayback'); } function showDvdMessage() { - return showMessage("Playback of Dvd folders in this app is experimental. Some titles may not work at all. For a better experience, consider converting to mkv video files, or use an Jellyfin app with native Dvd folder support.", "dvdexpirementalinfo", "nativedvdplayback") + + var message = + 'Playback of Dvd folders in this app is experimental. Some titles may not work at all. For a better experience, consider converting to mkv video files, or use an Jellyfin app with native Dvd folder support.'; + return showMessage(message, 'dvdexpirementalinfo', 'nativedvdplayback'); } function showIsoMessage() { - return showMessage("Playback of ISO files in this app is experimental. Some titles may not work at all. For a better experience, consider converting to mkv video files, or use an Jellyfin app with native ISO support.", "isoexpirementalinfo", "nativeisoplayback") + + var message = + 'Playback of ISO files in this app is experimental. Some titles may not work at all. For a better experience, consider converting to mkv video files, or use an Jellyfin app with native ISO support.'; + return showMessage(message, 'isoexpirementalinfo', 'nativeisoplayback'); } function ExpirementalPlaybackWarnings() { - this.name = "Experimental playback warnings", this.type = "preplayintercept", this.id = "expirementalplaybackwarnings" + + this.name = 'Experimental playback warnings'; + this.type = 'preplayintercept'; + this.id = 'expirementalplaybackwarnings'; } - return ExpirementalPlaybackWarnings.prototype.intercept = function(options) { + + ExpirementalPlaybackWarnings.prototype.intercept = function (options) { + var item = options.item; - return item ? "Iso" === item.VideoType ? showIsoMessage() : "BluRay" === item.VideoType ? showBlurayMessage() : "Dvd" === item.VideoType ? showDvdMessage() : Promise.resolve() : Promise.resolve() - }, ExpirementalPlaybackWarnings -}); + if (!item) { + return Promise.resolve(); + } + + if (item.VideoType === 'Iso') { + return showIsoMessage(); + } + + if (item.VideoType === 'BluRay') { + return showBlurayMessage(); + } + + if (item.VideoType === 'Dvd') { + return showDvdMessage(); + } + + return Promise.resolve(); + }; + + return ExpirementalPlaybackWarnings; +}); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/playback/iconosd.css b/src/bower_components/emby-webcomponents/playback/iconosd.css index d3034a6abe..3d0c8e5805 100644 --- a/src/bower_components/emby-webcomponents/playback/iconosd.css +++ b/src/bower_components/emby-webcomponents/playback/iconosd.css @@ -1,4 +1,4 @@ -.iconOsd { +.iconOsd { position: fixed; top: 7%; right: 3%; @@ -8,38 +8,33 @@ padding: 1em; color: #fff; backdrop-filter: blur(5px); - -webkit-border-radius: .25em; border-radius: .25em; - -webkit-transition: opacity .2s ease-out; - -o-transition: opacity .2s ease-out; - transition: opacity .2s ease-out + transition: opacity 200ms ease-out; } .iconOsd-hidden { - opacity: 0 + opacity: 0; } .iconOsdIcon { font-size: 320%; display: block; - margin: .25em .7em + margin: .25em .7em; } .iconOsdProgressOuter { margin: 1.5em .25em 1em; height: .35em; background: #222; - -webkit-border-radius: .25em; - border-radius: .25em + border-radius: .25em; } .iconOsdProgressInner { background: #00a4dc; height: 100%; - -webkit-border-radius: .25em; - border-radius: .25em + border-radius: .25em; } .brightnessOsdProgressInner { - background: #FF9800 + background: #FF9800; } \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/playback/mediasession.js b/src/bower_components/emby-webcomponents/playback/mediasession.js index 15c6f9cc23..8198a95520 100644 --- a/src/bower_components/emby-webcomponents/playback/mediasession.js +++ b/src/bower_components/emby-webcomponents/playback/mediasession.js @@ -1,54 +1,136 @@ -define(["playbackManager", "nowPlayingHelper", "events", "connectionManager"], function(playbackManager, nowPlayingHelper, events, connectionManager) { +define(['playbackManager', 'nowPlayingHelper', 'events', 'connectionManager'], function (playbackManager, nowPlayingHelper, events, connectionManager) { "use strict"; + // Reports media playback to the device for lock screen control + + var currentPlayer; + var lastUpdateTime = 0; + function seriesImageUrl(item, options) { - if ("Episode" !== item.Type) return null; - if (options = options || {}, options.type = options.type || "Primary", "Primary" === options.type && item.SeriesPrimaryImageTag) return options.tag = item.SeriesPrimaryImageTag, connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); - if ("Thumb" === options.type) { - if (item.SeriesThumbImageTag) return options.tag = item.SeriesThumbImageTag, connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); - if (item.ParentThumbImageTag) return options.tag = item.ParentThumbImageTag, connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.ParentThumbItemId, options) + + if (item.Type !== 'Episode') { + return null; } - return null + + options = options || {}; + options.type = options.type || "Primary"; + + if (options.type === 'Primary') { + + if (item.SeriesPrimaryImageTag) { + + options.tag = item.SeriesPrimaryImageTag; + + return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); + } + } + + if (options.type === 'Thumb') { + + if (item.SeriesThumbImageTag) { + + options.tag = item.SeriesThumbImageTag; + + return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); + } + if (item.ParentThumbImageTag) { + + options.tag = item.ParentThumbImageTag; + + return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.ParentThumbItemId, options); + } + } + + return null; } function imageUrl(item, options) { - return options = options || {}, options.type = options.type || "Primary", item.ImageTags && item.ImageTags[options.type] ? (options.tag = item.ImageTags[options.type], connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.Id, options)) : item.AlbumId && item.AlbumPrimaryImageTag ? (options.tag = item.AlbumPrimaryImageTag, connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.AlbumId, options)) : null + + options = options || {}; + options.type = options.type || "Primary"; + + if (item.ImageTags && item.ImageTags[options.type]) { + + options.tag = item.ImageTags[options.type]; + return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.Id, options); + } + + if (item.AlbumId && item.AlbumPrimaryImageTag) { + + options.tag = item.AlbumPrimaryImageTag; + return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.AlbumId, options); + } + + return null; } function pushImageUrl(item, height, list) { + var imageOptions = { - height: height - }, - url = seriesImageUrl(item, imageOptions) || imageUrl(item, imageOptions); - url && list.push({ - src: url, - sizes: height + "x" + height - }) + height: height + }; + + var url = seriesImageUrl(item, imageOptions) || imageUrl(item, imageOptions); + if (url) { + list.push({ src: url, sizes: height + 'x' + height }); + } } function getImageUrls(item) { + var list = []; - return pushImageUrl(item, 96, list), pushImageUrl(item, 128, list), pushImageUrl(item, 192, list), pushImageUrl(item, 256, list), pushImageUrl(item, 384, list), pushImageUrl(item, 512, list), list + + pushImageUrl(item, 96, list); + pushImageUrl(item, 128, list); + pushImageUrl(item, 192, list); + pushImageUrl(item, 256, list); + pushImageUrl(item, 384, list); + pushImageUrl(item, 512, list); + + return list; } function updatePlayerState(player, state, eventName) { + var item = state.NowPlayingItem; - if (!item) return void hideMediaControls(); - var playState = state.PlayState || {}, - parts = nowPlayingHelper.getNowPlayingNames(item), - artist = 1 === parts.length ? "" : parts[0].text, - title = parts[parts.length - 1].text; - if ("Video" === item.MediaType && parts.length > 1) { - var temp = artist; - artist = title, title = temp + + if (!item) { + hideMediaControls(); + return; } + + var playState = state.PlayState || {}; + + var parts = nowPlayingHelper.getNowPlayingNames(item); + + var artist = parts.length === 1 ? '' : parts[0].text; + var title = parts[parts.length - 1].text; + + var isVideo = item.MediaType === 'Video'; + + // Switch these two around for video + if (isVideo && parts.length > 1) { + var temp = artist; + artist = title; + title = temp; + } + var albumArtist; - item.AlbumArtists && item.AlbumArtists[0] && (albumArtist = item.AlbumArtists[0].Name); - var album = item.Album || "", - itemId = item.Id, - duration = parseInt(item.RunTimeTicks ? item.RunTimeTicks / 1e4 : 0), - currentTime = parseInt(playState.PositionTicks ? playState.PositionTicks / 1e4 : 0), - isPaused = playState.IsPaused || !1; + + if (item.AlbumArtists && item.AlbumArtists[0]) { + albumArtist = item.AlbumArtists[0].Name; + } + + var album = item.Album || ''; + var itemId = item.Id; + + // Convert to ms + var duration = parseInt(item.RunTimeTicks ? (item.RunTimeTicks / 10000) : 0); + var currentTime = parseInt(playState.PositionTicks ? (playState.PositionTicks / 10000) : 0); + + var isPaused = playState.IsPaused || false; + var canSeek = playState.CanSeek || false; + navigator.mediaSession.metadata = new MediaMetadata({ title: title, artist: artist, @@ -60,58 +142,111 @@ define(["playbackManager", "nowPlayingHelper", "events", "connectionManager"], f paused: isPaused, itemId: itemId, mediaType: item.MediaType - }) + }); } function onGeneralEvent(e) { + var player = this; - updatePlayerState(player, playbackManager.getPlayerState(player), e.type) + var state = playbackManager.getPlayerState(player); + + updatePlayerState(player, state, e.type); } function onStateChanged(e, state) { - updatePlayerState(this, state, "statechange") + + var player = this; + updatePlayerState(player, state, 'statechange'); } function onPlaybackStart(e, state) { - updatePlayerState(this, state, e.type) + + var player = this; + + updatePlayerState(player, state, e.type); } function onPlaybackStopped(e, state) { - hideMediaControls() + + var player = this; + + hideMediaControls(); } function releaseCurrentPlayer() { - currentPlayer && (events.off(currentPlayer, "playbackstart", onPlaybackStart), events.off(currentPlayer, "playbackstop", onPlaybackStopped), events.off(currentPlayer, "unpause", onGeneralEvent), events.off(currentPlayer, "pause", onGeneralEvent), events.off(currentPlayer, "statechange", onStateChanged), events.off(currentPlayer, "timeupdate", onGeneralEvent), currentPlayer = null, hideMediaControls()) - } - function hideMediaControls() { - navigator.mediaSession.metadata = null - } + if (currentPlayer) { - function bindToPlayer(player) { - if (releaseCurrentPlayer(), player) { - currentPlayer = player; - updatePlayerState(player, playbackManager.getPlayerState(player), "init"), events.on(currentPlayer, "playbackstart", onPlaybackStart), events.on(currentPlayer, "playbackstop", onPlaybackStopped), events.on(currentPlayer, "unpause", onGeneralEvent), events.on(currentPlayer, "pause", onGeneralEvent), events.on(currentPlayer, "statechange", onStateChanged), events.on(currentPlayer, "timeupdate", onGeneralEvent) + events.off(currentPlayer, 'playbackstart', onPlaybackStart); + events.off(currentPlayer, 'playbackstop', onPlaybackStopped); + events.off(currentPlayer, 'unpause', onGeneralEvent); + events.off(currentPlayer, 'pause', onGeneralEvent); + events.off(currentPlayer, 'statechange', onStateChanged); + events.off(currentPlayer, 'timeupdate', onGeneralEvent); + + currentPlayer = null; + + hideMediaControls(); } } - function execute(name) { - playbackManager[name](currentPlayer) + function hideMediaControls() { + navigator.mediaSession.metadata = null; } - var currentPlayer; - navigator.mediaSession.setActionHandler("previoustrack", function() { - execute("previousTrack") - }), navigator.mediaSession.setActionHandler("nexttrack", function() { - execute("nextTrack") - }), navigator.mediaSession.setActionHandler("play", function() { - execute("unpause") - }), navigator.mediaSession.setActionHandler("pause", function() { - execute("pause") - }), navigator.mediaSession.setActionHandler("seekbackward", function() { - execute("rewind") - }), navigator.mediaSession.setActionHandler("seekforward", function() { - execute("fastForward") - }), events.on(playbackManager, "playerchange", function() { - bindToPlayer(playbackManager.getCurrentPlayer()) - }), bindToPlayer(playbackManager.getCurrentPlayer()) + + function bindToPlayer(player) { + + releaseCurrentPlayer(); + + if (!player) { + return; + } + + currentPlayer = player; + + var state = playbackManager.getPlayerState(player); + updatePlayerState(player, state, 'init'); + + events.on(currentPlayer, 'playbackstart', onPlaybackStart); + events.on(currentPlayer, 'playbackstop', onPlaybackStopped); + events.on(currentPlayer, 'unpause', onGeneralEvent); + events.on(currentPlayer, 'pause', onGeneralEvent); + events.on(currentPlayer, 'statechange', onStateChanged); + events.on(currentPlayer, 'timeupdate', onGeneralEvent); + } + + function execute(name) { + playbackManager[name](currentPlayer); + } + + navigator.mediaSession.setActionHandler('previoustrack', function () { + execute('previousTrack'); + }); + + navigator.mediaSession.setActionHandler('nexttrack', function () { + execute('nextTrack'); + }); + + navigator.mediaSession.setActionHandler('play', function () { + execute('unpause'); + }); + + navigator.mediaSession.setActionHandler('pause', function () { + execute('pause'); + }); + + navigator.mediaSession.setActionHandler('seekbackward', function () { + execute('rewind'); + }); + + navigator.mediaSession.setActionHandler('seekforward', function () { + execute('fastForward'); + }); + + events.on(playbackManager, 'playerchange', function () { + + bindToPlayer(playbackManager.getCurrentPlayer()); + }); + + bindToPlayer(playbackManager.getCurrentPlayer()); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/playback/nowplayinghelper.js b/src/bower_components/emby-webcomponents/playback/nowplayinghelper.js index 0e79788291..d5803b426f 100644 --- a/src/bower_components/emby-webcomponents/playback/nowplayinghelper.js +++ b/src/bower_components/emby-webcomponents/playback/nowplayinghelper.js @@ -1,40 +1,88 @@ -define([], function() { - "use strict"; +define([], function () { + 'use strict'; function getNowPlayingNames(nowPlayingItem, includeNonNameInfo) { - var topItem = nowPlayingItem, - bottomItem = null, - topText = nowPlayingItem.Name; - nowPlayingItem.AlbumId && "Audio" === nowPlayingItem.MediaType && (topItem = { - Id: nowPlayingItem.AlbumId, - Name: nowPlayingItem.Album, - Type: "MusicAlbum", - IsFolder: !0 - }), "Video" === nowPlayingItem.MediaType && (null != nowPlayingItem.IndexNumber && (topText = nowPlayingItem.IndexNumber + " - " + topText), null != nowPlayingItem.ParentIndexNumber && (topText = nowPlayingItem.ParentIndexNumber + "." + topText)); - var bottomText = ""; - nowPlayingItem.ArtistItems && nowPlayingItem.ArtistItems.length ? (bottomItem = { - Id: nowPlayingItem.ArtistItems[0].Id, - Name: nowPlayingItem.ArtistItems[0].Name, - Type: "MusicArtist", - IsFolder: !0 - }, bottomText = nowPlayingItem.ArtistItems.map(function(a) { - return a.Name - }).join(", ")) : nowPlayingItem.Artists && nowPlayingItem.Artists.length ? bottomText = nowPlayingItem.Artists.join(", ") : nowPlayingItem.SeriesName || nowPlayingItem.Album ? (bottomText = topText, topText = nowPlayingItem.SeriesName || nowPlayingItem.Album, bottomItem = topItem, topItem = nowPlayingItem.SeriesId ? { - Id: nowPlayingItem.SeriesId, - Name: nowPlayingItem.SeriesName, - Type: "Series", - IsFolder: !0 - } : null) : nowPlayingItem.ProductionYear && !1 !== includeNonNameInfo && (bottomText = nowPlayingItem.ProductionYear); + + var topItem = nowPlayingItem; + var bottomItem = null; + var topText = nowPlayingItem.Name; + + if (nowPlayingItem.AlbumId && nowPlayingItem.MediaType === 'Audio') { + topItem = { + Id: nowPlayingItem.AlbumId, + Name: nowPlayingItem.Album, + Type: 'MusicAlbum', + IsFolder: true + }; + } + + if (nowPlayingItem.MediaType === 'Video') { + if (nowPlayingItem.IndexNumber != null) { + topText = nowPlayingItem.IndexNumber + " - " + topText; + } + if (nowPlayingItem.ParentIndexNumber != null) { + topText = nowPlayingItem.ParentIndexNumber + "." + topText; + } + } + + var bottomText = ''; + + if (nowPlayingItem.ArtistItems && nowPlayingItem.ArtistItems.length) { + + bottomItem = { + Id: nowPlayingItem.ArtistItems[0].Id, + Name: nowPlayingItem.ArtistItems[0].Name, + Type: 'MusicArtist', + IsFolder: true + }; + + bottomText = nowPlayingItem.ArtistItems.map(function (a) { + return a.Name; + }).join(', '); + + } else if (nowPlayingItem.Artists && nowPlayingItem.Artists.length) { + + bottomText = nowPlayingItem.Artists.join(', '); + } + else if (nowPlayingItem.SeriesName || nowPlayingItem.Album) { + bottomText = topText; + topText = nowPlayingItem.SeriesName || nowPlayingItem.Album; + + bottomItem = topItem; + + if (nowPlayingItem.SeriesId) { + topItem = { + Id: nowPlayingItem.SeriesId, + Name: nowPlayingItem.SeriesName, + Type: 'Series', + IsFolder: true + }; + } else { + topItem = null; + } + } + else if (nowPlayingItem.ProductionYear && includeNonNameInfo !== false) { + bottomText = nowPlayingItem.ProductionYear; + } + var list = []; - return list.push({ + + list.push({ text: topText, item: topItem - }), bottomText && list.push({ - text: bottomText, - item: bottomItem - }), list + }); + + if (bottomText) { + list.push({ + text: bottomText, + item: bottomItem + }); + } + + return list; } + return { getNowPlayingNames: getNowPlayingNames - } -}); \ No newline at end of file + }; +}); diff --git a/src/bower_components/emby-webcomponents/playback/playaccessvalidation.js b/src/bower_components/emby-webcomponents/playback/playaccessvalidation.js index 30a687d771..231ef09d0a 100644 --- a/src/bower_components/emby-webcomponents/playback/playaccessvalidation.js +++ b/src/bower_components/emby-webcomponents/playback/playaccessvalidation.js @@ -1,29 +1,56 @@ -define(["connectionManager", "globalize"], function(connectionManager, globalize) { +define(['connectionManager', 'globalize'], function (connectionManager, globalize) { "use strict"; function getRequirePromise(deps) { - return new Promise(function(resolve, reject) { - require(deps, resolve) - }) + + return new Promise(function (resolve, reject) { + + require(deps, resolve); + }); } function showErrorMessage() { - return getRequirePromise(["alert"]).then(function(alert) { - return alert(globalize.translate("sharedcomponents#MessagePlayAccessRestricted")).then(function() { - return Promise.reject() - }) - }) + return getRequirePromise(['alert']).then(function (alert) { + + return alert(globalize.translate('sharedcomponents#MessagePlayAccessRestricted')).then(function () { + return Promise.reject(); + }); + }); } function PlayAccessValidation() { - this.name = "Playback validation", this.type = "preplayintercept", this.id = "playaccessvalidation", this.order = -2 + + this.name = 'Playback validation'; + this.type = 'preplayintercept'; + this.id = 'playaccessvalidation'; + this.order = -2; } - return PlayAccessValidation.prototype.intercept = function(options) { + + PlayAccessValidation.prototype.intercept = function (options) { + var item = options.item; - if (!item) return Promise.resolve(); + if (!item) { + return Promise.resolve(); + } var serverId = item.ServerId; - return serverId ? connectionManager.getApiClient(serverId).getCurrentUser().then(function(user) { - return user.Policy.EnableMediaPlayback ? Promise.resolve() : options.fullscreen ? showErrorMessage() : Promise.reject() - }) : Promise.resolve() - }, PlayAccessValidation + if (!serverId) { + return Promise.resolve(); + } + + return connectionManager.getApiClient(serverId).getCurrentUser().then(function (user) { + + if (user.Policy.EnableMediaPlayback) { + return Promise.resolve(); + } + + // reject but don't show an error message + if (!options.fullscreen) { + return Promise.reject(); + } + + return showErrorMessage(); + }); + }; + + return PlayAccessValidation; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/playback/playbackmanager.js b/src/bower_components/emby-webcomponents/playback/playbackmanager.js index a321efc36b..db70313ca0 100644 --- a/src/bower_components/emby-webcomponents/playback/playbackmanager.js +++ b/src/bower_components/emby-webcomponents/playback/playbackmanager.js @@ -1,153 +1,325 @@ -define(["events", "datetime", "appSettings", "itemHelper", "pluginManager", "playQueueManager", "userSettings", "globalize", "connectionManager", "loading", "apphost", "fullscreenManager"], function(events, datetime, appSettings, itemHelper, pluginManager, PlayQueueManager, userSettings, globalize, connectionManager, loading, apphost, fullscreenManager) { - "use strict"; +define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'playQueueManager', 'userSettings', 'globalize', 'connectionManager', 'loading', 'apphost', 'fullscreenManager'], function (events, datetime, appSettings, itemHelper, pluginManager, PlayQueueManager, userSettings, globalize, connectionManager, loading, apphost, fullscreenManager) { + 'use strict'; function enableLocalPlaylistManagement(player) { - return !player.getPlaylist && !!player.isLocalPlayer + + if (player.getPlaylist) { + + return false; + } + + if (player.isLocalPlayer) { + + return true; + } + + return false; } function bindToFullscreenChange(player) { - events.on(fullscreenManager, "fullscreenchange", function() { - events.trigger(player, "fullscreenchange") - }) + events.on(fullscreenManager, 'fullscreenchange', function () { + events.trigger(player, 'fullscreenchange'); + }); } function triggerPlayerChange(playbackManagerInstance, newPlayer, newTarget, previousPlayer, previousTargetInfo) { - (newPlayer || previousPlayer) && (newTarget && previousTargetInfo && newTarget.id === previousTargetInfo.id || events.trigger(playbackManagerInstance, "playerchange", [newPlayer, newTarget, previousPlayer])) + + if (!newPlayer && !previousPlayer) { + return; + } + + if (newTarget && previousTargetInfo) { + + if (newTarget.id === previousTargetInfo.id) { + return; + } + } + + events.trigger(playbackManagerInstance, 'playerchange', [newPlayer, newTarget, previousPlayer]); } function reportPlayback(playbackManagerInstance, state, player, reportPlaylist, serverId, method, progressEventName) { - if (serverId) { - var info = Object.assign({}, state.PlayState); - info.ItemId = state.NowPlayingItem.Id, progressEventName && (info.EventName = progressEventName), reportPlaylist && addPlaylistToPlaybackReport(playbackManagerInstance, info, player, serverId); - connectionManager.getApiClient(serverId)[method](info) + + if (!serverId) { + // Not a server item + // We can expand on this later and possibly report them + return; } + + var info = Object.assign({}, state.PlayState); + info.ItemId = state.NowPlayingItem.Id; + + if (progressEventName) { + info.EventName = progressEventName; + } + + if (reportPlaylist) { + addPlaylistToPlaybackReport(playbackManagerInstance, info, player, serverId); + } + + //console.log(method + '-' + JSON.stringify(info)); + var apiClient = connectionManager.getApiClient(serverId); + apiClient[method](info); } function getPlaylistSync(playbackManagerInstance, player) { - return player = player || playbackManagerInstance._currentPlayer, player && !enableLocalPlaylistManagement(player) ? player.getPlaylistSync() : playbackManagerInstance._playQueueManager.getPlaylist() + player = player || playbackManagerInstance._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + return player.getPlaylistSync(); + } + + return playbackManagerInstance._playQueueManager.getPlaylist(); } function addPlaylistToPlaybackReport(playbackManagerInstance, info, player, serverId) { - info.NowPlayingQueue = getPlaylistSync(playbackManagerInstance, player).map(function(i) { + + info.NowPlayingQueue = getPlaylistSync(playbackManagerInstance, player).map(function (i) { + var itemInfo = { Id: i.Id, PlaylistItemId: i.PlaylistItemId }; - return i.ServerId !== serverId && (itemInfo.ServerId = i.ServerId), itemInfo - }) + + if (i.ServerId !== serverId) { + itemInfo.ServerId = i.ServerId; + } + + return itemInfo; + }); } function normalizeName(t) { - return t.toLowerCase().replace(" ", "") + return t.toLowerCase().replace(' ', ''); } function getItemsForPlayback(serverId, query) { + var apiClient = connectionManager.getApiClient(serverId); - if (query.Ids && 1 === query.Ids.split(",").length) { - var itemId = query.Ids.split(","); - return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function(item) { + + if (query.Ids && query.Ids.split(',').length === 1) { + + var itemId = query.Ids.split(','); + + return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) { + return { Items: [item], TotalRecordCount: 1 - } - }) + }; + }); + } + else { + + query.Limit = query.Limit || 300; + query.Fields = "Chapters"; + query.ExcludeLocationTypes = "Virtual"; + query.EnableTotalRecordCount = false; + query.CollapseBoxSetItems = false; + + return apiClient.getItems(apiClient.getCurrentUserId(), query); } - return query.Limit = query.Limit || 300, query.Fields = "Chapters", query.ExcludeLocationTypes = "Virtual", query.EnableTotalRecordCount = !1, query.CollapseBoxSetItems = !1, apiClient.getItems(apiClient.getCurrentUserId(), query) } function createStreamInfoFromUrlItem(item) { + + // Check item.Path for games return { url: item.Url || item.Path, - playMethod: "DirectPlay", + playMethod: 'DirectPlay', item: item, textTracks: [], mediaType: item.MediaType - } + }; } function mergePlaybackQueries(obj1, obj2) { - var query = Object.assign(obj1, obj2), - filters = query.Filters ? query.Filters.split(",") : []; - return -1 === filters.indexOf("IsNotFolder") && filters.push("IsNotFolder"), query.Filters = filters.join(","), query + + var query = Object.assign(obj1, obj2); + + var filters = query.Filters ? query.Filters.split(',') : []; + if (filters.indexOf('IsNotFolder') === -1) { + filters.push('IsNotFolder'); + } + query.Filters = filters.join(','); + return query; } function backdropImageUrl(apiClient, item, options) { - return options = options || {}, options.type = options.type || "Backdrop", options.maxWidth || options.width || options.maxHeight || options.height || (options.quality = 100), item.BackdropImageTags && item.BackdropImageTags.length ? (options.tag = item.BackdropImageTags[0], apiClient.getScaledImageUrl(item.Id, options)) : item.ParentBackdropImageTags && item.ParentBackdropImageTags.length ? (options.tag = item.ParentBackdropImageTags[0], apiClient.getScaledImageUrl(item.ParentBackdropItemId, options)) : null + + options = options || {}; + options.type = options.type || "Backdrop"; + + // If not resizing, get the original image + if (!options.maxWidth && !options.width && !options.maxHeight && !options.height) { + options.quality = 100; + } + + if (item.BackdropImageTags && item.BackdropImageTags.length) { + + options.tag = item.BackdropImageTags[0]; + return apiClient.getScaledImageUrl(item.Id, options); + } + + if (item.ParentBackdropImageTags && item.ParentBackdropImageTags.length) { + options.tag = item.ParentBackdropImageTags[0]; + return apiClient.getScaledImageUrl(item.ParentBackdropItemId, options); + } + + return null; } function getMimeType(type, container) { - if (container = (container || "").toLowerCase(), "audio" === type) { - if ("opus" === container) return "audio/ogg"; - if ("webma" === container) return "audio/webm"; - if ("m4a" === container) return "audio/mp4" - } else if ("video" === type) { - if ("mkv" === container) return "video/x-matroska"; - if ("m4v" === container) return "video/mp4"; - if ("mov" === container) return "video/quicktime"; - if ("mpg" === container) return "video/mpeg"; - if ("flv" === container) return "video/x-flv" + + container = (container || '').toLowerCase(); + + if (type === 'audio') { + if (container === 'opus') { + return 'audio/ogg'; + } + if (container === 'webma') { + return 'audio/webm'; + } + if (container === 'm4a') { + return 'audio/mp4'; + } } - return type + "/" + container + else if (type === 'video') { + if (container === 'mkv') { + return 'video/x-matroska'; + } + if (container === 'm4v') { + return 'video/mp4'; + } + if (container === 'mov') { + return 'video/quicktime'; + } + if (container === 'mpg') { + return 'video/mpeg'; + } + if (container === 'flv') { + return 'video/x-flv'; + } + } + + return type + '/' + container; } function getParam(name, url) { - name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); - var regexS = "[\\?&]" + name + "=([^&#]*)", - regex = new RegExp(regexS, "i"), - results = regex.exec(url); - return null == results ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")) + name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]"); + var regexS = "[\\?&]" + name + "=([^&#]*)"; + var regex = new RegExp(regexS, "i"); + + var results = regex.exec(url); + if (results == null) { + return ""; + } + else { + return decodeURIComponent(results[1].replace(/\+/g, " ")); + } } function isAutomaticPlayer(player) { - return !!player.isLocalPlayer + + if (player.isLocalPlayer) { + return true; + } + + return false; } function getAutomaticPlayers(instance, forceLocalPlayer) { + if (!forceLocalPlayer) { var player = instance._currentPlayer; - if (player && !isAutomaticPlayer(player)) return [player] + if (player && !isAutomaticPlayer(player)) { + return [player]; + } } - return instance.getPlayers().filter(isAutomaticPlayer) + + return instance.getPlayers().filter(isAutomaticPlayer); } function isServerItem(item) { - return !!item.Id + if (!item.Id) { + return false; + } + return true; } function enableIntros(item) { - return "Video" === item.MediaType && ("TvChannel" !== item.Type && ("InProgress" !== item.Status && isServerItem(item))) + + if (item.MediaType !== 'Video') { + return false; + } + if (item.Type === 'TvChannel') { + return false; + } + // disable for in-progress recordings + if (item.Status === 'InProgress') { + return false; + } + + return isServerItem(item); } function getIntros(firstItem, apiClient, options) { - return options.startPositionTicks || options.startIndex || !1 === options.fullscreen || !enableIntros(firstItem) || !userSettings.enableCinemaMode() ? Promise.resolve({ - Items: [] - }) : apiClient.getIntros(firstItem.Id).then(function(result) { - return result - }, function(err) { + + if (options.startPositionTicks || options.startIndex || options.fullscreen === false || !enableIntros(firstItem) || !userSettings.enableCinemaMode()) { return Promise.resolve({ Items: [] - }) - }) + }); + } + + return apiClient.getIntros(firstItem.Id).then(function (result) { + + return result; + + }, function (err) { + + return Promise.resolve({ + Items: [] + }); + }); } function getAudioMaxValues(deviceProfile) { - var maxAudioSampleRate = null, - maxAudioBitDepth = null, - maxAudioBitrate = null; - return deviceProfile.CodecProfiles.map(function(codecProfile) { - "Audio" === codecProfile.Type && (codecProfile.Conditions || []).map(function(condition) { - "LessThanEqual" === condition.Condition && "AudioBitDepth" === condition.Property && (maxAudioBitDepth = condition.Value), "LessThanEqual" === condition.Condition && "AudioSampleRate" === condition.Property && (maxAudioSampleRate = condition.Value), "LessThanEqual" === condition.Condition && "AudioBitrate" === condition.Property && (maxAudioBitrate = condition.Value) - }) - }), { + + // TODO - this could vary per codec and should be done on the server using the entire profile + var maxAudioSampleRate = null; + var maxAudioBitDepth = null; + var maxAudioBitrate = null; + + deviceProfile.CodecProfiles.map(function (codecProfile) { + + if (codecProfile.Type === 'Audio') { + (codecProfile.Conditions || []).map(function (condition) { + if (condition.Condition === 'LessThanEqual' && condition.Property === 'AudioBitDepth') { + maxAudioBitDepth = condition.Value; + } + if (condition.Condition === 'LessThanEqual' && condition.Property === 'AudioSampleRate') { + maxAudioSampleRate = condition.Value; + } + if (condition.Condition === 'LessThanEqual' && condition.Property === 'AudioBitrate') { + maxAudioBitrate = condition.Value; + } + }); + } + }); + + return { maxAudioSampleRate: maxAudioSampleRate, maxAudioBitDepth: maxAudioBitDepth, maxAudioBitrate: maxAudioBitrate - } + }; } + var startingPlaySession = new Date().getTime(); function getAudioStreamUrl(item, transcodingProfile, directPlayContainers, maxBitrate, apiClient, maxAudioSampleRate, maxAudioBitDepth, maxAudioBitrate, startPosition) { - var url = "Audio/" + item.Id + "/universal"; - return startingPlaySession++, apiClient.getUrl(url, { + + var url = 'Audio/' + item.Id + '/universal'; + + startingPlaySession++; + return apiClient.getUrl(url, { UserId: apiClient.getCurrentUserId(), DeviceId: apiClient.deviceId(), MaxStreamingBitrate: maxAudioBitrate || maxBitrate, @@ -160,177 +332,415 @@ define(["events", "datetime", "appSettings", "itemHelper", "pluginManager", "pla api_key: apiClient.accessToken(), PlaySessionId: startingPlaySession, StartTimeTicks: startPosition || 0, - EnableRedirection: !0, - EnableRemoteMedia: apphost.supports("remoteaudio") - }) + EnableRedirection: true, + EnableRemoteMedia: apphost.supports('remoteaudio') + }); } function getAudioStreamUrlFromDeviceProfile(item, deviceProfile, maxBitrate, apiClient, startPosition) { - var transcodingProfile = deviceProfile.TranscodingProfiles.filter(function(p) { - return "Audio" === p.Type && "Streaming" === p.Context - })[0], - directPlayContainers = ""; - deviceProfile.DirectPlayProfiles.map(function(p) { - "Audio" === p.Type && (directPlayContainers ? directPlayContainers += "," + p.Container : directPlayContainers = p.Container, p.AudioCodec && (directPlayContainers += "|" + p.AudioCodec)) + + var transcodingProfile = deviceProfile.TranscodingProfiles.filter(function (p) { + return p.Type === 'Audio' && p.Context === 'Streaming'; + })[0]; + + var directPlayContainers = ''; + + deviceProfile.DirectPlayProfiles.map(function (p) { + + if (p.Type === 'Audio') { + if (directPlayContainers) { + directPlayContainers += ',' + p.Container; + } else { + directPlayContainers = p.Container; + } + + if (p.AudioCodec) { + directPlayContainers += '|' + p.AudioCodec; + } + } + }); + var maxValues = getAudioMaxValues(deviceProfile); - return getAudioStreamUrl(item, transcodingProfile, directPlayContainers, maxBitrate, apiClient, maxValues.maxAudioSampleRate, maxValues.maxAudioBitDepth, maxValues.maxAudioBitrate, startPosition) + + return getAudioStreamUrl(item, transcodingProfile, directPlayContainers, maxBitrate, apiClient, maxValues.maxAudioSampleRate, maxValues.maxAudioBitDepth, maxValues.maxAudioBitrate, startPosition); } function getStreamUrls(items, deviceProfile, maxBitrate, apiClient, startPosition) { - var audioTranscodingProfile = deviceProfile.TranscodingProfiles.filter(function(p) { - return "Audio" === p.Type && "Streaming" === p.Context - })[0], - audioDirectPlayContainers = ""; - deviceProfile.DirectPlayProfiles.map(function(p) { - "Audio" === p.Type && (audioDirectPlayContainers ? audioDirectPlayContainers += "," + p.Container : audioDirectPlayContainers = p.Container, p.AudioCodec && (audioDirectPlayContainers += "|" + p.AudioCodec)) + + var audioTranscodingProfile = deviceProfile.TranscodingProfiles.filter(function (p) { + return p.Type === 'Audio' && p.Context === 'Streaming'; + })[0]; + + var audioDirectPlayContainers = ''; + + deviceProfile.DirectPlayProfiles.map(function (p) { + + if (p.Type === 'Audio') { + if (audioDirectPlayContainers) { + audioDirectPlayContainers += ',' + p.Container; + } else { + audioDirectPlayContainers = p.Container; + } + + if (p.AudioCodec) { + audioDirectPlayContainers += '|' + p.AudioCodec; + } + } }); - for (var maxValues = getAudioMaxValues(deviceProfile), streamUrls = [], i = 0, length = items.length; i < length; i++) { - var streamUrl, item = items[i]; - "Audio" !== item.MediaType || itemHelper.isLocalItem(item) || (streamUrl = getAudioStreamUrl(item, audioTranscodingProfile, audioDirectPlayContainers, maxBitrate, apiClient, maxValues.maxAudioSampleRate, maxValues.maxAudioBitDepth, maxValues.maxAudioBitrate, startPosition)), streamUrls.push(streamUrl || ""), 0 === i && (startPosition = 0) + + var maxValues = getAudioMaxValues(deviceProfile); + + var streamUrls = []; + + for (var i = 0, length = items.length; i < length; i++) { + + var item = items[i]; + var streamUrl; + + if (item.MediaType === 'Audio' && !itemHelper.isLocalItem(item)) { + streamUrl = getAudioStreamUrl(item, audioTranscodingProfile, audioDirectPlayContainers, maxBitrate, apiClient, maxValues.maxAudioSampleRate, maxValues.maxAudioBitDepth, maxValues.maxAudioBitrate, startPosition); + } + + streamUrls.push(streamUrl || ''); + + if (i === 0) { + startPosition = 0; + } } - return Promise.resolve(streamUrls) + + return Promise.resolve(streamUrls); } function setStreamUrls(items, deviceProfile, maxBitrate, apiClient, startPosition) { - return getStreamUrls(items, deviceProfile, maxBitrate, apiClient, startPosition).then(function(streamUrls) { + + return getStreamUrls(items, deviceProfile, maxBitrate, apiClient, startPosition).then(function (streamUrls) { + for (var i = 0, length = items.length; i < length; i++) { - var item = items[i], - streamUrl = streamUrls[i]; - streamUrl && (item.PresetMediaSource = { - StreamUrl: streamUrl, - Id: item.Id, - MediaStreams: [], - RunTimeTicks: item.RunTimeTicks - }) + + var item = items[i]; + var streamUrl = streamUrls[i]; + + if (streamUrl) { + item.PresetMediaSource = { + StreamUrl: streamUrl, + Id: item.Id, + MediaStreams: [], + RunTimeTicks: item.RunTimeTicks + }; + } } - }) + }); } - function getPlaybackInfo(player, apiClient, item, deviceProfile, maxBitrate, startPosition, isPlayback, mediaSourceId, audioStreamIndex, subtitleStreamIndex, liveStreamId, enableDirectPlay, enableDirectStream, allowVideoStreamCopy, allowAudioStreamCopy) { - if (!itemHelper.isLocalItem(item) && "Audio" === item.MediaType) return Promise.resolve({ - MediaSources: [{ - StreamUrl: getAudioStreamUrlFromDeviceProfile(item, deviceProfile, maxBitrate, apiClient, startPosition), - Id: item.Id, - MediaStreams: [], - RunTimeTicks: item.RunTimeTicks - }] - }); - if (item.PresetMediaSource) return Promise.resolve({ - MediaSources: [item.PresetMediaSource] - }); - var itemId = item.Id, - query = { - UserId: apiClient.getCurrentUserId(), - StartTimeTicks: startPosition || 0 - }; - return isPlayback ? (query.IsPlayback = !0, query.AutoOpenLiveStream = !0) : (query.IsPlayback = !1, query.AutoOpenLiveStream = !1), null != audioStreamIndex && (query.AudioStreamIndex = audioStreamIndex), null != subtitleStreamIndex && (query.SubtitleStreamIndex = subtitleStreamIndex), null != enableDirectPlay && (query.EnableDirectPlay = enableDirectPlay), null != enableDirectStream && (query.EnableDirectStream = enableDirectStream), null != allowVideoStreamCopy && (query.AllowVideoStreamCopy = allowVideoStreamCopy), null != allowAudioStreamCopy && (query.AllowAudioStreamCopy = allowAudioStreamCopy), mediaSourceId && (query.MediaSourceId = mediaSourceId), liveStreamId && (query.LiveStreamId = liveStreamId), maxBitrate && (query.MaxStreamingBitrate = maxBitrate), player.enableMediaProbe && !player.enableMediaProbe(item) && (query.EnableMediaProbe = !1), !1 !== query.EnableDirectStream && player.supportsPlayMethod && !player.supportsPlayMethod("DirectStream", item) && (query.EnableDirectStream = !1), player.getDirectPlayProtocols && (query.DirectPlayProtocols = player.getDirectPlayProtocols()), apiClient.getPlaybackInfo(itemId, query, deviceProfile) + function getPlaybackInfo(player, + apiClient, + item, + deviceProfile, + maxBitrate, + startPosition, + isPlayback, + mediaSourceId, + audioStreamIndex, + subtitleStreamIndex, + liveStreamId, + enableDirectPlay, + enableDirectStream, + allowVideoStreamCopy, + allowAudioStreamCopy) { + + if (!itemHelper.isLocalItem(item) && item.MediaType === 'Audio') { + + return Promise.resolve({ + MediaSources: [ + { + StreamUrl: getAudioStreamUrlFromDeviceProfile(item, deviceProfile, maxBitrate, apiClient, startPosition), + Id: item.Id, + MediaStreams: [], + RunTimeTicks: item.RunTimeTicks + }] + }); + } + + if (item.PresetMediaSource) { + return Promise.resolve({ + MediaSources: [item.PresetMediaSource] + }); + } + + var itemId = item.Id; + + var query = { + UserId: apiClient.getCurrentUserId(), + StartTimeTicks: startPosition || 0 + }; + + if (isPlayback) { + query.IsPlayback = true; + query.AutoOpenLiveStream = true; + } else { + query.IsPlayback = false; + query.AutoOpenLiveStream = false; + } + + if (audioStreamIndex != null) { + query.AudioStreamIndex = audioStreamIndex; + } + if (subtitleStreamIndex != null) { + query.SubtitleStreamIndex = subtitleStreamIndex; + } + if (enableDirectPlay != null) { + query.EnableDirectPlay = enableDirectPlay; + } + + if (enableDirectStream != null) { + query.EnableDirectStream = enableDirectStream; + } + if (allowVideoStreamCopy != null) { + query.AllowVideoStreamCopy = allowVideoStreamCopy; + } + if (allowAudioStreamCopy != null) { + query.AllowAudioStreamCopy = allowAudioStreamCopy; + } + if (mediaSourceId) { + query.MediaSourceId = mediaSourceId; + } + if (liveStreamId) { + query.LiveStreamId = liveStreamId; + } + if (maxBitrate) { + query.MaxStreamingBitrate = maxBitrate; + } + if (player.enableMediaProbe && !player.enableMediaProbe(item)) { + query.EnableMediaProbe = false; + } + + // lastly, enforce player overrides for special situations + if (query.EnableDirectStream !== false) { + if (player.supportsPlayMethod && !player.supportsPlayMethod('DirectStream', item)) { + query.EnableDirectStream = false; + } + } + + if (player.getDirectPlayProtocols) { + query.DirectPlayProtocols = player.getDirectPlayProtocols(); + } + + return apiClient.getPlaybackInfo(itemId, query, deviceProfile); } function getOptimalMediaSource(apiClient, item, versions) { - var promises = versions.map(function(v) { - return supportsDirectPlay(apiClient, item, v) + + var promises = versions.map(function (v) { + return supportsDirectPlay(apiClient, item, v); }); - return promises.length ? Promise.all(promises).then(function(results) { - for (var i = 0, length = versions.length; i < length; i++) versions[i].enableDirectPlay = results[i] || !1; - var optimalVersion = versions.filter(function(v) { - return v.enableDirectPlay + + if (!promises.length) { + return Promise.reject(); + } + + return Promise.all(promises).then(function (results) { + + for (var i = 0, length = versions.length; i < length; i++) { + versions[i].enableDirectPlay = results[i] || false; + } + var optimalVersion = versions.filter(function (v) { + + return v.enableDirectPlay; + })[0]; - return optimalVersion || (optimalVersion = versions.filter(function(v) { - return v.SupportsDirectStream - })[0]), (optimalVersion = optimalVersion || versions.filter(function(s) { - return s.SupportsTranscoding - })[0]) || versions[0] - }) : Promise.reject() + + if (!optimalVersion) { + optimalVersion = versions.filter(function (v) { + + return v.SupportsDirectStream; + + })[0]; + } + + optimalVersion = optimalVersion || versions.filter(function (s) { + return s.SupportsTranscoding; + })[0]; + + return optimalVersion || versions[0]; + }); } function getLiveStream(player, apiClient, item, playSessionId, deviceProfile, maxBitrate, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex) { + var postData = { - DeviceProfile: deviceProfile, - OpenToken: mediaSource.OpenToken - }, - query = { - UserId: apiClient.getCurrentUserId(), - StartTimeTicks: startPosition || 0, - ItemId: item.Id, - PlaySessionId: playSessionId - }; - return maxBitrate && (query.MaxStreamingBitrate = maxBitrate), null != audioStreamIndex && (query.AudioStreamIndex = audioStreamIndex), null != subtitleStreamIndex && (query.SubtitleStreamIndex = subtitleStreamIndex), !1 !== query.EnableDirectStream && player.supportsPlayMethod && !player.supportsPlayMethod("DirectStream", item) && (query.EnableDirectStream = !1), apiClient.ajax({ - url: apiClient.getUrl("LiveStreams/Open", query), - type: "POST", + DeviceProfile: deviceProfile, + OpenToken: mediaSource.OpenToken + }; + + var query = { + UserId: apiClient.getCurrentUserId(), + StartTimeTicks: startPosition || 0, + ItemId: item.Id, + PlaySessionId: playSessionId + }; + + if (maxBitrate) { + query.MaxStreamingBitrate = maxBitrate; + } + if (audioStreamIndex != null) { + query.AudioStreamIndex = audioStreamIndex; + } + if (subtitleStreamIndex != null) { + query.SubtitleStreamIndex = subtitleStreamIndex; + } + + // lastly, enforce player overrides for special situations + if (query.EnableDirectStream !== false) { + if (player.supportsPlayMethod && !player.supportsPlayMethod('DirectStream', item)) { + query.EnableDirectStream = false; + } + } + + return apiClient.ajax({ + url: apiClient.getUrl('LiveStreams/Open', query), + type: 'POST', data: JSON.stringify(postData), contentType: "application/json", dataType: "json" - }) + + }); } function isHostReachable(mediaSource, apiClient) { - return mediaSource.IsRemote ? Promise.resolve(!0) : apiClient.getEndpointInfo().then(function(endpointInfo) { + + if (mediaSource.IsRemote) { + return Promise.resolve(true); + } + + return apiClient.getEndpointInfo().then(function (endpointInfo) { + if (endpointInfo.IsInNetwork) { + if (!endpointInfo.IsLocal) { - var path = (mediaSource.Path || "").toLowerCase(); - if (-1 !== path.indexOf("localhost") || -1 !== path.indexOf("127.0.0.1")) return Promise.resolve(!1) + var path = (mediaSource.Path || '').toLowerCase(); + if (path.indexOf('localhost') !== -1 || path.indexOf('127.0.0.1') !== -1) { + // This will only work if the app is on the same machine as the server + return Promise.resolve(false); + } } - return Promise.resolve(!0) + + return Promise.resolve(true); } - return Promise.resolve(!1) - }) + + // media source is in network, but connection is out of network + return Promise.resolve(false); + }); } function supportsDirectPlay(apiClient, item, mediaSource) { - var isFolderRip = "BluRay" === mediaSource.VideoType || "Dvd" === mediaSource.VideoType || "HdDvd" === mediaSource.VideoType; + + // folder rip hacks due to not yet being supported by the stream building engine + var isFolderRip = mediaSource.VideoType === 'BluRay' || mediaSource.VideoType === 'Dvd' || mediaSource.VideoType === 'HdDvd'; + if (mediaSource.SupportsDirectPlay || isFolderRip) { - if (mediaSource.IsRemote && !apphost.supports("remotevideo")) return Promise.resolve(!1); - if ("Http" === mediaSource.Protocol && !mediaSource.RequiredHttpHeaders.length) return mediaSource.SupportsDirectStream || mediaSource.SupportsTranscoding ? isHostReachable(mediaSource, apiClient) : Promise.resolve(!0); - if ("File" === mediaSource.Protocol) return new Promise(function(resolve, reject) { - require(["filesystem"], function(filesystem) { - filesystem[isFolderRip ? "directoryExists" : "fileExists"](mediaSource.Path).then(function() { - resolve(!0) - }, function() { - resolve(!1) - }) - }) - }) + + if (mediaSource.IsRemote && !apphost.supports('remotevideo')) { + return Promise.resolve(false); + } + + if (mediaSource.Protocol === 'Http' && !mediaSource.RequiredHttpHeaders.length) { + + // If this is the only way it can be played, then allow it + if (!mediaSource.SupportsDirectStream && !mediaSource.SupportsTranscoding) { + return Promise.resolve(true); + } + else { + return isHostReachable(mediaSource, apiClient); + } + } + + else if (mediaSource.Protocol === 'File') { + + return new Promise(function (resolve, reject) { + + // Determine if the file can be accessed directly + require(['filesystem'], function (filesystem) { + + var method = isFolderRip ? + 'directoryExists' : + 'fileExists'; + + filesystem[method](mediaSource.Path).then(function () { + resolve(true); + }, function () { + resolve(false); + }); + + }); + }); + } } - return Promise.resolve(!1) + + return Promise.resolve(false); } function validatePlaybackInfoResult(instance, result) { - return !result.ErrorCode || (showPlaybackInfoErrorMessage(instance, result.ErrorCode), !1) + + if (result.ErrorCode) { + + showPlaybackInfoErrorMessage(instance, result.ErrorCode); + return false; + } + + return true; } function showPlaybackInfoErrorMessage(instance, errorCode, playNextTrack) { - require(["alert"], function(alert) { + + require(['alert'], function (alert) { alert({ - text: globalize.translate("sharedcomponents#PlaybackError" + errorCode), - title: globalize.translate("sharedcomponents#HeaderPlaybackError") - }).then(function() { - playNextTrack && instance.nextTrack() - }) - }) + text: globalize.translate('sharedcomponents#PlaybackError' + errorCode), + title: globalize.translate('sharedcomponents#HeaderPlaybackError') + }).then(function () { + + if (playNextTrack) { + instance.nextTrack(); + } + }); + }); } function normalizePlayOptions(playOptions) { - playOptions.fullscreen = !1 !== playOptions.fullscreen + playOptions.fullscreen = playOptions.fullscreen !== false; } function truncatePlayOptions(playOptions) { + return { fullscreen: playOptions.fullscreen, mediaSourceId: playOptions.mediaSourceId, audioStreamIndex: playOptions.audioStreamIndex, subtitleStreamIndex: playOptions.subtitleStreamIndex, startPositionTicks: playOptions.startPositionTicks - } + }; } function getNowPlayingItemForReporting(player, item, mediaSource) { + var nowPlayingItem = Object.assign({}, item); - return mediaSource && (nowPlayingItem.RunTimeTicks = mediaSource.RunTimeTicks, nowPlayingItem.MediaStreams = mediaSource.MediaStreams, nowPlayingItem.MediaSources = null), nowPlayingItem.RunTimeTicks = nowPlayingItem.RunTimeTicks || 1e4 * player.duration(), nowPlayingItem + + if (mediaSource) { + nowPlayingItem.RunTimeTicks = mediaSource.RunTimeTicks; + nowPlayingItem.MediaStreams = mediaSource.MediaStreams; + + // not needed + nowPlayingItem.MediaSources = null; + } + + nowPlayingItem.RunTimeTicks = nowPlayingItem.RunTimeTicks || player.duration() * 10000; + + return nowPlayingItem; } function displayPlayerIndividually(player) { - return !player.isLocalPlayer + + return !player.isLocalPlayer; } function createTarget(instance, player) { @@ -338,1215 +748,3241 @@ define(["events", "datetime", "appSettings", "itemHelper", "pluginManager", "pla name: player.name, id: player.id, playerName: player.name, - playableMediaTypes: ["Audio", "Video", "Game", "Photo", "Book"].map(player.canPlayMediaType), + playableMediaTypes: ['Audio', 'Video', 'Game', 'Photo', 'Book'].map(player.canPlayMediaType), isLocalPlayer: player.isLocalPlayer, supportedCommands: instance.getSupportedCommands(player) - } + }; } function getPlayerTargets(player) { - return player.getTargets ? player.getTargets() : Promise.resolve([createTarget(player)]) + if (player.getTargets) { + return player.getTargets(); + } + + return Promise.resolve([createTarget(player)]); } function sortPlayerTargets(a, b) { - var aVal = a.isLocalPlayer ? 0 : 1, - bVal = b.isLocalPlayer ? 0 : 1; - return aVal = aVal.toString() + a.name, bVal = bVal.toString() + b.name, aVal.localeCompare(bVal) + + var aVal = a.isLocalPlayer ? 0 : 1; + var bVal = b.isLocalPlayer ? 0 : 1; + + aVal = aVal.toString() + a.name; + bVal = bVal.toString() + b.name; + + return aVal.localeCompare(bVal); } function PlaybackManager() { - function getCurrentSubtitleStream(player) { - if (!player) throw new Error("player cannot be null"); - var index = getPlayerData(player).subtitleStreamIndex; - return null == index || -1 === index ? null : getSubtitleStream(player, index) - } - function getSubtitleStream(player, index) { - return self.subtitleTracks(player).filter(function(s) { - return "Subtitle" === s.Type && s.Index === index - })[0] - } + var self = this; - function removeCurrentPlayer(player) { - var previousPlayer = self._currentPlayer; - previousPlayer && player.id !== previousPlayer.id || setCurrentPlayerInternal(null) - } + var players = []; + var currentTargetInfo; + var lastLocalPlayer; + var currentPairingId = null; - function setCurrentPlayerInternal(player, targetInfo) { - var previousPlayer = self._currentPlayer, - previousTargetInfo = currentTargetInfo; - if (player && !targetInfo && player.isLocalPlayer && (targetInfo = createTarget(self, player)), player && !targetInfo) throw new Error("targetInfo cannot be null"); - currentPairingId = null, self._currentPlayer = player, currentTargetInfo = targetInfo, targetInfo && console.log("Active player: " + JSON.stringify(targetInfo)), player && player.isLocalPlayer && (lastLocalPlayer = player), previousPlayer && self.endPlayerUpdates(previousPlayer), player && self.beginPlayerUpdates(player), triggerPlayerChange(self, player, targetInfo, previousPlayer, previousTargetInfo) - } - - function getDefaultPlayOptions() { - return { - fullscreen: !0 - } - } - - function isAudioStreamSupported(mediaSource, index, deviceProfile) { - var mediaStream, i, length, mediaStreams = mediaSource.MediaStreams; - for (i = 0, length = mediaStreams.length; i < length; i++) - if ("Audio" === mediaStreams[i].Type && mediaStreams[i].Index === index) { - mediaStream = mediaStreams[i]; - break - } if (!mediaStream) return !1; - var codec = (mediaStream.Codec || "").toLowerCase(); - return !!codec && (deviceProfile.DirectPlayProfiles || []).filter(function(p) { - return "Video" === p.Type && (!p.AudioCodec || (0 === p.AudioCodec.indexOf("-") ? -1 === p.AudioCodec.toLowerCase().indexOf(codec) : -1 !== p.AudioCodec.toLowerCase().indexOf(codec))) - }).length > 0 - } - - function getSavedMaxStreamingBitrate(apiClient, mediaType) { - apiClient || (apiClient = connectionManager.currentApiClient()); - var endpointInfo = apiClient.getSavedEndpointInfo() || {}; - return appSettings.maxStreamingBitrate(endpointInfo.IsInNetwork, mediaType) - } - - function getDeliveryMethod(subtitleStream) { - return subtitleStream.DeliveryMethod ? subtitleStream.DeliveryMethod : subtitleStream.IsExternal ? "External" : "Embed" - } - - function canPlayerSeek(player) { - if (!player) throw new Error("player cannot be null"); - return -1 !== (getPlayerData(player).streamInfo.url || "").toLowerCase().indexOf(".m3u8") || (player.seekable ? player.seekable() : !("Transcode" === self.playMethod(player)) && player.duration()) - } - - function changeStream(player, ticks, params) { - if (canPlayerSeek(player) && null == params) return void player.currentTime(parseInt(ticks / 1e4)); - params = params || {}; - var liveStreamId = getPlayerData(player).streamInfo.liveStreamId, - lastMediaInfoQuery = getPlayerData(player).streamInfo.lastMediaInfoQuery, - playSessionId = self.playSessionId(player), - currentItem = self.currentItem(player); - player.getDeviceProfile(currentItem, { - isRetry: !1 === params.EnableDirectPlay - }).then(function(deviceProfile) { - var audioStreamIndex = null == params.AudioStreamIndex ? getPlayerData(player).audioStreamIndex : params.AudioStreamIndex, - subtitleStreamIndex = null == params.SubtitleStreamIndex ? getPlayerData(player).subtitleStreamIndex : params.SubtitleStreamIndex, - currentMediaSource = self.currentMediaSource(player), - apiClient = connectionManager.getApiClient(currentItem.ServerId); - ticks && (ticks = parseInt(ticks)); - var maxBitrate = params.MaxStreamingBitrate || self.getMaxStreamingBitrate(player), - currentPlayOptions = currentItem.playOptions || {}; - getPlaybackInfo(player, apiClient, currentItem, deviceProfile, maxBitrate, ticks, !0, currentMediaSource.Id, audioStreamIndex, subtitleStreamIndex, liveStreamId, params.EnableDirectPlay, params.EnableDirectStream, params.AllowVideoStreamCopy, params.AllowAudioStreamCopy).then(function(result) { - if (validatePlaybackInfoResult(self, result)) { - currentMediaSource = result.MediaSources[0]; - var streamInfo = createStreamInfo(apiClient, currentItem.MediaType, currentItem, currentMediaSource, ticks); - if (streamInfo.fullscreen = currentPlayOptions.fullscreen, streamInfo.lastMediaInfoQuery = lastMediaInfoQuery, !streamInfo.url) return void showPlaybackInfoErrorMessage(self, "NoCompatibleStream", !0); - getPlayerData(player).subtitleStreamIndex = subtitleStreamIndex, getPlayerData(player).audioStreamIndex = audioStreamIndex, getPlayerData(player).maxStreamingBitrate = maxBitrate, changeStreamToUrl(apiClient, player, playSessionId, streamInfo) - } - }) - }) - } - - function changeStreamToUrl(apiClient, player, playSessionId, streamInfo, newPositionTicks) { - var playerData = getPlayerData(player); - playerData.isChangingStream = !0, playerData.streamInfo && playSessionId ? apiClient.stopActiveEncodings(playSessionId).then(function() { - var afterSetSrc = function() { - apiClient.stopActiveEncodings(playSessionId) - }; - setSrcIntoPlayer(apiClient, player, streamInfo).then(afterSetSrc, afterSetSrc) - }) : setSrcIntoPlayer(apiClient, player, streamInfo) - } - - function setSrcIntoPlayer(apiClient, player, streamInfo) { - return player.play(streamInfo).then(function() { - var playerData = getPlayerData(player); - playerData.isChangingStream = !1, playerData.streamInfo = streamInfo, streamInfo.started = !0, streamInfo.ended = !1, sendProgressUpdate(player, "timeupdate") - }, function(e) { - getPlayerData(player).isChangingStream = !1, onPlaybackError.call(player, e, { - type: "mediadecodeerror", - streamInfo: streamInfo - }) - }) - } - - function translateItemsForPlayback(items, options) { - var promise, firstItem = items[0], - serverId = firstItem.ServerId, - queryOptions = options.queryOptions || {}; - return "Program" === firstItem.Type ? promise = getItemsForPlayback(serverId, { - Ids: firstItem.ChannelId - }) : "Playlist" === firstItem.Type ? promise = getItemsForPlayback(serverId, { - ParentId: firstItem.Id, - SortBy: options.shuffle ? "Random" : null - }) : "MusicArtist" === firstItem.Type ? promise = getItemsForPlayback(serverId, { - ArtistIds: firstItem.Id, - Filters: "IsNotFolder", - Recursive: !0, - SortBy: options.shuffle ? "Random" : "SortName", - MediaTypes: "Audio" - }) : "Photo" === firstItem.MediaType ? promise = getItemsForPlayback(serverId, { - ParentId: firstItem.ParentId, - Filters: "IsNotFolder", - Recursive: !1, - SortBy: options.shuffle ? "Random" : "SortName", - MediaTypes: "Photo,Video", - Limit: 5e3 - }).then(function(result) { - var items = result.Items, - index = items.map(function(i) { - return i.Id - }).indexOf(firstItem.Id); - return -1 === index && (index = 0), options.startIndex = index, Promise.resolve(result) - }) : "PhotoAlbum" === firstItem.Type ? promise = getItemsForPlayback(serverId, { - ParentId: firstItem.Id, - Filters: "IsNotFolder", - Recursive: !1, - SortBy: options.shuffle ? "Random" : "SortName", - MediaTypes: "Photo,Video", - Limit: 1e3 - }) : "MusicGenre" === firstItem.Type ? promise = getItemsForPlayback(serverId, { - GenreIds: firstItem.Id, - Filters: "IsNotFolder", - Recursive: !0, - SortBy: options.shuffle ? "Random" : "SortName", - MediaTypes: "Audio" - }) : firstItem.IsFolder ? promise = getItemsForPlayback(serverId, mergePlaybackQueries({ - ParentId: firstItem.Id, - Filters: "IsNotFolder", - Recursive: !0, - SortBy: options.shuffle ? "Random" : -1 === ["BoxSet"].indexOf(firstItem.Type) ? "SortName" : null, - MediaTypes: "Audio,Video" - }, queryOptions)) : "Episode" === firstItem.Type && 1 === items.length && !1 !== getPlayer(firstItem, options).supportsProgress && (promise = new Promise(function(resolve, reject) { - var apiClient = connectionManager.getApiClient(firstItem.ServerId); - apiClient.getCurrentUser().then(function(user) { - if (!user.Configuration.EnableNextEpisodeAutoPlay || !firstItem.SeriesId) return void resolve(null); - apiClient.getEpisodes(firstItem.SeriesId, { - IsVirtualUnaired: !1, - IsMissing: !1, - UserId: apiClient.getCurrentUserId(), - Fields: "Chapters" - }).then(function(episodesResult) { - var foundItem = !1; - episodesResult.Items = episodesResult.Items.filter(function(e) { - return !!foundItem || e.Id === firstItem.Id && (foundItem = !0, !0) - }), episodesResult.TotalRecordCount = episodesResult.Items.length, resolve(episodesResult) - }, reject) - }) - })), promise ? promise.then(function(result) { - return result ? result.Items : items - }) : Promise.resolve(items) - } - - function getPlayerData(player) { - if (!player) throw new Error("player cannot be null"); - if (!player.name) throw new Error("player name cannot be null"); - var state = playerStates[player.name]; - return state || (playerStates[player.name] = {}, state = playerStates[player.name]), player - } - - function getCurrentTicks(player) { - if (!player) throw new Error("player cannot be null"); - var playerTime = Math.floor(1e4 * (player || self._currentPlayer).currentTime()); - return getPlayerData(player).streamInfo && (playerTime += getPlayerData(player).streamInfo.transcodingOffsetTicks || 0), playerTime - } - - function playPhotos(items, options, user) { - var playStartIndex = options.startIndex || 0, - player = getPlayer(items[playStartIndex], options); - return loading.hide(), options.items = items, player.play(options) - } - - function playWithIntros(items, options, user) { - var playStartIndex = options.startIndex || 0, - firstItem = items[playStartIndex]; - if (firstItem || (playStartIndex = 0, firstItem = items[playStartIndex]), !firstItem) return showPlaybackInfoErrorMessage(self, "NoCompatibleStream", !1), Promise.reject(); - if ("Photo" === firstItem.MediaType) return playPhotos(items, options, user); - var apiClient = connectionManager.getApiClient(firstItem.ServerId); - return getIntros(firstItem, apiClient, options).then(function(introsResult) { - var introPlayOptions, introItems = introsResult.Items; - return firstItem.playOptions = truncatePlayOptions(options), introPlayOptions = introItems.length ? { - fullscreen: firstItem.playOptions.fullscreen - } : firstItem.playOptions, items = introItems.concat(items), introPlayOptions.items = items, introPlayOptions.startIndex = playStartIndex, playInternal(items[playStartIndex], introPlayOptions, function() { - self._playQueueManager.setPlaylist(items), setPlaylistState(items[playStartIndex].PlaylistItemId, playStartIndex), loading.hide() - }) - }) - } - - function setPlaylistState(playlistItemId, index) { - isNaN(index) || self._playQueueManager.setPlaylistState(playlistItemId, index) - } - - function playInternal(item, playOptions, onPlaybackStartedFn) { - return item.IsPlaceHolder ? (loading.hide(), showPlaybackInfoErrorMessage(self, "PlaceHolder", !0), Promise.reject()) : (normalizePlayOptions(playOptions), playOptions.isFirstItem ? playOptions.isFirstItem = !1 : playOptions.isFirstItem = !0, runInterceptors(item, playOptions).then(function() { - playOptions.fullscreen && loading.show(); - var mediaType = item.MediaType, - onBitrateDetectionFailure = function() { - return playAfterBitrateDetect(getSavedMaxStreamingBitrate(connectionManager.getApiClient(item.ServerId), mediaType), item, playOptions, onPlaybackStartedFn) - }; - if (!isServerItem(item) || itemHelper.isLocalItem(item)) return onBitrateDetectionFailure(); - var apiClient = connectionManager.getApiClient(item.ServerId); - apiClient.getEndpointInfo().then(function(endpointInfo) { - if (("Video" === mediaType || "Audio" === mediaType) && appSettings.enableAutomaticBitrateDetection(endpointInfo.IsInNetwork, mediaType)) return apiClient.detectBitrate().then(function(bitrate) { - return appSettings.maxStreamingBitrate(endpointInfo.IsInNetwork, mediaType, bitrate), playAfterBitrateDetect(bitrate, item, playOptions, onPlaybackStartedFn) - }, onBitrateDetectionFailure); - onBitrateDetectionFailure() - }, onBitrateDetectionFailure) - }, onInterceptorRejection)) - } - - function onInterceptorRejection() { - var player = self._currentPlayer; - return player && (destroyPlayer(player), removeCurrentPlayer(player)), events.trigger(self, "playbackcancelled"), Promise.reject() - } - - function destroyPlayer(player) { - player.destroy() - } - - function runInterceptors(item, playOptions) { - return new Promise(function(resolve, reject) { - var interceptors = pluginManager.ofType("preplayintercept"); - if (interceptors.sort(function(a, b) { - return (a.order || 0) - (b.order || 0) - }), !interceptors.length) return void resolve(); - loading.hide(); - var options = Object.assign({}, playOptions); - options.mediaType = item.MediaType, options.item = item, runNextPrePlay(interceptors, 0, options, resolve, reject) - }) - } - - function runNextPrePlay(interceptors, index, options, resolve, reject) { - if (index >= interceptors.length) return void resolve(); - interceptors[index].intercept(options).then(function() { - runNextPrePlay(interceptors, index + 1, options, resolve, reject) - }, reject) - } - - function sendPlaybackListToPlayer(player, items, deviceProfile, maxBitrate, apiClient, startPositionTicks, mediaSourceId, audioStreamIndex, subtitleStreamIndex, startIndex) { - return setStreamUrls(items, deviceProfile, maxBitrate, apiClient, startPositionTicks).then(function() { - return loading.hide(), player.play({ - items: items, - startPositionTicks: startPositionTicks || 0, - mediaSourceId: mediaSourceId, - audioStreamIndex: audioStreamIndex, - subtitleStreamIndex: subtitleStreamIndex, - startIndex: startIndex - }) - }) - } - - function playAfterBitrateDetect(maxBitrate, item, playOptions, onPlaybackStartedFn) { - var promise, startPosition = playOptions.startPositionTicks, - player = getPlayer(item, playOptions), - activePlayer = self._currentPlayer; - return activePlayer ? (self._playNextAfterEnded = !1, promise = onPlaybackChanging(activePlayer, player, item)) : promise = Promise.resolve(), isServerItem(item) && "Game" !== item.MediaType && "Book" !== item.MediaType ? Promise.all([promise, player.getDeviceProfile(item)]).then(function(responses) { - var deviceProfile = responses[1], - apiClient = connectionManager.getApiClient(item.ServerId), - mediaSourceId = playOptions.mediaSourceId, - audioStreamIndex = playOptions.audioStreamIndex, - subtitleStreamIndex = playOptions.subtitleStreamIndex; - return player && !enableLocalPlaylistManagement(player) ? sendPlaybackListToPlayer(player, playOptions.items, deviceProfile, maxBitrate, apiClient, startPosition, mediaSourceId, audioStreamIndex, subtitleStreamIndex, playOptions.startIndex) : (playOptions.items = null, getPlaybackMediaSource(player, apiClient, deviceProfile, maxBitrate, item, startPosition, mediaSourceId, audioStreamIndex, subtitleStreamIndex).then(function(mediaSource) { - var streamInfo = createStreamInfo(apiClient, item.MediaType, item, mediaSource, startPosition); - return streamInfo.fullscreen = playOptions.fullscreen, getPlayerData(player).isChangingStream = !1, getPlayerData(player).maxStreamingBitrate = maxBitrate, player.play(streamInfo).then(function() { - loading.hide(), onPlaybackStartedFn(), onPlaybackStarted(player, playOptions, streamInfo, mediaSource) - }, function(err) { - onPlaybackStartedFn(), onPlaybackStarted(player, playOptions, streamInfo, mediaSource), setTimeout(function() { - onPlaybackError.call(player, err, { - type: "mediadecodeerror", - streamInfo: streamInfo - }) - }, 100) - }) - })) - }) : promise.then(function() { - var streamInfo = createStreamInfoFromUrlItem(item); - return streamInfo.fullscreen = playOptions.fullscreen, getPlayerData(player).isChangingStream = !1, player.play(streamInfo).then(function() { - loading.hide(), onPlaybackStartedFn(), onPlaybackStarted(player, playOptions, streamInfo) - }, function() { - self.stop(player) - }) - }) - } - - function createStreamInfo(apiClient, type, item, mediaSource, startPosition) { - var mediaUrl, contentType, directOptions, transcodingOffsetTicks = 0, - playerStartPositionTicks = startPosition, - liveStreamId = mediaSource.LiveStreamId, - playMethod = "Transcode", - mediaSourceContainer = (mediaSource.Container || "").toLowerCase(); - if ("Video" === type || "Audio" === type) - if (contentType = getMimeType(type.toLowerCase(), mediaSourceContainer), mediaSource.enableDirectPlay) mediaUrl = mediaSource.Path, playMethod = "DirectPlay"; - else if (mediaSource.StreamUrl) playMethod = "Transcode", mediaUrl = mediaSource.StreamUrl; - else if (mediaSource.SupportsDirectStream) { - directOptions = { - Static: !0, - mediaSourceId: mediaSource.Id, - deviceId: apiClient.deviceId(), - api_key: apiClient.accessToken() - }, mediaSource.ETag && (directOptions.Tag = mediaSource.ETag), mediaSource.LiveStreamId && (directOptions.LiveStreamId = mediaSource.LiveStreamId); - var prefix = "Video" === type ? "Videos" : "Audio"; - mediaUrl = apiClient.getUrl(prefix + "/" + item.Id + "/stream." + mediaSourceContainer, directOptions), playMethod = "DirectStream" - } else mediaSource.SupportsTranscoding && (mediaUrl = apiClient.getUrl(mediaSource.TranscodingUrl), "hls" === mediaSource.TranscodingSubProtocol ? contentType = "application/x-mpegURL" : (playerStartPositionTicks = null, contentType = getMimeType(type.toLowerCase(), mediaSource.TranscodingContainer), -1 === mediaUrl.toLowerCase().indexOf("copytimestamps=true") && (transcodingOffsetTicks = startPosition || 0))); - else mediaUrl = mediaSource.Path, playMethod = "DirectPlay"; - !mediaUrl && mediaSource.SupportsDirectPlay && (mediaUrl = mediaSource.Path, playMethod = "DirectPlay"); - var resultInfo = { - url: mediaUrl, - mimeType: contentType, - transcodingOffsetTicks: transcodingOffsetTicks, - playMethod: playMethod, - playerStartPositionTicks: playerStartPositionTicks, - item: item, - mediaSource: mediaSource, - textTracks: getTextTracks(apiClient, item, mediaSource), - tracks: getTextTracks(apiClient, item, mediaSource), - mediaType: type, - liveStreamId: liveStreamId, - playSessionId: getParam("playSessionId", mediaUrl), - title: item.Name - }, - backdropUrl = backdropImageUrl(apiClient, item, {}); - return backdropUrl && (resultInfo.backdropUrl = backdropUrl), resultInfo - } - - function getTextTracks(apiClient, item, mediaSource) { - for (var subtitleStreams = mediaSource.MediaStreams.filter(function(s) { - return "Subtitle" === s.Type - }), textStreams = subtitleStreams.filter(function(s) { - return "External" === s.DeliveryMethod - }), tracks = [], i = 0, length = textStreams.length; i < length; i++) { - var textStreamUrl, textStream = textStreams[i]; - textStreamUrl = itemHelper.isLocalItem(item) ? textStream.Path : textStream.IsExternalUrl ? textStream.DeliveryUrl : apiClient.getUrl(textStream.DeliveryUrl), tracks.push({ - url: textStreamUrl, - language: textStream.Language || "und", - isDefault: textStream.Index === mediaSource.DefaultSubtitleStreamIndex, - index: textStream.Index, - format: textStream.Codec - }) - } - return tracks - } - - function getPlaybackMediaSource(player, apiClient, deviceProfile, maxBitrate, item, startPosition, mediaSourceId, audioStreamIndex, subtitleStreamIndex) { - return getPlaybackInfo(player, apiClient, item, deviceProfile, maxBitrate, startPosition, !0, mediaSourceId, audioStreamIndex, subtitleStreamIndex, null).then(function(playbackInfoResult) { - return validatePlaybackInfoResult(self, playbackInfoResult) ? getOptimalMediaSource(apiClient, item, playbackInfoResult.MediaSources).then(function(mediaSource) { - return mediaSource ? mediaSource.RequiresOpening && !mediaSource.LiveStreamId ? getLiveStream(player, apiClient, item, playbackInfoResult.PlaySessionId, deviceProfile, maxBitrate, startPosition, mediaSource, null, null).then(function(openLiveStreamResult) { - return supportsDirectPlay(apiClient, item, openLiveStreamResult.MediaSource).then(function(result) { - return openLiveStreamResult.MediaSource.enableDirectPlay = result, openLiveStreamResult.MediaSource - }) - }) : mediaSource : (showPlaybackInfoErrorMessage(self, "NoCompatibleStream"), Promise.reject()) - }) : Promise.reject() - }) - } - - function getPlayer(item, playOptions, forceLocalPlayers) { - var serverItem = isServerItem(item); - return getAutomaticPlayers(self, forceLocalPlayers).filter(function(p) { - if (p.canPlayMediaType(item.MediaType)) { - if (serverItem) return !p.canPlayItem || p.canPlayItem(item, playOptions); - if (item.Url && p.canPlayUrl) return p.canPlayUrl(item.Url) - } - return !1 - })[0] - } - - function queue(options, mode, player) { - if (!(player = player || self._currentPlayer)) return self.play(options); - if (options.items) return translateItemsForPlayback(options.items, options).then(function(items) { - queueAll(items, mode, player) - }); - if (!options.serverId) throw new Error("serverId required!"); - return getItemsForPlayback(options.serverId, { - Ids: options.ids.join(",") - }).then(function(result) { - return translateItemsForPlayback(result.Items, options).then(function(items) { - queueAll(items, mode, player) - }) - }) - } - - function queueAll(items, mode, player) { - if (items.length) { - if (!player.isLocalPlayer) return void("next" === mode ? player.queueNext({ - items: items - }) : player.queue({ - items: items - })); - if (player && !enableLocalPlaylistManagement(player)) { - var apiClient = connectionManager.getApiClient(items[0].ServerId); - return void player.getDeviceProfile(items[0]).then(function(profile) { - setStreamUrls(items, profile, self.getMaxStreamingBitrate(player), apiClient, 0).then(function() { - "next" === mode ? player.queueNext(items) : player.queue(items) - }) - }) - } - "next" === mode ? self._playQueueManager.queueNext(items) : self._playQueueManager.queue(items) - } - } - - function onPlayerProgressInterval() { - sendProgressUpdate(this, "timeupdate") - } - - function startPlaybackProgressTimer(player) { - stopPlaybackProgressTimer(player), player._progressInterval = setInterval(onPlayerProgressInterval.bind(player), 1e4) - } - - function stopPlaybackProgressTimer(player) { - player._progressInterval && (clearInterval(player._progressInterval), player._progressInterval = null) - } - - function onPlaybackStarted(player, playOptions, streamInfo, mediaSource) { - if (!player) throw new Error("player cannot be null"); - setCurrentPlayerInternal(player); - var playerData = getPlayerData(player); - playerData.streamInfo = streamInfo, streamInfo.playbackStartTimeTicks = 1e4 * (new Date).getTime(), mediaSource ? (playerData.audioStreamIndex = mediaSource.DefaultAudioStreamIndex, playerData.subtitleStreamIndex = mediaSource.DefaultSubtitleStreamIndex) : (playerData.audioStreamIndex = null, playerData.subtitleStreamIndex = null), self._playNextAfterEnded = !0; - var isFirstItem = playOptions.isFirstItem, - fullscreen = playOptions.fullscreen, - state = self.getPlayerState(player, streamInfo.item, streamInfo.mediaSource); - reportPlayback(self, state, player, !0, state.NowPlayingItem.ServerId, "reportPlaybackStart"), state.IsFirstItem = isFirstItem, state.IsFullscreen = fullscreen, events.trigger(player, "playbackstart", [state]), events.trigger(self, "playbackstart", [player, state]), streamInfo.started = !0, startPlaybackProgressTimer(player) - } - - function onPlaybackStartedFromSelfManagingPlayer(e, item, mediaSource) { - var player = this; - setCurrentPlayerInternal(player); - var playOptions = item.playOptions || {}, - isFirstItem = playOptions.isFirstItem, - fullscreen = playOptions.fullscreen; - playOptions.isFirstItem = !1; - var playerData = getPlayerData(player); - playerData.streamInfo = {}; - var streamInfo = playerData.streamInfo; - streamInfo.playbackStartTimeTicks = 1e4 * (new Date).getTime(); - var state = self.getPlayerState(player, item, mediaSource); - reportPlayback(self, state, player, !0, state.NowPlayingItem.ServerId, "reportPlaybackStart"), state.IsFirstItem = isFirstItem, state.IsFullscreen = fullscreen, events.trigger(player, "playbackstart", [state]), events.trigger(self, "playbackstart", [player, state]), streamInfo.started = !0, startPlaybackProgressTimer(player) - } - - function onPlaybackStoppedFromSelfManagingPlayer(e, playerStopInfo) { - var player = this; - stopPlaybackProgressTimer(player); - var state = self.getPlayerState(player, playerStopInfo.item, playerStopInfo.mediaSource), - nextItem = playerStopInfo.nextItem, - nextMediaType = playerStopInfo.nextMediaType, - playbackStopInfo = { - player: player, - state: state, - nextItem: nextItem ? nextItem.item : null, - nextMediaType: nextMediaType - }; - state.NextMediaType = nextMediaType, getPlayerData(player).streamInfo.ended = !0, isServerItem(playerStopInfo.item) && (state.PlayState.PositionTicks = 1e4 * (playerStopInfo.positionMs || 0), reportPlayback(self, state, player, !0, playerStopInfo.item.ServerId, "reportPlaybackStopped")), state.NextItem = playbackStopInfo.nextItem, events.trigger(player, "playbackstop", [state]), events.trigger(self, "playbackstop", [playbackStopInfo]); - var nextItemPlayOptions = nextItem ? nextItem.item.playOptions || getDefaultPlayOptions() : getDefaultPlayOptions(); - (nextItem ? getPlayer(nextItem.item, nextItemPlayOptions) : null) !== player && (destroyPlayer(player), removeCurrentPlayer(player)) - } - - function enablePlaybackRetryWithTranscoding(streamInfo, errorType, currentlyPreventsVideoStreamCopy, currentlyPreventsAudioStreamCopy) { - return !(!streamInfo.mediaSource.SupportsTranscoding || currentlyPreventsVideoStreamCopy && currentlyPreventsAudioStreamCopy) - } - - function onPlaybackError(e, error) { - var player = this; - error = error || {}; - var errorType = error.type; - console.log("playbackmanager playback error type: " + (errorType || "")); - var streamInfo = error.streamInfo || getPlayerData(player).streamInfo; - if (streamInfo) { - var currentlyPreventsVideoStreamCopy = -1 !== streamInfo.url.toLowerCase().indexOf("allowvideostreamcopy=false"), - currentlyPreventsAudioStreamCopy = -1 !== streamInfo.url.toLowerCase().indexOf("allowaudiostreamcopy=false"); - if (enablePlaybackRetryWithTranscoding(streamInfo, errorType, currentlyPreventsVideoStreamCopy, currentlyPreventsAudioStreamCopy)) { - return void changeStream(player, getCurrentTicks(player) || streamInfo.playerStartPositionTicks, { - EnableDirectPlay: !1, - EnableDirectStream: !1, - AllowVideoStreamCopy: !1, - AllowAudioStreamCopy: !currentlyPreventsAudioStreamCopy && !currentlyPreventsVideoStreamCopy && null - }, !0) - } - } - onPlaybackStopped.call(player, e, "NoCompatibleStream") - } - - function onPlaybackStopped(e, displayErrorCode) { - var player = this; - if (!getPlayerData(player).isChangingStream) { - stopPlaybackProgressTimer(player); - var state = self.getPlayerState(player), - streamInfo = getPlayerData(player).streamInfo, - nextItem = self._playNextAfterEnded ? self._playQueueManager.getNextItemInfo() : null, - nextMediaType = nextItem ? nextItem.item.MediaType : null, - playbackStopInfo = { - player: player, - state: state, - nextItem: nextItem ? nextItem.item : null, - nextMediaType: nextMediaType - }; - state.NextMediaType = nextMediaType, isServerItem(streamInfo.item) && (!1 === player.supportsProgress && state.PlayState && !state.PlayState.PositionTicks && (state.PlayState.PositionTicks = streamInfo.item.RunTimeTicks), streamInfo.ended = !0, reportPlayback(self, state, player, !0, streamInfo.item.ServerId, "reportPlaybackStopped")), state.NextItem = playbackStopInfo.nextItem, nextItem || self._playQueueManager.reset(), events.trigger(player, "playbackstop", [state]), events.trigger(self, "playbackstop", [playbackStopInfo]); - var nextItemPlayOptions = nextItem ? nextItem.item.playOptions || getDefaultPlayOptions() : getDefaultPlayOptions(); - (nextItem ? getPlayer(nextItem.item, nextItemPlayOptions) : null) !== player && (destroyPlayer(player), removeCurrentPlayer(player)), displayErrorCode && "string" == typeof displayErrorCode ? showPlaybackInfoErrorMessage(self, displayErrorCode, nextItem) : nextItem && self.nextTrack() - } - } - - function onPlaybackChanging(activePlayer, newPlayer, newItem) { - var promise, state = self.getPlayerState(activePlayer), - serverId = self.currentItem(activePlayer).ServerId; - return stopPlaybackProgressTimer(activePlayer), unbindStopped(activePlayer), promise = activePlayer === newPlayer ? activePlayer.stop(!1) : activePlayer.stop(!0), promise.then(function() { - bindStopped(activePlayer), enableLocalPlaylistManagement(activePlayer) && reportPlayback(self, state, activePlayer, !0, serverId, "reportPlaybackStopped"), events.trigger(self, "playbackstop", [{ - player: activePlayer, - state: state, - nextItem: newItem, - nextMediaType: newItem.MediaType - }]) - }) - } - - function bindStopped(player) { - enableLocalPlaylistManagement(player) && (events.off(player, "stopped", onPlaybackStopped), events.on(player, "stopped", onPlaybackStopped)) - } - - function onPlaybackTimeUpdate(e) { - sendProgressUpdate(this, "timeupdate") - } - - function onPlaybackPause(e) { - sendProgressUpdate(this, "pause") - } - - function onPlaybackUnpause(e) { - sendProgressUpdate(this, "unpause") - } - - function onPlaybackVolumeChange(e) { - sendProgressUpdate(this, "volumechange") - } - - function onRepeatModeChange(e) { - sendProgressUpdate(this, "repeatmodechange") - } - - function onPlaylistItemMove(e) { - sendProgressUpdate(this, "playlistitemmove", !0) - } - - function onPlaylistItemRemove(e) { - sendProgressUpdate(this, "playlistitemremove", !0) - } - - function onPlaylistItemAdd(e) { - sendProgressUpdate(this, "playlistitemadd", !0) - } - - function unbindStopped(player) { - events.off(player, "stopped", onPlaybackStopped) - } - - function initLegacyVolumeMethods(player) { - player.getVolume = function() { - return player.volume() - }, player.setVolume = function(val) { - return player.volume(val) - } - } - - function initMediaPlayer(player) { - players.push(player), players.sort(function(a, b) { - return (a.priority || 0) - (b.priority || 0) - }), !1 !== player.isLocalPlayer && (player.isLocalPlayer = !0), player.currentState = {}, player.getVolume && player.setVolume || initLegacyVolumeMethods(player), enableLocalPlaylistManagement(player) ? (events.on(player, "error", onPlaybackError), events.on(player, "timeupdate", onPlaybackTimeUpdate), events.on(player, "pause", onPlaybackPause), events.on(player, "unpause", onPlaybackUnpause), events.on(player, "volumechange", onPlaybackVolumeChange), events.on(player, "repeatmodechange", onRepeatModeChange), events.on(player, "playlistitemmove", onPlaylistItemMove), events.on(player, "playlistitemremove", onPlaylistItemRemove), events.on(player, "playlistitemadd", onPlaylistItemAdd)) : player.isLocalPlayer && (events.on(player, "itemstarted", onPlaybackStartedFromSelfManagingPlayer), events.on(player, "itemstopped", onPlaybackStoppedFromSelfManagingPlayer), events.on(player, "timeupdate", onPlaybackTimeUpdate), events.on(player, "pause", onPlaybackPause), events.on(player, "unpause", onPlaybackUnpause), events.on(player, "volumechange", onPlaybackVolumeChange), events.on(player, "repeatmodechange", onRepeatModeChange), events.on(player, "playlistitemmove", onPlaylistItemMove), events.on(player, "playlistitemremove", onPlaylistItemRemove), events.on(player, "playlistitemadd", onPlaylistItemAdd)), player.isLocalPlayer && bindToFullscreenChange(player), bindStopped(player) - } - - function sendProgressUpdate(player, progressEventName, reportPlaylist) { - if (!player) throw new Error("player cannot be null"); - var state = self.getPlayerState(player); - if (state.NowPlayingItem) { - var serverId = state.NowPlayingItem.ServerId, - streamInfo = getPlayerData(player).streamInfo; - streamInfo && streamInfo.started && !streamInfo.ended && reportPlayback(self, state, player, reportPlaylist, serverId, "reportPlaybackProgress", progressEventName), streamInfo && streamInfo.liveStreamId && (new Date).getTime() - (streamInfo.lastMediaInfoQuery || 0) >= 6e5 && getLiveStreamMediaInfo(player, streamInfo, self.currentMediaSource(player), streamInfo.liveStreamId, serverId) - } - } - - function getLiveStreamMediaInfo(player, streamInfo, mediaSource, liveStreamId, serverId) { - console.log("getLiveStreamMediaInfo"), streamInfo.lastMediaInfoQuery = (new Date).getTime(), connectionManager.getApiClient(serverId).isMinServerVersion("3.2.70.7") && connectionManager.getApiClient(serverId).getLiveStreamMediaInfo(liveStreamId).then(function(info) { - mediaSource.MediaStreams = info.MediaStreams, events.trigger(player, "mediastreamschange") - }, function() {}) - } - var currentTargetInfo, lastLocalPlayer, self = this, - players = [], - currentPairingId = null; - this._playNextAfterEnded = !0; + this._playNextAfterEnded = true; var playerStates = {}; - this._playQueueManager = new PlayQueueManager, self.currentItem = function(player) { - if (!player) throw new Error("player cannot be null"); - if (player.currentItem) return player.currentItem(); + + this._playQueueManager = new PlayQueueManager(); + + self.currentItem = function (player) { + + if (!player) { + throw new Error('player cannot be null'); + } + + if (player.currentItem) { + return player.currentItem(); + } + var data = getPlayerData(player); - return data.streamInfo ? data.streamInfo.item : null - }, self.currentMediaSource = function(player) { - if (!player) throw new Error("player cannot be null"); - if (player.currentMediaSource) return player.currentMediaSource(); + return data.streamInfo ? data.streamInfo.item : null; + }; + + self.currentMediaSource = function (player) { + + if (!player) { + throw new Error('player cannot be null'); + } + + if (player.currentMediaSource) { + return player.currentMediaSource(); + } + var data = getPlayerData(player); - return data.streamInfo ? data.streamInfo.mediaSource : null - }, self.playMethod = function(player) { - if (!player) throw new Error("player cannot be null"); - if (player.playMethod) return player.playMethod(); + return data.streamInfo ? data.streamInfo.mediaSource : null; + }; + + self.playMethod = function (player) { + + if (!player) { + throw new Error('player cannot be null'); + } + + if (player.playMethod) { + return player.playMethod(); + } + var data = getPlayerData(player); - return data.streamInfo ? data.streamInfo.playMethod : null - }, self.playSessionId = function(player) { - if (!player) throw new Error("player cannot be null"); - if (player.playSessionId) return player.playSessionId(); + return data.streamInfo ? data.streamInfo.playMethod : null; + }; + + self.playSessionId = function (player) { + + if (!player) { + throw new Error('player cannot be null'); + } + + if (player.playSessionId) { + return player.playSessionId(); + } + var data = getPlayerData(player); - return data.streamInfo ? data.streamInfo.playSessionId : null - }, self.getPlayerInfo = function() { + return data.streamInfo ? data.streamInfo.playSessionId : null; + }; + + self.getPlayerInfo = function () { + var player = self._currentPlayer; - if (!player) return null; + + if (!player) { + return null; + } + var target = currentTargetInfo || {}; + return { + name: player.name, isLocalPlayer: player.isLocalPlayer, id: target.id, deviceName: target.deviceName, playableMediaTypes: target.playableMediaTypes, supportedCommands: target.supportedCommands + }; + }; + + self.setActivePlayer = function (player, targetInfo) { + + if (player === 'localplayer' || player.name === 'localplayer') { + if (self._currentPlayer && self._currentPlayer.isLocalPlayer) { + return; + } + setCurrentPlayerInternal(null, null); + return; } - }, self.setActivePlayer = function(player, targetInfo) { - if ("localplayer" === player || "localplayer" === player.name) { - if (self._currentPlayer && self._currentPlayer.isLocalPlayer) return; - return void setCurrentPlayerInternal(null, null) + + if (typeof (player) === 'string') { + player = players.filter(function (p) { + return p.name === player; + })[0]; } - if ("string" == typeof player && (player = players.filter(function(p) { - return p.name === player - })[0]), !player) throw new Error("null player"); - setCurrentPlayerInternal(player, targetInfo) - }, self.trySetActivePlayer = function(player, targetInfo) { - if ("localplayer" === player || "localplayer" === player.name) return void(self._currentPlayer && self._currentPlayer.isLocalPlayer); - if ("string" == typeof player && (player = players.filter(function(p) { - return p.name === player - })[0]), !player) throw new Error("null player"); - if (currentPairingId !== targetInfo.id) { - currentPairingId = targetInfo.id; - var promise = player.tryPair ? player.tryPair(targetInfo) : Promise.resolve(); - events.trigger(self, "pairing"), promise.then(function() { - events.trigger(self, "paired"), setCurrentPlayerInternal(player, targetInfo) - }, function() { - events.trigger(self, "pairerror"), currentPairingId === targetInfo.id && (currentPairingId = null) - }) + + if (!player) { + throw new Error('null player'); } - }, self.getTargets = function() { + + setCurrentPlayerInternal(player, targetInfo); + }; + + self.trySetActivePlayer = function (player, targetInfo) { + + if (player === 'localplayer' || player.name === 'localplayer') { + if (self._currentPlayer && self._currentPlayer.isLocalPlayer) { + return; + } + return; + } + + if (typeof (player) === 'string') { + player = players.filter(function (p) { + return p.name === player; + })[0]; + } + + if (!player) { + throw new Error('null player'); + } + + if (currentPairingId === targetInfo.id) { + return; + } + + currentPairingId = targetInfo.id; + + var promise = player.tryPair ? + player.tryPair(targetInfo) : + Promise.resolve(); + + events.trigger(self, 'pairing'); + + promise.then(function () { + + events.trigger(self, 'paired'); + + setCurrentPlayerInternal(player, targetInfo); + }, function () { + + events.trigger(self, 'pairerror'); + + if (currentPairingId === targetInfo.id) { + currentPairingId = null; + } + }); + }; + + self.getTargets = function () { + var promises = players.filter(displayPlayerIndividually).map(getPlayerTargets); - return Promise.all(promises).then(function(responses) { - return connectionManager.currentApiClient().getCurrentUser().then(function(user) { + + return Promise.all(promises).then(function (responses) { + + return connectionManager.currentApiClient().getCurrentUser().then(function (user) { + var targets = []; + targets.push({ - name: globalize.translate("sharedcomponents#HeaderMyDevice"), - id: "localplayer", - playerName: "localplayer", - playableMediaTypes: ["Audio", "Video", "Game", "Photo", "Book"], - isLocalPlayer: !0, + name: globalize.translate('sharedcomponents#HeaderMyDevice'), + id: 'localplayer', + playerName: 'localplayer', + playableMediaTypes: ['Audio', 'Video', 'Game', 'Photo', 'Book'], + isLocalPlayer: true, supportedCommands: self.getSupportedCommands({ - isLocalPlayer: !0 + isLocalPlayer: true }), user: user }); - for (var i = 0; i < responses.length; i++) - for (var subTargets = responses[i], j = 0; j < subTargets.length; j++) targets.push(subTargets[j]); - return targets = targets.sort(sortPlayerTargets) - }) - }) - }, self.getPlaylist = function(player) { - return player = player || self._currentPlayer, player && !enableLocalPlaylistManagement(player) ? player.getPlaylistSync ? Promise.resolve(player.getPlaylistSync()) : player.getPlaylist() : Promise.resolve(self._playQueueManager.getPlaylist()) - }, self.isPlaying = function(player) { - return player = player || self._currentPlayer, player && player.isPlaying ? player.isPlaying() : null != player && null != player.currentSrc() - }, self.isPlayingMediaType = function(mediaType, player) { - if ((player = player || self._currentPlayer) && player.isPlaying) return player.isPlaying(mediaType); + + for (var i = 0; i < responses.length; i++) { + + var subTargets = responses[i]; + + for (var j = 0; j < subTargets.length; j++) { + + targets.push(subTargets[j]); + } + } + + targets = targets.sort(sortPlayerTargets); + + return targets; + }); + }); + }; + + function getCurrentSubtitleStream(player) { + + if (!player) { + throw new Error('player cannot be null'); + } + + var index = getPlayerData(player).subtitleStreamIndex; + + if (index == null || index === -1) { + return null; + } + + return getSubtitleStream(player, index); + } + + function getSubtitleStream(player, index) { + return self.subtitleTracks(player).filter(function (s) { + return s.Type === 'Subtitle' && s.Index === index; + })[0]; + } + + self.getPlaylist = function (player) { + + player = player || self._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + + if (player.getPlaylistSync) { + return Promise.resolve(player.getPlaylistSync()); + } + + return player.getPlaylist(); + } + + return Promise.resolve(self._playQueueManager.getPlaylist()); + }; + + function removeCurrentPlayer(player) { + var previousPlayer = self._currentPlayer; + + if (!previousPlayer || player.id === previousPlayer.id) { + setCurrentPlayerInternal(null); + } + } + + function setCurrentPlayerInternal(player, targetInfo) { + + var previousPlayer = self._currentPlayer; + var previousTargetInfo = currentTargetInfo; + + if (player && !targetInfo && player.isLocalPlayer) { + targetInfo = createTarget(self, player); + } + + if (player && !targetInfo) { + throw new Error('targetInfo cannot be null'); + } + + currentPairingId = null; + self._currentPlayer = player; + currentTargetInfo = targetInfo; + + if (targetInfo) { + console.log('Active player: ' + JSON.stringify(targetInfo)); + } + + if (player && player.isLocalPlayer) { + lastLocalPlayer = player; + } + + if (previousPlayer) { + self.endPlayerUpdates(previousPlayer); + } + + if (player) { + self.beginPlayerUpdates(player); + } + + triggerPlayerChange(self, player, targetInfo, previousPlayer, previousTargetInfo); + } + + self.isPlaying = function (player) { + + player = player || self._currentPlayer; + + if (player) { + if (player.isPlaying) { + return player.isPlaying(); + } + } + + return player != null && player.currentSrc() != null; + }; + + self.isPlayingMediaType = function (mediaType, player) { + player = player || self._currentPlayer; + + if (player) { + if (player.isPlaying) { + return player.isPlaying(mediaType); + } + } + if (self.isPlaying(player)) { - return getPlayerData(player).streamInfo.mediaType === mediaType + var playerData = getPlayerData(player); + + return playerData.streamInfo.mediaType === mediaType; } - return !1 - }, self.isPlayingLocally = function(mediaTypes, player) { - return !(!(player = player || self._currentPlayer) || !player.isLocalPlayer) && mediaTypes.filter(function(mediaType) { - return self.isPlayingMediaType(mediaType, player) - }).length > 0 - }, self.isPlayingVideo = function(player) { - return self.isPlayingMediaType("Video", player) - }, self.isPlayingAudio = function(player) { - return self.isPlayingMediaType("Audio", player) - }, self.getPlayers = function() { - return players - }, self.canPlay = function(item) { + + return false; + }; + + self.isPlayingLocally = function (mediaTypes, player) { + + player = player || self._currentPlayer; + + if (!player || !player.isLocalPlayer) { + return false; + } + + return mediaTypes.filter(function (mediaType) { + + return self.isPlayingMediaType(mediaType, player); + + }).length > 0; + }; + + self.isPlayingVideo = function (player) { + return self.isPlayingMediaType('Video', player); + }; + + self.isPlayingAudio = function (player) { + return self.isPlayingMediaType('Audio', player); + }; + + self.getPlayers = function () { + + return players; + }; + + function getDefaultPlayOptions() { + return { + fullscreen: true + }; + } + + self.canPlay = function (item) { + var itemType = item.Type; - if ("PhotoAlbum" === itemType || "MusicGenre" === itemType || "Season" === itemType || "Series" === itemType || "BoxSet" === itemType || "MusicAlbum" === itemType || "MusicArtist" === itemType || "Playlist" === itemType) return !0; - if ("Virtual" === item.LocationType && "Program" !== itemType) return !1; - if ("Program" === itemType) { - if (!item.EndDate || !item.StartDate) return !1; - if ((new Date).getTime() > datetime.parseISO8601Date(item.EndDate).getTime() || (new Date).getTime() < datetime.parseISO8601Date(item.StartDate).getTime()) return !1 + + if (itemType === "PhotoAlbum" || itemType === "MusicGenre" || itemType === "Season" || itemType === "Series" || itemType === "BoxSet" || itemType === "MusicAlbum" || itemType === "MusicArtist" || itemType === "Playlist") { + return true; } - return null != getPlayer(item, getDefaultPlayOptions()) - }, self.toggleAspectRatio = function(player) { - if (player = player || self._currentPlayer) { - for (var current = self.getAspectRatio(player), supported = self.getSupportedAspectRatios(player), index = -1, i = 0, length = supported.length; i < length; i++) + + if (item.LocationType === "Virtual") { + if (itemType !== "Program") { + return false; + } + } + + if (itemType === "Program") { + + if (!item.EndDate || !item.StartDate) { + return false; + } + + if (new Date().getTime() > datetime.parseISO8601Date(item.EndDate).getTime() || new Date().getTime() < datetime.parseISO8601Date(item.StartDate).getTime()) { + return false; + } + } + + //var mediaType = item.MediaType; + return getPlayer(item, getDefaultPlayOptions()) != null; + }; + + self.toggleAspectRatio = function (player) { + + player = player || self._currentPlayer; + + if (player) { + var current = self.getAspectRatio(player); + + var supported = self.getSupportedAspectRatios(player); + + var index = -1; + for (var i = 0, length = supported.length; i < length; i++) { if (supported[i].id === current) { index = i; - break - } index++, index >= supported.length && (index = 0), self.setAspectRatio(supported[index].id, player) + break; + } + } + + index++; + if (index >= supported.length) { + index = 0; + } + + self.setAspectRatio(supported[index].id, player); } - }, self.setAspectRatio = function(val, player) { - (player = player || self._currentPlayer) && player.setAspectRatio && player.setAspectRatio(val) - }, self.getSupportedAspectRatios = function(player) { - return player = player || self._currentPlayer, player && player.getSupportedAspectRatios ? player.getSupportedAspectRatios() : [] - }, self.getAspectRatio = function(player) { - if ((player = player || self._currentPlayer) && player.getAspectRatio) return player.getAspectRatio() }; + + self.setAspectRatio = function (val, player) { + + player = player || self._currentPlayer; + + if (player && player.setAspectRatio) { + + player.setAspectRatio(val); + } + }; + + self.getSupportedAspectRatios = function (player) { + + player = player || self._currentPlayer; + + if (player && player.getSupportedAspectRatios) { + return player.getSupportedAspectRatios(); + } + + return []; + }; + + self.getAspectRatio = function (player) { + + player = player || self._currentPlayer; + + if (player && player.getAspectRatio) { + return player.getAspectRatio(); + } + }; + var brightnessOsdLoaded; - self.setBrightness = function(val, player) { - (player = player || self._currentPlayer) && (brightnessOsdLoaded || (brightnessOsdLoaded = !0, require(["brightnessOsd"])), player.setBrightness(val)) - }, self.getBrightness = function(player) { - if (player = player || self._currentPlayer) return player.getBrightness() - }, self.setVolume = function(val, player) { - (player = player || self._currentPlayer) && player.setVolume(val) - }, self.getVolume = function(player) { - if (player = player || self._currentPlayer) return player.getVolume() - }, self.volumeUp = function(player) { - (player = player || self._currentPlayer) && player.volumeUp() - }, self.volumeDown = function(player) { - (player = player || self._currentPlayer) && player.volumeDown() - }, self.changeAudioStream = function(player) { - if ((player = player || self._currentPlayer) && !enableLocalPlaylistManagement(player)) return player.changeAudioStream(); + self.setBrightness = function (val, player) { + + player = player || self._currentPlayer; + if (player) { - var i, length, currentMediaSource = self.currentMediaSource(player), - mediaStreams = []; - for (i = 0, length = currentMediaSource.MediaStreams.length; i < length; i++) "Audio" === currentMediaSource.MediaStreams[i].Type && mediaStreams.push(currentMediaSource.MediaStreams[i]); - if (!(mediaStreams.length <= 1)) { - var currentStreamIndex = self.getAudioStreamIndex(player), - indexInList = -1; - for (i = 0, length = mediaStreams.length; i < length; i++) - if (mediaStreams[i].Index === currentStreamIndex) { - indexInList = i; - break - } var nextIndex = indexInList + 1; - nextIndex >= mediaStreams.length && (nextIndex = 0), nextIndex = -1 === nextIndex ? -1 : mediaStreams[nextIndex].Index, self.setAudioStreamIndex(nextIndex, player) + + if (!brightnessOsdLoaded) { + brightnessOsdLoaded = true; + // TODO: Have this trigger an event instead to get the osd out of here + require(['brightnessOsd']); + } + player.setBrightness(val); + } + }; + + self.getBrightness = function (player) { + + player = player || self._currentPlayer; + + if (player) { + return player.getBrightness(); + } + }; + + self.setVolume = function (val, player) { + + player = player || self._currentPlayer; + + if (player) { + player.setVolume(val); + } + }; + + self.getVolume = function (player) { + + player = player || self._currentPlayer; + + if (player) { + return player.getVolume(); + } + }; + + self.volumeUp = function (player) { + + player = player || self._currentPlayer; + + if (player) { + player.volumeUp(); + } + }; + + self.volumeDown = function (player) { + + player = player || self._currentPlayer; + + if (player) { + player.volumeDown(); + } + }; + + self.changeAudioStream = function (player) { + + player = player || self._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + return player.changeAudioStream(); + } + + if (!player) { + return; + } + + var currentMediaSource = self.currentMediaSource(player); + var mediaStreams = []; + var i, length; + for (i = 0, length = currentMediaSource.MediaStreams.length; i < length; i++) { + if (currentMediaSource.MediaStreams[i].Type === 'Audio') { + mediaStreams.push(currentMediaSource.MediaStreams[i]); } } - }, self.changeSubtitleStream = function(player) { - if ((player = player || self._currentPlayer) && !enableLocalPlaylistManagement(player)) return player.changeSubtitleStream(); - if (player) { - var i, length, currentMediaSource = self.currentMediaSource(player), - mediaStreams = []; - for (i = 0, length = currentMediaSource.MediaStreams.length; i < length; i++) "Subtitle" === currentMediaSource.MediaStreams[i].Type && mediaStreams.push(currentMediaSource.MediaStreams[i]); - if (mediaStreams.length) { - var currentStreamIndex = self.getSubtitleStreamIndex(player), - indexInList = -1; - for (i = 0, length = mediaStreams.length; i < length; i++) - if (mediaStreams[i].Index === currentStreamIndex) { - indexInList = i; - break - } var nextIndex = indexInList + 1; - nextIndex >= mediaStreams.length && (nextIndex = -1), nextIndex = -1 === nextIndex ? -1 : mediaStreams[nextIndex].Index, self.setSubtitleStreamIndex(nextIndex, player) + + // Nothing to change + if (mediaStreams.length <= 1) { + return; + } + + var currentStreamIndex = self.getAudioStreamIndex(player); + var indexInList = -1; + for (i = 0, length = mediaStreams.length; i < length; i++) { + if (mediaStreams[i].Index === currentStreamIndex) { + indexInList = i; + break; } } - }, self.getAudioStreamIndex = function(player) { - return player = player || self._currentPlayer, player && !enableLocalPlaylistManagement(player) ? player.getAudioStreamIndex() : getPlayerData(player).audioStreamIndex - }, self.setAudioStreamIndex = function(index, player) { - if ((player = player || self._currentPlayer) && !enableLocalPlaylistManagement(player)) return player.setAudioStreamIndex(index); - "Transcode" !== self.playMethod(player) && player.canSetAudioStreamIndex() ? player.getDeviceProfile(self.currentItem(player)).then(function(profile) { - isAudioStreamSupported(self.currentMediaSource(player), index, profile) ? (player.setAudioStreamIndex(index), getPlayerData(player).audioStreamIndex = index) : (changeStream(player, getCurrentTicks(player), { - AudioStreamIndex: index - }), getPlayerData(player).audioStreamIndex = index) - }) : (changeStream(player, getCurrentTicks(player), { - AudioStreamIndex: index - }), getPlayerData(player).audioStreamIndex = index) - }, self.getMaxStreamingBitrate = function(player) { - if ((player = player || self._currentPlayer) && player.getMaxStreamingBitrate) return player.getMaxStreamingBitrate(); + + var nextIndex = indexInList + 1; + if (nextIndex >= mediaStreams.length) { + nextIndex = 0; + } + + nextIndex = nextIndex === -1 ? -1 : mediaStreams[nextIndex].Index; + + self.setAudioStreamIndex(nextIndex, player); + }; + + self.changeSubtitleStream = function (player) { + + player = player || self._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + return player.changeSubtitleStream(); + } + + if (!player) { + return; + } + + var currentMediaSource = self.currentMediaSource(player); + var mediaStreams = []; + var i, length; + for (i = 0, length = currentMediaSource.MediaStreams.length; i < length; i++) { + if (currentMediaSource.MediaStreams[i].Type === 'Subtitle') { + mediaStreams.push(currentMediaSource.MediaStreams[i]); + } + } + + // No known streams, nothing to change + if (!mediaStreams.length) { + return; + } + + var currentStreamIndex = self.getSubtitleStreamIndex(player); + var indexInList = -1; + for (i = 0, length = mediaStreams.length; i < length; i++) { + if (mediaStreams[i].Index === currentStreamIndex) { + indexInList = i; + break; + } + } + + var nextIndex = indexInList + 1; + if (nextIndex >= mediaStreams.length) { + nextIndex = -1; + } + + nextIndex = nextIndex === -1 ? -1 : mediaStreams[nextIndex].Index; + + self.setSubtitleStreamIndex(nextIndex, player); + }; + + self.getAudioStreamIndex = function (player) { + + player = player || self._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + return player.getAudioStreamIndex(); + } + + return getPlayerData(player).audioStreamIndex; + }; + + function isAudioStreamSupported(mediaSource, index, deviceProfile) { + + var mediaStream; + var i, length; + var mediaStreams = mediaSource.MediaStreams; + + for (i = 0, length = mediaStreams.length; i < length; i++) { + if (mediaStreams[i].Type === 'Audio' && mediaStreams[i].Index === index) { + mediaStream = mediaStreams[i]; + break; + } + } + + if (!mediaStream) { + return false; + } + + var codec = (mediaStream.Codec || '').toLowerCase(); + + if (!codec) { + return false; + } + + var profiles = deviceProfile.DirectPlayProfiles || []; + + return profiles.filter(function (p) { + + if (p.Type === 'Video') { + + if (!p.AudioCodec) { + return true; + } + + // This is an exclusion filter + if (p.AudioCodec.indexOf('-') === 0) { + return p.AudioCodec.toLowerCase().indexOf(codec) === -1; + } + + return p.AudioCodec.toLowerCase().indexOf(codec) !== -1; + } + + return false; + + }).length > 0; + } + + self.setAudioStreamIndex = function (index, player) { + + player = player || self._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + return player.setAudioStreamIndex(index); + } + + if (self.playMethod(player) === 'Transcode' || !player.canSetAudioStreamIndex()) { + + changeStream(player, getCurrentTicks(player), { AudioStreamIndex: index }); + getPlayerData(player).audioStreamIndex = index; + + } else { + + // See if the player supports the track without transcoding + player.getDeviceProfile(self.currentItem(player)).then(function (profile) { + + if (isAudioStreamSupported(self.currentMediaSource(player), index, profile)) { + player.setAudioStreamIndex(index); + getPlayerData(player).audioStreamIndex = index; + } + else { + changeStream(player, getCurrentTicks(player), { AudioStreamIndex: index }); + getPlayerData(player).audioStreamIndex = index; + } + }); + } + }; + + function getSavedMaxStreamingBitrate(apiClient, mediaType) { + + if (!apiClient) { + // This should hopefully never happen + apiClient = connectionManager.currentApiClient(); + } + + var endpointInfo = apiClient.getSavedEndpointInfo() || {}; + + return appSettings.maxStreamingBitrate(endpointInfo.IsInNetwork, mediaType); + } + + self.getMaxStreamingBitrate = function (player) { + + player = player || self._currentPlayer; + if (player && player.getMaxStreamingBitrate) { + return player.getMaxStreamingBitrate(); + } + var playerData = getPlayerData(player); - if (playerData.maxStreamingBitrate) return playerData.maxStreamingBitrate; - var mediaType = playerData.streamInfo ? playerData.streamInfo.mediaType : null, - currentItem = self.currentItem(player); - return getSavedMaxStreamingBitrate(currentItem ? connectionManager.getApiClient(currentItem.ServerId) : connectionManager.currentApiClient(), mediaType) - }, self.enableAutomaticBitrateDetection = function(player) { - if ((player = player || self._currentPlayer) && player.enableAutomaticBitrateDetection) return player.enableAutomaticBitrateDetection(); - var playerData = getPlayerData(player), - mediaType = playerData.streamInfo ? playerData.streamInfo.mediaType : null, - currentItem = self.currentItem(player), - apiClient = currentItem ? connectionManager.getApiClient(currentItem.ServerId) : connectionManager.currentApiClient(), - endpointInfo = apiClient.getSavedEndpointInfo() || {}; - return appSettings.enableAutomaticBitrateDetection(endpointInfo.IsInNetwork, mediaType) - }, self.setMaxStreamingBitrate = function(options, player) { - if ((player = player || self._currentPlayer) && player.setMaxStreamingBitrate) return player.setMaxStreamingBitrate(options); + + if (playerData.maxStreamingBitrate) { + return playerData.maxStreamingBitrate; + } + + var mediaType = playerData.streamInfo ? playerData.streamInfo.mediaType : null; + var currentItem = self.currentItem(player); + + var apiClient = currentItem ? connectionManager.getApiClient(currentItem.ServerId) : connectionManager.currentApiClient(); + return getSavedMaxStreamingBitrate(apiClient, mediaType); + }; + + self.enableAutomaticBitrateDetection = function (player) { + + player = player || self._currentPlayer; + if (player && player.enableAutomaticBitrateDetection) { + return player.enableAutomaticBitrateDetection(); + } + + var playerData = getPlayerData(player); + var mediaType = playerData.streamInfo ? playerData.streamInfo.mediaType : null; + var currentItem = self.currentItem(player); + + var apiClient = currentItem ? connectionManager.getApiClient(currentItem.ServerId) : connectionManager.currentApiClient(); + var endpointInfo = apiClient.getSavedEndpointInfo() || {}; + + return appSettings.enableAutomaticBitrateDetection(endpointInfo.IsInNetwork, mediaType); + }; + + self.setMaxStreamingBitrate = function (options, player) { + + player = player || self._currentPlayer; + if (player && player.setMaxStreamingBitrate) { + return player.setMaxStreamingBitrate(options); + } + var apiClient = connectionManager.getApiClient(self.currentItem(player).ServerId); - apiClient.getEndpointInfo().then(function(endpointInfo) { - var promise, playerData = getPlayerData(player), - mediaType = playerData.streamInfo ? playerData.streamInfo.mediaType : null; - options.enableAutomaticBitrateDetection ? (appSettings.enableAutomaticBitrateDetection(endpointInfo.IsInNetwork, mediaType, !0), promise = apiClient.detectBitrate(!0)) : (appSettings.enableAutomaticBitrateDetection(endpointInfo.IsInNetwork, mediaType, !1), promise = Promise.resolve(options.maxBitrate)), promise.then(function(bitrate) { - appSettings.maxStreamingBitrate(endpointInfo.IsInNetwork, mediaType, bitrate), changeStream(player, getCurrentTicks(player), { + + apiClient.getEndpointInfo().then(function (endpointInfo) { + + var playerData = getPlayerData(player); + var mediaType = playerData.streamInfo ? playerData.streamInfo.mediaType : null; + + var promise; + if (options.enableAutomaticBitrateDetection) { + appSettings.enableAutomaticBitrateDetection(endpointInfo.IsInNetwork, mediaType, true); + promise = apiClient.detectBitrate(true); + } else { + appSettings.enableAutomaticBitrateDetection(endpointInfo.IsInNetwork, mediaType, false); + promise = Promise.resolve(options.maxBitrate); + } + + promise.then(function (bitrate) { + + appSettings.maxStreamingBitrate(endpointInfo.IsInNetwork, mediaType, bitrate); + + changeStream(player, getCurrentTicks(player), { MaxStreamingBitrate: bitrate - }) - }) - }) - }, self.isFullscreen = function(player) { - return player = player || self._currentPlayer, !player.isLocalPlayer || player.isFullscreen ? player.isFullscreen() : fullscreenManager.isFullScreen() - }, self.toggleFullscreen = function(player) { - if (player = player || self._currentPlayer, !player.isLocalPlayer || player.toggleFulscreen) return player.toggleFulscreen(); - fullscreenManager.isFullScreen() ? fullscreenManager.exitFullscreen() : fullscreenManager.requestFullscreen() - }, self.togglePictureInPicture = function(player) { - return player = player || self._currentPlayer, player.togglePictureInPicture() - }, self.getSubtitleStreamIndex = function(player) { - if ((player = player || self._currentPlayer) && !enableLocalPlaylistManagement(player)) return player.getSubtitleStreamIndex(); - if (!player) throw new Error("player cannot be null"); - return getPlayerData(player).subtitleStreamIndex - }, self.setSubtitleStreamIndex = function(index, player) { - if ((player = player || self._currentPlayer) && !enableLocalPlaylistManagement(player)) return player.setSubtitleStreamIndex(index); - var currentStream = getCurrentSubtitleStream(player), - newStream = getSubtitleStream(player, index); - if (currentStream || newStream) { - var selectedTrackElementIndex = -1, - currentPlayMethod = self.playMethod(player); - currentStream && !newStream ? ("Encode" === getDeliveryMethod(currentStream) || "Embed" === getDeliveryMethod(currentStream) && "Transcode" === currentPlayMethod) && changeStream(player, getCurrentTicks(player), { - SubtitleStreamIndex: -1 - }) : !currentStream && newStream ? "External" === getDeliveryMethod(newStream) ? selectedTrackElementIndex = index : "Embed" === getDeliveryMethod(newStream) && "Transcode" !== currentPlayMethod ? selectedTrackElementIndex = index : changeStream(player, getCurrentTicks(player), { - SubtitleStreamIndex: index - }) : currentStream && newStream && ("External" === getDeliveryMethod(newStream) || "Embed" === getDeliveryMethod(newStream) && "Transcode" !== currentPlayMethod ? (selectedTrackElementIndex = index, "External" !== getDeliveryMethod(currentStream) && "Embed" !== getDeliveryMethod(currentStream) && changeStream(player, getCurrentTicks(player), { - SubtitleStreamIndex: -1 - })) : changeStream(player, getCurrentTicks(player), { - SubtitleStreamIndex: index - })), player.setSubtitleStreamIndex(selectedTrackElementIndex), getPlayerData(player).subtitleStreamIndex = index - } - }, self.seek = function(ticks, player) { - if (ticks = Math.max(0, ticks), (player = player || self._currentPlayer) && !enableLocalPlaylistManagement(player)) return player.isLocalPlayer ? player.seek((ticks || 0) / 1e4) : player.seek(ticks); - changeStream(player, ticks) - }, self.seekRelative = function(offsetTicks, player) { - if ((player = player || self._currentPlayer) && !enableLocalPlaylistManagement(player) && player.seekRelative) return player.isLocalPlayer ? player.seekRelative((ticks || 0) / 1e4) : player.seekRelative(ticks); - var ticks = getCurrentTicks(player) + offsetTicks; - return this.seek(ticks, player) - }, self.play = function(options) { - if (normalizePlayOptions(options), self._currentPlayer) { - if (!1 === options.enableRemotePlayers && !self._currentPlayer.isLocalPlayer) return Promise.reject(); - if (!self._currentPlayer.isLocalPlayer) return self._currentPlayer.play(options) - } - if (options.fullscreen && loading.show(), options.items) return translateItemsForPlayback(options.items, options).then(function(items) { - return playWithIntros(items, options) + }); + }); }); - if (!options.serverId) throw new Error("serverId required!"); - return getItemsForPlayback(options.serverId, { - Ids: options.ids.join(",") - }).then(function(result) { - return translateItemsForPlayback(result.Items, options).then(function(items) { - return playWithIntros(items, options) - }) - }) - }, self.getPlayerState = function(player, item, mediaSource) { - if (!(player = player || self._currentPlayer)) throw new Error("player cannot be null"); - if (!enableLocalPlaylistManagement(player) && player.getPlayerState) return player.getPlayerState(); - item = item || self.currentItem(player), mediaSource = mediaSource || self.currentMediaSource(player); + }; + + self.isFullscreen = function (player) { + + player = player || self._currentPlayer; + if (!player.isLocalPlayer || player.isFullscreen) { + return player.isFullscreen(); + } + + return fullscreenManager.isFullScreen(); + }; + + self.toggleFullscreen = function (player) { + + player = player || self._currentPlayer; + if (!player.isLocalPlayer || player.toggleFulscreen) { + return player.toggleFulscreen(); + } + + if (fullscreenManager.isFullScreen()) { + fullscreenManager.exitFullscreen(); + } else { + fullscreenManager.requestFullscreen(); + } + }; + + self.togglePictureInPicture = function (player) { + player = player || self._currentPlayer; + return player.togglePictureInPicture(); + }; + + self.getSubtitleStreamIndex = function (player) { + + player = player || self._currentPlayer; + + if (player && !enableLocalPlaylistManagement(player)) { + return player.getSubtitleStreamIndex(); + } + + if (!player) { + throw new Error('player cannot be null'); + } + + return getPlayerData(player).subtitleStreamIndex; + }; + + function getDeliveryMethod(subtitleStream) { + + // This will be null for internal subs for local items + if (subtitleStream.DeliveryMethod) { + return subtitleStream.DeliveryMethod; + } + + return subtitleStream.IsExternal ? 'External' : 'Embed'; + } + + self.setSubtitleStreamIndex = function (index, player) { + + player = player || self._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + return player.setSubtitleStreamIndex(index); + } + + var currentStream = getCurrentSubtitleStream(player); + + var newStream = getSubtitleStream(player, index); + + if (!currentStream && !newStream) { + return; + } + + var selectedTrackElementIndex = -1; + + var currentPlayMethod = self.playMethod(player); + + if (currentStream && !newStream) { + + if (getDeliveryMethod(currentStream) === 'Encode' || (getDeliveryMethod(currentStream) === 'Embed' && currentPlayMethod === 'Transcode')) { + + // Need to change the transcoded stream to remove subs + changeStream(player, getCurrentTicks(player), { SubtitleStreamIndex: -1 }); + } + } + else if (!currentStream && newStream) { + + if (getDeliveryMethod(newStream) === 'External') { + selectedTrackElementIndex = index; + } else if (getDeliveryMethod(newStream) === 'Embed' && currentPlayMethod !== 'Transcode') { + selectedTrackElementIndex = index; + } else { + + // Need to change the transcoded stream to add subs + changeStream(player, getCurrentTicks(player), { SubtitleStreamIndex: index }); + } + } + else if (currentStream && newStream) { + + // Switching tracks + // We can handle this clientside if the new track is external or the new track is embedded and we're not transcoding + if (getDeliveryMethod(newStream) === 'External' || (getDeliveryMethod(newStream) === 'Embed' && currentPlayMethod !== 'Transcode')) { + selectedTrackElementIndex = index; + + // But in order to handle this client side, if the previous track is being added via transcoding, we'll have to remove it + if (getDeliveryMethod(currentStream) !== 'External' && getDeliveryMethod(currentStream) !== 'Embed') { + changeStream(player, getCurrentTicks(player), { SubtitleStreamIndex: -1 }); + } + } else { + + // Need to change the transcoded stream to add subs + changeStream(player, getCurrentTicks(player), { SubtitleStreamIndex: index }); + } + } + + player.setSubtitleStreamIndex(selectedTrackElementIndex); + + getPlayerData(player).subtitleStreamIndex = index; + }; + + self.seek = function (ticks, player) { + + ticks = Math.max(0, ticks); + + player = player || self._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + + if (player.isLocalPlayer) { + return player.seek((ticks || 0) / 10000); + } else { + return player.seek(ticks); + } + } + + changeStream(player, ticks); + }; + + self.seekRelative = function (offsetTicks, player) { + + player = player || self._currentPlayer; + if (player && !enableLocalPlaylistManagement(player) && player.seekRelative) { + + if (player.isLocalPlayer) { + return player.seekRelative((ticks || 0) / 10000); + } else { + return player.seekRelative(ticks); + } + } + + var ticks = getCurrentTicks(player) + offsetTicks; + return this.seek(ticks, player); + }; + + // Returns true if the player can seek using native client-side seeking functions + function canPlayerSeek(player) { + + if (!player) { + throw new Error('player cannot be null'); + } + + var playerData = getPlayerData(player); + + var currentSrc = (playerData.streamInfo.url || '').toLowerCase(); + + if (currentSrc.indexOf('.m3u8') !== -1) { + return true; + } + + if (player.seekable) { + return player.seekable(); + } + + var isPlayMethodTranscode = self.playMethod(player) === 'Transcode'; + + if (isPlayMethodTranscode) { + return false; + } + + return player.duration(); + } + + function changeStream(player, ticks, params) { + + if (canPlayerSeek(player) && params == null) { + + player.currentTime(parseInt(ticks / 10000)); + return; + } + + params = params || {}; + + var liveStreamId = getPlayerData(player).streamInfo.liveStreamId; + var lastMediaInfoQuery = getPlayerData(player).streamInfo.lastMediaInfoQuery; + + var playSessionId = self.playSessionId(player); + + var currentItem = self.currentItem(player); + + player.getDeviceProfile(currentItem, { + + isRetry: params.EnableDirectPlay === false + + }).then(function (deviceProfile) { + + var audioStreamIndex = params.AudioStreamIndex == null ? getPlayerData(player).audioStreamIndex : params.AudioStreamIndex; + var subtitleStreamIndex = params.SubtitleStreamIndex == null ? getPlayerData(player).subtitleStreamIndex : params.SubtitleStreamIndex; + + var currentMediaSource = self.currentMediaSource(player); + var apiClient = connectionManager.getApiClient(currentItem.ServerId); + + if (ticks) { + ticks = parseInt(ticks); + } + + var maxBitrate = params.MaxStreamingBitrate || self.getMaxStreamingBitrate(player); + + var currentPlayOptions = currentItem.playOptions || {}; + + getPlaybackInfo(player, apiClient, currentItem, deviceProfile, maxBitrate, ticks, true, currentMediaSource.Id, audioStreamIndex, subtitleStreamIndex, liveStreamId, params.EnableDirectPlay, params.EnableDirectStream, params.AllowVideoStreamCopy, params.AllowAudioStreamCopy).then(function (result) { + + if (validatePlaybackInfoResult(self, result)) { + + currentMediaSource = result.MediaSources[0]; + + var streamInfo = createStreamInfo(apiClient, currentItem.MediaType, currentItem, currentMediaSource, ticks); + streamInfo.fullscreen = currentPlayOptions.fullscreen; + streamInfo.lastMediaInfoQuery = lastMediaInfoQuery; + + if (!streamInfo.url) { + showPlaybackInfoErrorMessage(self, 'NoCompatibleStream', true); + return; + } + + getPlayerData(player).subtitleStreamIndex = subtitleStreamIndex; + getPlayerData(player).audioStreamIndex = audioStreamIndex; + getPlayerData(player).maxStreamingBitrate = maxBitrate; + + changeStreamToUrl(apiClient, player, playSessionId, streamInfo); + } + }); + }); + } + + function changeStreamToUrl(apiClient, player, playSessionId, streamInfo, newPositionTicks) { + + var playerData = getPlayerData(player); + + playerData.isChangingStream = true; + + if (playerData.streamInfo && playSessionId) { + + apiClient.stopActiveEncodings(playSessionId).then(function () { + + // Stop the first transcoding afterwards because the player may still send requests to the original url + var afterSetSrc = function () { + + apiClient.stopActiveEncodings(playSessionId); + }; + setSrcIntoPlayer(apiClient, player, streamInfo).then(afterSetSrc, afterSetSrc); + }); + + } else { + setSrcIntoPlayer(apiClient, player, streamInfo); + } + } + + function setSrcIntoPlayer(apiClient, player, streamInfo) { + + return player.play(streamInfo).then(function () { + + var playerData = getPlayerData(player); + + playerData.isChangingStream = false; + playerData.streamInfo = streamInfo; + streamInfo.started = true; + streamInfo.ended = false; + + sendProgressUpdate(player, 'timeupdate'); + }, function (e) { + + var playerData = getPlayerData(player); + playerData.isChangingStream = false; + + onPlaybackError.call(player, e, { + type: 'mediadecodeerror', + streamInfo: streamInfo + }); + }); + } + + function translateItemsForPlayback(items, options) { + + var firstItem = items[0]; + var promise; + + var serverId = firstItem.ServerId; + + var queryOptions = options.queryOptions || {}; + + if (firstItem.Type === "Program") { + + promise = getItemsForPlayback(serverId, { + Ids: firstItem.ChannelId, + }); + } + else if (firstItem.Type === "Playlist") { + + promise = getItemsForPlayback(serverId, { + ParentId: firstItem.Id, + SortBy: options.shuffle ? 'Random' : null + }); + } + else if (firstItem.Type === "MusicArtist") { + + promise = getItemsForPlayback(serverId, { + ArtistIds: firstItem.Id, + Filters: "IsNotFolder", + Recursive: true, + SortBy: options.shuffle ? 'Random' : 'SortName', + MediaTypes: "Audio" + }); + + } + else if (firstItem.MediaType === "Photo") { + + promise = getItemsForPlayback(serverId, { + ParentId: firstItem.ParentId, + Filters: "IsNotFolder", + // Setting this to true may cause some incorrect sorting + Recursive: false, + SortBy: options.shuffle ? 'Random' : 'SortName', + MediaTypes: "Photo,Video", + Limit: 500 + + }).then(function (result) { + + var items = result.Items; + + var index = items.map(function (i) { + return i.Id; + + }).indexOf(firstItem.Id); + + if (index === -1) { + index = 0; + } + + options.startIndex = index; + + return Promise.resolve(result); + + }); + } + else if (firstItem.Type === "PhotoAlbum") { + + promise = getItemsForPlayback(serverId, { + ParentId: firstItem.Id, + Filters: "IsNotFolder", + // Setting this to true may cause some incorrect sorting + Recursive: false, + SortBy: options.shuffle ? 'Random' : 'SortName', + MediaTypes: "Photo,Video", + Limit: 1000 + + }); + } + else if (firstItem.Type === "MusicGenre") { + + promise = getItemsForPlayback(serverId, { + GenreIds: firstItem.Id, + Filters: "IsNotFolder", + Recursive: true, + SortBy: options.shuffle ? 'Random' : 'SortName', + MediaTypes: "Audio" + }); + } + else if (firstItem.IsFolder) { + + promise = getItemsForPlayback(serverId, mergePlaybackQueries({ + + ParentId: firstItem.Id, + Filters: "IsNotFolder", + Recursive: true, + // These are pre-sorted + SortBy: options.shuffle ? 'Random' : (['BoxSet'].indexOf(firstItem.Type) === -1 ? 'SortName' : null), + MediaTypes: "Audio,Video" + + }, queryOptions)); + } + else if (firstItem.Type === "Episode" && items.length === 1 && getPlayer(firstItem, options).supportsProgress !== false) { + + promise = new Promise(function (resolve, reject) { + var apiClient = connectionManager.getApiClient(firstItem.ServerId); + + apiClient.getCurrentUser().then(function (user) { + + if (!user.Configuration.EnableNextEpisodeAutoPlay || !firstItem.SeriesId) { + resolve(null); + return; + } + + apiClient.getEpisodes(firstItem.SeriesId, { + IsVirtualUnaired: false, + IsMissing: false, + UserId: apiClient.getCurrentUserId(), + Fields: "Chapters" + + }).then(function (episodesResult) { + + var foundItem = false; + episodesResult.Items = episodesResult.Items.filter(function (e) { + + if (foundItem) { + return true; + } + if (e.Id === firstItem.Id) { + foundItem = true; + return true; + } + + return false; + }); + episodesResult.TotalRecordCount = episodesResult.Items.length; + resolve(episodesResult); + }, reject); + }); + }); + } + + if (promise) { + return promise.then(function (result) { + + return result ? result.Items : items; + }); + } else { + return Promise.resolve(items); + } + } + + self.play = function (options) { + + normalizePlayOptions(options); + + if (self._currentPlayer) { + if (options.enableRemotePlayers === false && !self._currentPlayer.isLocalPlayer) { + return Promise.reject(); + } + + if (!self._currentPlayer.isLocalPlayer) { + return self._currentPlayer.play(options); + } + } + + if (options.fullscreen) { + loading.show(); + } + + if (options.items) { + + return translateItemsForPlayback(options.items, options).then(function (items) { + + return playWithIntros(items, options); + }); + + } else { + + if (!options.serverId) { + throw new Error('serverId required!'); + } + + return getItemsForPlayback(options.serverId, { + + Ids: options.ids.join(',') + + }).then(function (result) { + + return translateItemsForPlayback(result.Items, options).then(function (items) { + + return playWithIntros(items, options); + }); + + }); + } + }; + + function getPlayerData(player) { + + if (!player) { + throw new Error('player cannot be null'); + } + if (!player.name) { + throw new Error('player name cannot be null'); + } + var state = playerStates[player.name]; + + if (!state) { + playerStates[player.name] = {}; + state = playerStates[player.name]; + } + + return player; + } + + self.getPlayerState = function (player, item, mediaSource) { + + player = player || self._currentPlayer; + + if (!player) { + throw new Error('player cannot be null'); + } + + if (!enableLocalPlaylistManagement(player) && player.getPlayerState) { + return player.getPlayerState(); + } + + item = item || self.currentItem(player); + mediaSource = mediaSource || self.currentMediaSource(player); + var state = { PlayState: {} }; - return player && (state.PlayState.VolumeLevel = player.getVolume(), state.PlayState.IsMuted = player.isMuted(), state.PlayState.IsPaused = player.paused(), state.PlayState.RepeatMode = self.getRepeatMode(player), state.PlayState.MaxStreamingBitrate = self.getMaxStreamingBitrate(player), state.PlayState.PositionTicks = getCurrentTicks(player), state.PlayState.PlaybackStartTimeTicks = self.playbackStartTime(player), state.PlayState.SubtitleStreamIndex = self.getSubtitleStreamIndex(player), state.PlayState.AudioStreamIndex = self.getAudioStreamIndex(player), state.PlayState.BufferedRanges = self.getBufferedRanges(player), state.PlayState.PlayMethod = self.playMethod(player), mediaSource && (state.PlayState.LiveStreamId = mediaSource.LiveStreamId), state.PlayState.PlaySessionId = self.playSessionId(player), state.PlayState.PlaylistItemId = self.getCurrentPlaylistItemId(player)), mediaSource && (state.PlayState.MediaSourceId = mediaSource.Id, state.NowPlayingItem = { - RunTimeTicks: mediaSource.RunTimeTicks - }, state.PlayState.CanSeek = (mediaSource.RunTimeTicks || 0) > 0 || canPlayerSeek(player)), item && (state.NowPlayingItem = getNowPlayingItemForReporting(player, item, mediaSource)), state.MediaSource = mediaSource, state - }, self.duration = function(player) { - if ((player = player || self._currentPlayer) && !enableLocalPlaylistManagement(player) && !player.isLocalPlayer) return player.duration(); - if (!player) throw new Error("player cannot be null"); + + if (player) { + + state.PlayState.VolumeLevel = player.getVolume(); + state.PlayState.IsMuted = player.isMuted(); + state.PlayState.IsPaused = player.paused(); + state.PlayState.RepeatMode = self.getRepeatMode(player); + state.PlayState.MaxStreamingBitrate = self.getMaxStreamingBitrate(player); + + state.PlayState.PositionTicks = getCurrentTicks(player); + state.PlayState.PlaybackStartTimeTicks = self.playbackStartTime(player); + + state.PlayState.SubtitleStreamIndex = self.getSubtitleStreamIndex(player); + state.PlayState.AudioStreamIndex = self.getAudioStreamIndex(player); + state.PlayState.BufferedRanges = self.getBufferedRanges(player); + + state.PlayState.PlayMethod = self.playMethod(player); + + if (mediaSource) { + state.PlayState.LiveStreamId = mediaSource.LiveStreamId; + } + state.PlayState.PlaySessionId = self.playSessionId(player); + state.PlayState.PlaylistItemId = self.getCurrentPlaylistItemId(player); + } + + if (mediaSource) { + + state.PlayState.MediaSourceId = mediaSource.Id; + + state.NowPlayingItem = { + RunTimeTicks: mediaSource.RunTimeTicks + }; + + state.PlayState.CanSeek = (mediaSource.RunTimeTicks || 0) > 0 || canPlayerSeek(player); + } + + if (item) { + + state.NowPlayingItem = getNowPlayingItemForReporting(player, item, mediaSource); + } + + state.MediaSource = mediaSource; + + return state; + }; + + self.duration = function (player) { + + player = player || self._currentPlayer; + + if (player && !enableLocalPlaylistManagement(player) && !player.isLocalPlayer) { + return player.duration(); + } + + if (!player) { + throw new Error('player cannot be null'); + } + var mediaSource = self.currentMediaSource(player); - if (mediaSource && mediaSource.RunTimeTicks) return mediaSource.RunTimeTicks; + + if (mediaSource && mediaSource.RunTimeTicks) { + return mediaSource.RunTimeTicks; + } + var playerDuration = player.duration(); - return playerDuration && (playerDuration *= 1e4), playerDuration - }, self.getCurrentTicks = getCurrentTicks, self.getPlaybackInfo = function(item, options) { + + if (playerDuration) { + playerDuration *= 10000; + } + + return playerDuration; + }; + + function getCurrentTicks(player) { + + if (!player) { + throw new Error('player cannot be null'); + } + + var playerTime = Math.floor(10000 * (player || self._currentPlayer).currentTime()); + + var streamInfo = getPlayerData(player).streamInfo; + if (streamInfo) { + playerTime += getPlayerData(player).streamInfo.transcodingOffsetTicks || 0; + } + + return playerTime; + } + + // Only used internally + self.getCurrentTicks = getCurrentTicks; + + function playPhotos(items, options, user) { + + var playStartIndex = options.startIndex || 0; + var player = getPlayer(items[playStartIndex], options); + + loading.hide(); + + options.items = items; + + return player.play(options); + } + + function playWithIntros(items, options, user) { + + var playStartIndex = options.startIndex || 0; + var firstItem = items[playStartIndex]; + + // If index was bad, reset it + if (!firstItem) { + playStartIndex = 0; + firstItem = items[playStartIndex]; + } + + // If it's still null then there's nothing to play + if (!firstItem) { + showPlaybackInfoErrorMessage(self, 'NoCompatibleStream', false); + return Promise.reject(); + } + + if (firstItem.MediaType === "Photo") { + + return playPhotos(items, options, user); + } + + var apiClient = connectionManager.getApiClient(firstItem.ServerId); + + return getIntros(firstItem, apiClient, options).then(function (introsResult) { + + var introItems = introsResult.Items; + var introPlayOptions; + + firstItem.playOptions = truncatePlayOptions(options); + + if (introItems.length) { + + introPlayOptions = { + fullscreen: firstItem.playOptions.fullscreen + }; + + } else { + introPlayOptions = firstItem.playOptions; + } + + items = introItems.concat(items); + + // Needed by players that manage their own playlist + introPlayOptions.items = items; + introPlayOptions.startIndex = playStartIndex; + + return playInternal(items[playStartIndex], introPlayOptions, function () { + + self._playQueueManager.setPlaylist(items); + + setPlaylistState(items[playStartIndex].PlaylistItemId, playStartIndex); + loading.hide(); + }); + }); + } + + // Set playlist state. Using a method allows for overloading in derived player implementations + function setPlaylistState(playlistItemId, index) { + + if (!isNaN(index)) { + self._playQueueManager.setPlaylistState(playlistItemId, index); + } + } + + function playInternal(item, playOptions, onPlaybackStartedFn) { + + if (item.IsPlaceHolder) { + loading.hide(); + showPlaybackInfoErrorMessage(self, 'PlaceHolder', true); + return Promise.reject(); + } + + // Normalize defaults to simplfy checks throughout the process + normalizePlayOptions(playOptions); + + if (playOptions.isFirstItem) { + playOptions.isFirstItem = false; + } else { + playOptions.isFirstItem = true; + } + + return runInterceptors(item, playOptions).then(function () { + + if (playOptions.fullscreen) { + loading.show(); + } + + // TODO: This should be the media type requested, not the original media type + var mediaType = item.MediaType; + + var onBitrateDetectionFailure = function () { + return playAfterBitrateDetect(getSavedMaxStreamingBitrate(connectionManager.getApiClient(item.ServerId), mediaType), item, playOptions, onPlaybackStartedFn); + }; + + if (!isServerItem(item) || itemHelper.isLocalItem(item)) { + return onBitrateDetectionFailure(); + } + + var apiClient = connectionManager.getApiClient(item.ServerId); + apiClient.getEndpointInfo().then(function (endpointInfo) { + + if ((mediaType === 'Video' || mediaType === 'Audio') && appSettings.enableAutomaticBitrateDetection(endpointInfo.IsInNetwork, mediaType)) { + + return apiClient.detectBitrate().then(function (bitrate) { + + appSettings.maxStreamingBitrate(endpointInfo.IsInNetwork, mediaType, bitrate); + + return playAfterBitrateDetect(bitrate, item, playOptions, onPlaybackStartedFn); + + }, onBitrateDetectionFailure); + + } else { + + onBitrateDetectionFailure(); + } + + }, onBitrateDetectionFailure); + + }, onInterceptorRejection); + } + + function onInterceptorRejection() { + var player = self._currentPlayer; + + if (player) { + destroyPlayer(player); + removeCurrentPlayer(player); + } + + events.trigger(self, 'playbackcancelled'); + + return Promise.reject(); + } + + function destroyPlayer(player) { + player.destroy(); + } + + function runInterceptors(item, playOptions) { + + return new Promise(function (resolve, reject) { + + var interceptors = pluginManager.ofType('preplayintercept'); + + interceptors.sort(function (a, b) { + return (a.order || 0) - (b.order || 0); + }); + + if (!interceptors.length) { + resolve(); + return; + } + + loading.hide(); + + var options = Object.assign({}, playOptions); + + options.mediaType = item.MediaType; + options.item = item; + + runNextPrePlay(interceptors, 0, options, resolve, reject); + }); + } + + function runNextPrePlay(interceptors, index, options, resolve, reject) { + + if (index >= interceptors.length) { + resolve(); + return; + } + + var interceptor = interceptors[index]; + + interceptor.intercept(options).then(function () { + + runNextPrePlay(interceptors, index + 1, options, resolve, reject); + + }, reject); + } + + function sendPlaybackListToPlayer(player, items, deviceProfile, maxBitrate, apiClient, startPositionTicks, mediaSourceId, audioStreamIndex, subtitleStreamIndex, startIndex) { + + return setStreamUrls(items, deviceProfile, maxBitrate, apiClient, startPositionTicks).then(function () { + + loading.hide(); + + return player.play({ + items: items, + startPositionTicks: startPositionTicks || 0, + mediaSourceId: mediaSourceId, + audioStreamIndex: audioStreamIndex, + subtitleStreamIndex: subtitleStreamIndex, + startIndex: startIndex + }); + }); + } + + function playAfterBitrateDetect(maxBitrate, item, playOptions, onPlaybackStartedFn) { + + var startPosition = playOptions.startPositionTicks; + + var player = getPlayer(item, playOptions); + var activePlayer = self._currentPlayer; + + var promise; + + if (activePlayer) { + + // TODO: if changing players within the same playlist, this will cause nextItem to be null + self._playNextAfterEnded = false; + promise = onPlaybackChanging(activePlayer, player, item); + } else { + promise = Promise.resolve(); + } + + if (!isServerItem(item) || item.MediaType === 'Game' || item.MediaType === 'Book') { + return promise.then(function () { + var streamInfo = createStreamInfoFromUrlItem(item); + streamInfo.fullscreen = playOptions.fullscreen; + getPlayerData(player).isChangingStream = false; + return player.play(streamInfo).then(function () { + loading.hide(); + onPlaybackStartedFn(); + onPlaybackStarted(player, playOptions, streamInfo); + }, function () { + // TODO: show error message + self.stop(player); + }); + }); + } + + return Promise.all([promise, player.getDeviceProfile(item)]).then(function (responses) { + + var deviceProfile = responses[1]; + + var apiClient = connectionManager.getApiClient(item.ServerId); + + var mediaSourceId = playOptions.mediaSourceId; + var audioStreamIndex = playOptions.audioStreamIndex; + var subtitleStreamIndex = playOptions.subtitleStreamIndex; + + if (player && !enableLocalPlaylistManagement(player)) { + + return sendPlaybackListToPlayer(player, playOptions.items, deviceProfile, maxBitrate, apiClient, startPosition, mediaSourceId, audioStreamIndex, subtitleStreamIndex, playOptions.startIndex); + } + + // this reference was only needed by sendPlaybackListToPlayer + playOptions.items = null; + + return getPlaybackMediaSource(player, apiClient, deviceProfile, maxBitrate, item, startPosition, mediaSourceId, audioStreamIndex, subtitleStreamIndex).then(function (mediaSource) { + + var streamInfo = createStreamInfo(apiClient, item.MediaType, item, mediaSource, startPosition); + + streamInfo.fullscreen = playOptions.fullscreen; + + getPlayerData(player).isChangingStream = false; + getPlayerData(player).maxStreamingBitrate = maxBitrate; + + return player.play(streamInfo).then(function () { + loading.hide(); + onPlaybackStartedFn(); + onPlaybackStarted(player, playOptions, streamInfo, mediaSource); + }, function (err) { + + // TODO: Improve this because it will report playback start on a failure + onPlaybackStartedFn(); + onPlaybackStarted(player, playOptions, streamInfo, mediaSource); + setTimeout(function () { + onPlaybackError.call(player, err, { + type: 'mediadecodeerror', + streamInfo: streamInfo + }); + }, 100); + }); + }); + }); + } + + self.getPlaybackInfo = function (item, options) { + options = options || {}; - var startPosition = options.startPositionTicks || 0, - mediaType = options.mediaType || item.MediaType, - player = getPlayer(item, options), - apiClient = connectionManager.getApiClient(item.ServerId); - return apiClient.getEndpointInfo().then(function() { + var startPosition = options.startPositionTicks || 0; + var mediaType = options.mediaType || item.MediaType; + var player = getPlayer(item, options); + var apiClient = connectionManager.getApiClient(item.ServerId); + + // Call this just to ensure the value is recorded, it is needed with getSavedMaxStreamingBitrate + return apiClient.getEndpointInfo().then(function () { + var maxBitrate = getSavedMaxStreamingBitrate(connectionManager.getApiClient(item.ServerId), mediaType); - return player.getDeviceProfile(item).then(function(deviceProfile) { - return getPlaybackMediaSource(player, apiClient, deviceProfile, maxBitrate, item, startPosition, options.mediaSourceId, options.audioStreamIndex, options.subtitleStreamIndex).then(function(mediaSource) { - return createStreamInfo(apiClient, item.MediaType, item, mediaSource, startPosition) - }) - }) - }) - }, self.getPlaybackMediaSources = function(item, options) { + + return player.getDeviceProfile(item).then(function (deviceProfile) { + + return getPlaybackMediaSource(player, apiClient, deviceProfile, maxBitrate, item, startPosition, options.mediaSourceId, options.audioStreamIndex, options.subtitleStreamIndex).then(function (mediaSource) { + + return createStreamInfo(apiClient, item.MediaType, item, mediaSource, startPosition); + }); + }); + }); + }; + + self.getPlaybackMediaSources = function (item, options) { + options = options || {}; - var startPosition = options.startPositionTicks || 0, - mediaType = options.mediaType || item.MediaType, - player = getPlayer(item, options, !0), - apiClient = connectionManager.getApiClient(item.ServerId); - return apiClient.getEndpointInfo().then(function() { + var startPosition = options.startPositionTicks || 0; + var mediaType = options.mediaType || item.MediaType; + // TODO: Remove the true forceLocalPlayer hack + var player = getPlayer(item, options, true); + var apiClient = connectionManager.getApiClient(item.ServerId); + + // Call this just to ensure the value is recorded, it is needed with getSavedMaxStreamingBitrate + return apiClient.getEndpointInfo().then(function () { + var maxBitrate = getSavedMaxStreamingBitrate(connectionManager.getApiClient(item.ServerId), mediaType); - return player.getDeviceProfile(item).then(function(deviceProfile) { - return getPlaybackInfo(player, apiClient, item, deviceProfile, maxBitrate, startPosition, !1, null, null, null, null).then(function(playbackInfoResult) { - return playbackInfoResult.MediaSources - }) - }) - }) - }, self.setCurrentPlaylistItem = function(playlistItemId, player) { - if ((player = player || self._currentPlayer) && !enableLocalPlaylistManagement(player)) return player.setCurrentPlaylistItem(playlistItemId); - for (var newItem, newItemIndex, playlist = self._playQueueManager.getPlaylist(), i = 0, length = playlist.length; i < length; i++) + + return player.getDeviceProfile(item).then(function (deviceProfile) { + + return getPlaybackInfo(player, apiClient, item, deviceProfile, maxBitrate, startPosition, false, null, null, null, null).then(function (playbackInfoResult) { + + return playbackInfoResult.MediaSources; + }); + }); + }); + + }; + + function createStreamInfo(apiClient, type, item, mediaSource, startPosition) { + + var mediaUrl; + var contentType; + var transcodingOffsetTicks = 0; + var playerStartPositionTicks = startPosition; + var liveStreamId = mediaSource.LiveStreamId; + + var playMethod = 'Transcode'; + + var mediaSourceContainer = (mediaSource.Container || '').toLowerCase(); + var directOptions; + + if (type === 'Video' || type === 'Audio') { + + contentType = getMimeType(type.toLowerCase(), mediaSourceContainer); + + if (mediaSource.enableDirectPlay) { + mediaUrl = mediaSource.Path; + + playMethod = 'DirectPlay'; + + } + + else if (mediaSource.StreamUrl) { + + // Only used for audio + playMethod = 'Transcode'; + mediaUrl = mediaSource.StreamUrl; + } + + else if (mediaSource.SupportsDirectStream) { + + directOptions = { + Static: true, + mediaSourceId: mediaSource.Id, + deviceId: apiClient.deviceId(), + api_key: apiClient.accessToken() + }; + + if (mediaSource.ETag) { + directOptions.Tag = mediaSource.ETag; + } + + if (mediaSource.LiveStreamId) { + directOptions.LiveStreamId = mediaSource.LiveStreamId; + } + + var prefix = type === 'Video' ? 'Videos' : 'Audio'; + mediaUrl = apiClient.getUrl(prefix + '/' + item.Id + '/stream.' + mediaSourceContainer, directOptions); + + playMethod = 'DirectStream'; + + } else if (mediaSource.SupportsTranscoding) { + + mediaUrl = apiClient.getUrl(mediaSource.TranscodingUrl); + + if (mediaSource.TranscodingSubProtocol === 'hls') { + + contentType = 'application/x-mpegURL'; + + } else { + + playerStartPositionTicks = null; + contentType = getMimeType(type.toLowerCase(), mediaSource.TranscodingContainer); + + if (mediaUrl.toLowerCase().indexOf('copytimestamps=true') === -1) { + transcodingOffsetTicks = startPosition || 0; + } + } + } + + } else { + + // All other media types + mediaUrl = mediaSource.Path; + playMethod = 'DirectPlay'; + } + + // Fallback (used for offline items) + if (!mediaUrl && mediaSource.SupportsDirectPlay) { + mediaUrl = mediaSource.Path; + playMethod = 'DirectPlay'; + } + + var resultInfo = { + url: mediaUrl, + mimeType: contentType, + transcodingOffsetTicks: transcodingOffsetTicks, + playMethod: playMethod, + playerStartPositionTicks: playerStartPositionTicks, + item: item, + mediaSource: mediaSource, + textTracks: getTextTracks(apiClient, item, mediaSource), + // TODO: Deprecate + tracks: getTextTracks(apiClient, item, mediaSource), + mediaType: type, + liveStreamId: liveStreamId, + playSessionId: getParam('playSessionId', mediaUrl), + title: item.Name + }; + + var backdropUrl = backdropImageUrl(apiClient, item, {}); + if (backdropUrl) { + resultInfo.backdropUrl = backdropUrl; + } + + return resultInfo; + } + + function getTextTracks(apiClient, item, mediaSource) { + + var subtitleStreams = mediaSource.MediaStreams.filter(function (s) { + return s.Type === 'Subtitle'; + }); + + var textStreams = subtitleStreams.filter(function (s) { + return s.DeliveryMethod === 'External'; + }); + + var tracks = []; + + for (var i = 0, length = textStreams.length; i < length; i++) { + + var textStream = textStreams[i]; + var textStreamUrl; + + if (itemHelper.isLocalItem(item)) { + textStreamUrl = textStream.Path; + } else { + textStreamUrl = !textStream.IsExternalUrl ? apiClient.getUrl(textStream.DeliveryUrl) : textStream.DeliveryUrl; + } + + tracks.push({ + url: textStreamUrl, + language: (textStream.Language || 'und'), + isDefault: textStream.Index === mediaSource.DefaultSubtitleStreamIndex, + index: textStream.Index, + format: textStream.Codec + }); + } + + return tracks; + } + + function getPlaybackMediaSource(player, apiClient, deviceProfile, maxBitrate, item, startPosition, mediaSourceId, audioStreamIndex, subtitleStreamIndex) { + + return getPlaybackInfo(player, apiClient, item, deviceProfile, maxBitrate, startPosition, true, mediaSourceId, audioStreamIndex, subtitleStreamIndex, null).then(function (playbackInfoResult) { + + if (validatePlaybackInfoResult(self, playbackInfoResult)) { + + return getOptimalMediaSource(apiClient, item, playbackInfoResult.MediaSources).then(function (mediaSource) { + if (mediaSource) { + + if (mediaSource.RequiresOpening && !mediaSource.LiveStreamId) { + + return getLiveStream(player, apiClient, item, playbackInfoResult.PlaySessionId, deviceProfile, maxBitrate, startPosition, mediaSource, null, null).then(function (openLiveStreamResult) { + + return supportsDirectPlay(apiClient, item, openLiveStreamResult.MediaSource).then(function (result) { + + openLiveStreamResult.MediaSource.enableDirectPlay = result; + return openLiveStreamResult.MediaSource; + }); + + }); + + } else { + return mediaSource; + } + } else { + showPlaybackInfoErrorMessage(self, 'NoCompatibleStream'); + return Promise.reject(); + } + }); + } else { + return Promise.reject(); + } + }); + } + + function getPlayer(item, playOptions, forceLocalPlayers) { + + var serverItem = isServerItem(item); + return getAutomaticPlayers(self, forceLocalPlayers).filter(function (p) { + + if (p.canPlayMediaType(item.MediaType)) { + + if (serverItem) { + if (p.canPlayItem) { + return p.canPlayItem(item, playOptions); + } + return true; + } + + else if (item.Url && p.canPlayUrl) { + return p.canPlayUrl(item.Url); + } + } + + return false; + + })[0]; + } + + self.setCurrentPlaylistItem = function (playlistItemId, player) { + + player = player || self._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + return player.setCurrentPlaylistItem(playlistItemId); + } + + var newItem; + var newItemIndex; + var playlist = self._playQueueManager.getPlaylist(); + + for (var i = 0, length = playlist.length; i < length; i++) { if (playlist[i].PlaylistItemId === playlistItemId) { - newItem = playlist[i], newItemIndex = i; - break - } if (newItem) { + newItem = playlist[i]; + newItemIndex = i; + break; + } + } + + if (newItem) { + var newItemPlayOptions = newItem.playOptions || {}; - playInternal(newItem, newItemPlayOptions, function() { - setPlaylistState(newItem.PlaylistItemId, newItemIndex) - }) + + playInternal(newItem, newItemPlayOptions, function () { + setPlaylistState(newItem.PlaylistItemId, newItemIndex); + }); } - }, self.removeFromPlaylist = function(playlistItemIds, player) { - if (!playlistItemIds) throw new Error("Invalid playlistItemIds"); - if ((player = player || self._currentPlayer) && !enableLocalPlaylistManagement(player)) return player.removeFromPlaylist(playlistItemIds); + }; + + self.removeFromPlaylist = function (playlistItemIds, player) { + + if (!playlistItemIds) { + throw new Error('Invalid playlistItemIds'); + } + + player = player || self._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + return player.removeFromPlaylist(playlistItemIds); + } + var removeResult = self._playQueueManager.removeFromPlaylist(playlistItemIds); - if ("empty" === removeResult.result) return self.stop(player); - var isCurrentIndex = removeResult.isCurrentIndex; - return events.trigger(player, "playlistitemremove", [{ - playlistItemIds: playlistItemIds - }]), isCurrentIndex ? self.setCurrentPlaylistItem(self._playQueueManager.getPlaylist()[0].PlaylistItemId, player) : Promise.resolve() - }, self.movePlaylistItem = function(playlistItemId, newIndex, player) { - if ((player = player || self._currentPlayer) && !enableLocalPlaylistManagement(player)) return player.movePlaylistItem(playlistItemId, newIndex); - var moveResult = self._playQueueManager.movePlaylistItem(playlistItemId, newIndex); - "noop" !== moveResult.result && events.trigger(player, "playlistitemmove", [{ - playlistItemId: moveResult.playlistItemId, - newIndex: moveResult.newIndex - }]) - }, self.getCurrentPlaylistIndex = function(player) { - return player = player || self._currentPlayer, player && !enableLocalPlaylistManagement(player) ? player.getCurrentPlaylistIndex() : self._playQueueManager.getCurrentPlaylistIndex() - }, self.getCurrentPlaylistItemId = function(player) { - return player = player || self._currentPlayer, player && !enableLocalPlaylistManagement(player) ? player.getCurrentPlaylistItemId() : self._playQueueManager.getCurrentPlaylistItemId() - }, self.channelUp = function(player) { - return player = player || self._currentPlayer, self.nextTrack(player) - }, self.channelDown = function(player) { - return player = player || self._currentPlayer, self.previousTrack(player) - }, self.nextTrack = function(player) { - if ((player = player || self._currentPlayer) && !enableLocalPlaylistManagement(player)) return player.nextTrack(); - var newItemInfo = self._playQueueManager.getNextItemInfo(); - if (newItemInfo) { - console.log("playing next track"); - var newItemPlayOptions = newItemInfo.item.playOptions || {}; - playInternal(newItemInfo.item, newItemPlayOptions, function() { - setPlaylistState(newItemInfo.item.PlaylistItemId, newItemInfo.index) - }) + + if (removeResult.result === 'empty') { + return self.stop(player); } - }, self.previousTrack = function(player) { - if ((player = player || self._currentPlayer) && !enableLocalPlaylistManagement(player)) return player.previousTrack(); + + var isCurrentIndex = removeResult.isCurrentIndex; + + events.trigger(player, 'playlistitemremove', [ + { + playlistItemIds: playlistItemIds + }]); + + if (isCurrentIndex) { + + return self.setCurrentPlaylistItem(self._playQueueManager.getPlaylist()[0].PlaylistItemId, player); + } + + return Promise.resolve(); + }; + + self.movePlaylistItem = function (playlistItemId, newIndex, player) { + + player = player || self._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + return player.movePlaylistItem(playlistItemId, newIndex); + } + + var moveResult = self._playQueueManager.movePlaylistItem(playlistItemId, newIndex); + + if (moveResult.result === 'noop') { + return; + } + + events.trigger(player, 'playlistitemmove', [ + { + playlistItemId: moveResult.playlistItemId, + newIndex: moveResult.newIndex + }]); + }; + + self.getCurrentPlaylistIndex = function (player) { + + player = player || self._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + return player.getCurrentPlaylistIndex(); + } + + return self._playQueueManager.getCurrentPlaylistIndex(); + }; + + self.getCurrentPlaylistItemId = function (player) { + + player = player || self._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + return player.getCurrentPlaylistItemId(); + } + + return self._playQueueManager.getCurrentPlaylistItemId(); + }; + + self.channelUp = function (player) { + + player = player || self._currentPlayer; + return self.nextTrack(player); + }; + + self.channelDown = function (player) { + + player = player || self._currentPlayer; + return self.previousTrack(player); + }; + + self.nextTrack = function (player) { + + player = player || self._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + return player.nextTrack(); + } + + var newItemInfo = self._playQueueManager.getNextItemInfo(); + + if (newItemInfo) { + + console.log('playing next track'); + + var newItemPlayOptions = newItemInfo.item.playOptions || {}; + + playInternal(newItemInfo.item, newItemPlayOptions, function () { + setPlaylistState(newItemInfo.item.PlaylistItemId, newItemInfo.index); + }); + } + }; + + self.previousTrack = function (player) { + + player = player || self._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + return player.previousTrack(); + } + var newIndex = self.getCurrentPlaylistIndex(player) - 1; if (newIndex >= 0) { - var playlist = self._playQueueManager.getPlaylist(), - newItem = playlist[newIndex]; + + var playlist = self._playQueueManager.getPlaylist(); + var newItem = playlist[newIndex]; + if (newItem) { + var newItemPlayOptions = newItem.playOptions || {}; - newItemPlayOptions.startPositionTicks = 0, playInternal(newItem, newItemPlayOptions, function() { - setPlaylistState(newItem.PlaylistItemId, newIndex) - }) + newItemPlayOptions.startPositionTicks = 0; + + playInternal(newItem, newItemPlayOptions, function () { + setPlaylistState(newItem.PlaylistItemId, newIndex); + }); } } - }, self.queue = function(options, player) { - queue(options, "", player) - }, self.queueNext = function(options, player) { - queue(options, "next", player) - }, events.on(pluginManager, "registered", function(e, plugin) { - "mediaplayer" === plugin.type && initMediaPlayer(plugin) - }), pluginManager.ofType("mediaplayer").map(initMediaPlayer), self.onAppClose = function() { - var player = this._currentPlayer; - player && this.isPlaying(player) && (this._playNextAfterEnded = !1, onPlaybackStopped.call(player)) - }, self.playbackStartTime = function(player) { - if ((player = player || this._currentPlayer) && !enableLocalPlaylistManagement(player) && !player.isLocalPlayer) return player.playbackStartTime(); - var streamInfo = getPlayerData(player).streamInfo; - return streamInfo ? streamInfo.playbackStartTimeTicks : null - }, apphost.supports("remotecontrol") && require(["serverNotifications"], function(serverNotifications) { - events.on(serverNotifications, "ServerShuttingDown", self.setDefaultPlayerActive.bind(self)), events.on(serverNotifications, "ServerRestarting", self.setDefaultPlayerActive.bind(self)) - }) - } - var startingPlaySession = (new Date).getTime(); - return PlaybackManager.prototype.getCurrentPlayer = function() { - return this._currentPlayer - }, PlaybackManager.prototype.currentTime = function(player) { - return player = player || this._currentPlayer, !player || enableLocalPlaylistManagement(player) || player.isLocalPlayer ? this.getCurrentTicks(player) : player.currentTime() - }, PlaybackManager.prototype.nextItem = function(player) { - if ((player = player || this._currentPlayer) && !enableLocalPlaylistManagement(player)) return player.nextItem(); - var nextItem = this._playQueueManager.getNextItemInfo(); - if (!nextItem || !nextItem.item) return Promise.reject(); - var apiClient = connectionManager.getApiClient(nextItem.item.ServerId); - return apiClient.getItem(apiClient.getCurrentUserId(), nextItem.item.Id) - }, PlaybackManager.prototype.canQueue = function(item) { - return "MusicAlbum" === item.Type || "MusicArtist" === item.Type || "MusicGenre" === item.Type ? this.canQueueMediaType("Audio") : this.canQueueMediaType(item.MediaType) - }, PlaybackManager.prototype.canQueueMediaType = function(mediaType) { - return !!this._currentPlayer && this._currentPlayer.canPlayMediaType(mediaType) - }, PlaybackManager.prototype.isMuted = function(player) { - return !!(player = player || this._currentPlayer) && player.isMuted() - }, PlaybackManager.prototype.setMute = function(mute, player) { - (player = player || this._currentPlayer) && player.setMute(mute) - }, PlaybackManager.prototype.toggleMute = function(mute, player) { - (player = player || this._currentPlayer) && (player.toggleMute ? player.toggleMute() : player.setMute(!player.isMuted())) - }, PlaybackManager.prototype.toggleDisplayMirroring = function() { - this.enableDisplayMirroring(!this.enableDisplayMirroring()) - }, PlaybackManager.prototype.enableDisplayMirroring = function(enabled) { - if (null != enabled) { - var val = enabled ? "1" : "0"; - return void appSettings.set("displaymirror", val) - } - return "0" !== (appSettings.get("displaymirror") || "") - }, PlaybackManager.prototype.nextChapter = function(player) { - player = player || this._currentPlayer; - var item = this.currentItem(player), - ticks = this.getCurrentTicks(player), - nextChapter = (item.Chapters || []).filter(function(i) { - return i.StartPositionTicks > ticks - })[0]; - nextChapter ? this.seek(nextChapter.StartPositionTicks, player) : this.nextTrack(player) - }, PlaybackManager.prototype.previousChapter = function(player) { - player = player || this._currentPlayer; - var item = this.currentItem(player), - ticks = this.getCurrentTicks(player); - ticks -= 1e8, 0 === this.getCurrentPlaylistIndex(player) && (ticks = Math.max(ticks, 0)); - var previousChapters = (item.Chapters || []).filter(function(i) { - return i.StartPositionTicks <= ticks - }); - previousChapters.length ? this.seek(previousChapters[previousChapters.length - 1].StartPositionTicks, player) : this.previousTrack(player) - }, PlaybackManager.prototype.fastForward = function(player) { - if (player = player || this._currentPlayer, null != player.fastForward) return void player.fastForward(userSettings.skipForwardLength()); - var offsetTicks = 1e4 * userSettings.skipForwardLength(); - this.seekRelative(offsetTicks, player) - }, PlaybackManager.prototype.rewind = function(player) { - if (player = player || this._currentPlayer, null != player.rewind) return void player.rewind(userSettings.skipBackLength()); - var offsetTicks = 0 - 1e4 * userSettings.skipBackLength(); - this.seekRelative(offsetTicks, player) - }, PlaybackManager.prototype.seekPercent = function(percent, player) { - player = player || this._currentPlayer; - var ticks = this.duration(player) || 0; - percent /= 100, ticks *= percent, this.seek(parseInt(ticks), player) - }, PlaybackManager.prototype.playTrailers = function(item) { - var player = this._currentPlayer; - if (player && player.playTrailers) return player.playTrailers(item); - var apiClient = connectionManager.getApiClient(item.ServerId), - instance = this; - if (item.LocalTrailerCount) return apiClient.getLocalTrailers(apiClient.getCurrentUserId(), item.Id).then(function(result) { - return instance.play({ - items: result - }) - }); - var remoteTrailers = item.RemoteTrailers || []; - return remoteTrailers.length ? this.play({ - items: remoteTrailers.map(function(t) { - return { - Name: t.Name || item.Name + " Trailer", - Url: t.Url, - MediaType: "Video", - Type: "Trailer", - ServerId: apiClient.serverId() + }; + + self.queue = function (options, player) { + queue(options, '', player); + }; + + self.queueNext = function (options, player) { + queue(options, 'next', player); + }; + + function queue(options, mode, player) { + + player = player || self._currentPlayer; + + if (!player) { + return self.play(options); + } + + if (options.items) { + + return translateItemsForPlayback(options.items, options).then(function (items) { + + // TODO: Handle options.startIndex for photos + queueAll(items, mode, player); + + }); + + } else { + + if (!options.serverId) { + throw new Error('serverId required!'); } - }) - }) : Promise.reject() - }, PlaybackManager.prototype.getSubtitleUrl = function(textStream, serverId) { - var apiClient = connectionManager.getApiClient(serverId); - return textStream.IsExternalUrl ? textStream.DeliveryUrl : apiClient.getUrl(textStream.DeliveryUrl) - }, PlaybackManager.prototype.stop = function(player) { - return player = player || this._currentPlayer, player ? (enableLocalPlaylistManagement(player) && (this._playNextAfterEnded = !1), player.stop(!0, !0)) : Promise.resolve() - }, PlaybackManager.prototype.getBufferedRanges = function(player) { - return player = player || this._currentPlayer, player && player.getBufferedRanges ? player.getBufferedRanges() : [] - }, PlaybackManager.prototype.playPause = function(player) { - if (player = player || this._currentPlayer) return player.playPause ? player.playPause() : player.paused() ? this.unpause(player) : this.pause(player) - }, PlaybackManager.prototype.paused = function(player) { - if (player = player || this._currentPlayer) return player.paused() - }, PlaybackManager.prototype.pause = function(player) { - (player = player || this._currentPlayer) && player.pause() - }, PlaybackManager.prototype.unpause = function(player) { - (player = player || this._currentPlayer) && player.unpause() - }, PlaybackManager.prototype.instantMix = function(item, player) { - if ((player = player || this._currentPlayer) && player.instantMix) return player.instantMix(item); - var apiClient = connectionManager.getApiClient(item.ServerId), - options = {}; - options.UserId = apiClient.getCurrentUserId(), options.Limit = 200; + + return getItemsForPlayback(options.serverId, { + + Ids: options.ids.join(',') + + }).then(function (result) { + + return translateItemsForPlayback(result.Items, options).then(function (items) { + + // TODO: Handle options.startIndex for photos + queueAll(items, mode, player); + + }); + }); + } + } + + function queueAll(items, mode, player) { + + if (!items.length) { + return; + } + + if (!player.isLocalPlayer) { + if (mode === 'next') { + player.queueNext({ + items: items + }); + } else { + player.queue({ + items: items + }); + } + return; + } + + var queueDirectToPlayer = player && !enableLocalPlaylistManagement(player); + + if (queueDirectToPlayer) { + + var apiClient = connectionManager.getApiClient(items[0].ServerId); + + player.getDeviceProfile(items[0]).then(function (profile) { + + setStreamUrls(items, profile, self.getMaxStreamingBitrate(player), apiClient, 0).then(function () { + + if (mode === 'next') { + player.queueNext(items); + } else { + player.queue(items); + } + }); + }); + + return; + } + + if (mode === 'next') { + self._playQueueManager.queueNext(items); + } else { + self._playQueueManager.queue(items); + } + } + + function onPlayerProgressInterval() { + var player = this; + sendProgressUpdate(player, 'timeupdate'); + } + + function startPlaybackProgressTimer(player) { + + stopPlaybackProgressTimer(player); + + player._progressInterval = setInterval(onPlayerProgressInterval.bind(player), 10000); + } + + function stopPlaybackProgressTimer(player) { + + if (player._progressInterval) { + + clearInterval(player._progressInterval); + player._progressInterval = null; + } + } + + function onPlaybackStarted(player, playOptions, streamInfo, mediaSource) { + + if (!player) { + throw new Error('player cannot be null'); + } + + setCurrentPlayerInternal(player); + + var playerData = getPlayerData(player); + + playerData.streamInfo = streamInfo; + + streamInfo.playbackStartTimeTicks = new Date().getTime() * 10000; + + if (mediaSource) { + playerData.audioStreamIndex = mediaSource.DefaultAudioStreamIndex; + playerData.subtitleStreamIndex = mediaSource.DefaultSubtitleStreamIndex; + } else { + playerData.audioStreamIndex = null; + playerData.subtitleStreamIndex = null; + } + + self._playNextAfterEnded = true; + var isFirstItem = playOptions.isFirstItem; + var fullscreen = playOptions.fullscreen; + + var state = self.getPlayerState(player, streamInfo.item, streamInfo.mediaSource); + + reportPlayback(self, state, player, true, state.NowPlayingItem.ServerId, 'reportPlaybackStart'); + + state.IsFirstItem = isFirstItem; + state.IsFullscreen = fullscreen; + events.trigger(player, 'playbackstart', [state]); + events.trigger(self, 'playbackstart', [player, state]); + + // only used internally as a safeguard to avoid reporting other events to the server before playback start + streamInfo.started = true; + + startPlaybackProgressTimer(player); + } + + function onPlaybackStartedFromSelfManagingPlayer(e, item, mediaSource) { + + var player = this; + setCurrentPlayerInternal(player); + + var playOptions = item.playOptions || {}; + var isFirstItem = playOptions.isFirstItem; + var fullscreen = playOptions.fullscreen; + + playOptions.isFirstItem = false; + + var playerData = getPlayerData(player); + playerData.streamInfo = {}; + + var streamInfo = playerData.streamInfo; + streamInfo.playbackStartTimeTicks = new Date().getTime() * 10000; + + var state = self.getPlayerState(player, item, mediaSource); + + reportPlayback(self, state, player, true, state.NowPlayingItem.ServerId, 'reportPlaybackStart'); + + state.IsFirstItem = isFirstItem; + state.IsFullscreen = fullscreen; + events.trigger(player, 'playbackstart', [state]); + events.trigger(self, 'playbackstart', [player, state]); + + // only used internally as a safeguard to avoid reporting other events to the server before playback start + streamInfo.started = true; + + startPlaybackProgressTimer(player); + } + + function onPlaybackStoppedFromSelfManagingPlayer(e, playerStopInfo) { + + var player = this; + + stopPlaybackProgressTimer(player); + var state = self.getPlayerState(player, playerStopInfo.item, playerStopInfo.mediaSource); + + var nextItem = playerStopInfo.nextItem; + var nextMediaType = playerStopInfo.nextMediaType; + + var playbackStopInfo = { + player: player, + state: state, + nextItem: (nextItem ? nextItem.item : null), + nextMediaType: nextMediaType + }; + + state.NextMediaType = nextMediaType; + + var streamInfo = getPlayerData(player).streamInfo; + + // only used internally as a safeguard to avoid reporting other events to the server after playback stopped + streamInfo.ended = true; + + if (isServerItem(playerStopInfo.item)) { + + state.PlayState.PositionTicks = (playerStopInfo.positionMs || 0) * 10000; + + reportPlayback(self, state, player, true, playerStopInfo.item.ServerId, 'reportPlaybackStopped'); + } + + state.NextItem = playbackStopInfo.nextItem; + + events.trigger(player, 'playbackstop', [state]); + events.trigger(self, 'playbackstop', [playbackStopInfo]); + + var nextItemPlayOptions = nextItem ? (nextItem.item.playOptions || getDefaultPlayOptions()) : getDefaultPlayOptions(); + var newPlayer = nextItem ? getPlayer(nextItem.item, nextItemPlayOptions) : null; + + if (newPlayer !== player) { + destroyPlayer(player); + removeCurrentPlayer(player); + } + } + + function enablePlaybackRetryWithTranscoding(streamInfo, errorType, currentlyPreventsVideoStreamCopy, currentlyPreventsAudioStreamCopy) { + + // mediadecodeerror, medianotsupported, network, servererror + + if (streamInfo.mediaSource.SupportsTranscoding && (!currentlyPreventsVideoStreamCopy || !currentlyPreventsAudioStreamCopy)) { + + return true; + } + + return false; + } + + function onPlaybackError(e, error) { + + var player = this; + error = error || {}; + + // network + // mediadecodeerror + // medianotsupported + var errorType = error.type; + + console.log('playbackmanager playback error type: ' + (errorType || '')); + + var streamInfo = error.streamInfo || getPlayerData(player).streamInfo; + + if (streamInfo) { + + var currentlyPreventsVideoStreamCopy = streamInfo.url.toLowerCase().indexOf('allowvideostreamcopy=false') !== -1; + var currentlyPreventsAudioStreamCopy = streamInfo.url.toLowerCase().indexOf('allowaudiostreamcopy=false') !== -1; + + // Auto switch to transcoding + if (enablePlaybackRetryWithTranscoding(streamInfo, errorType, currentlyPreventsVideoStreamCopy, currentlyPreventsAudioStreamCopy)) { + + var startTime = getCurrentTicks(player) || streamInfo.playerStartPositionTicks; + + changeStream(player, startTime, { + + // force transcoding + EnableDirectPlay: false, + EnableDirectStream: false, + AllowVideoStreamCopy: false, + AllowAudioStreamCopy: currentlyPreventsAudioStreamCopy || currentlyPreventsVideoStreamCopy ? false : null + + }, true); + + return; + } + } + + var displayErrorCode = 'NoCompatibleStream'; + onPlaybackStopped.call(player, e, displayErrorCode); + } + + function onPlaybackStopped(e, displayErrorCode) { + + var player = this; + + if (getPlayerData(player).isChangingStream) { + return; + } + + stopPlaybackProgressTimer(player); + + // User clicked stop or content ended + var state = self.getPlayerState(player); + var streamInfo = getPlayerData(player).streamInfo; + + var nextItem = self._playNextAfterEnded ? self._playQueueManager.getNextItemInfo() : null; + + var nextMediaType = (nextItem ? nextItem.item.MediaType : null); + + var playbackStopInfo = { + player: player, + state: state, + nextItem: (nextItem ? nextItem.item : null), + nextMediaType: nextMediaType + }; + + state.NextMediaType = nextMediaType; + + if (isServerItem(streamInfo.item)) { + + if (player.supportsProgress === false && state.PlayState && !state.PlayState.PositionTicks) { + state.PlayState.PositionTicks = streamInfo.item.RunTimeTicks; + } + + // only used internally as a safeguard to avoid reporting other events to the server after playback stopped + streamInfo.ended = true; + + reportPlayback(self, state, player, true, streamInfo.item.ServerId, 'reportPlaybackStopped'); + } + + state.NextItem = playbackStopInfo.nextItem; + + if (!nextItem) { + self._playQueueManager.reset(); + } + + events.trigger(player, 'playbackstop', [state]); + events.trigger(self, 'playbackstop', [playbackStopInfo]); + + var nextItemPlayOptions = nextItem ? (nextItem.item.playOptions || getDefaultPlayOptions()) : getDefaultPlayOptions(); + var newPlayer = nextItem ? getPlayer(nextItem.item, nextItemPlayOptions) : null; + + if (newPlayer !== player) { + destroyPlayer(player); + removeCurrentPlayer(player); + } + + if (displayErrorCode && typeof (displayErrorCode) === 'string') { + showPlaybackInfoErrorMessage(self, displayErrorCode, nextItem); + } + else if (nextItem) { + self.nextTrack(); + } + } + + function onPlaybackChanging(activePlayer, newPlayer, newItem) { + + var state = self.getPlayerState(activePlayer); + + var serverId = self.currentItem(activePlayer).ServerId; + + // User started playing something new while existing content is playing + var promise; + + stopPlaybackProgressTimer(activePlayer); + unbindStopped(activePlayer); + + if (activePlayer === newPlayer) { + + // If we're staying with the same player, stop it + promise = activePlayer.stop(false); + + } else { + + // If we're switching players, tear down the current one + promise = activePlayer.stop(true); + } + + return promise.then(function () { + + bindStopped(activePlayer); + + if (enableLocalPlaylistManagement(activePlayer)) { + reportPlayback(self, state, activePlayer, true, serverId, 'reportPlaybackStopped'); + } + + events.trigger(self, 'playbackstop', [{ + player: activePlayer, + state: state, + nextItem: newItem, + nextMediaType: newItem.MediaType + }]); + }); + } + + function bindStopped(player) { + + if (enableLocalPlaylistManagement(player)) { + events.off(player, 'stopped', onPlaybackStopped); + events.on(player, 'stopped', onPlaybackStopped); + } + } + + function onPlaybackTimeUpdate(e) { + var player = this; + sendProgressUpdate(player, 'timeupdate'); + } + + function onPlaybackPause(e) { + var player = this; + sendProgressUpdate(player, 'pause'); + } + + function onPlaybackUnpause(e) { + var player = this; + sendProgressUpdate(player, 'unpause'); + } + + function onPlaybackVolumeChange(e) { + var player = this; + sendProgressUpdate(player, 'volumechange'); + } + + function onRepeatModeChange(e) { + var player = this; + sendProgressUpdate(player, 'repeatmodechange'); + } + + function onPlaylistItemMove(e) { + var player = this; + sendProgressUpdate(player, 'playlistitemmove', true); + } + + function onPlaylistItemRemove(e) { + var player = this; + sendProgressUpdate(player, 'playlistitemremove', true); + } + + function onPlaylistItemAdd(e) { + var player = this; + sendProgressUpdate(player, 'playlistitemadd', true); + } + + function unbindStopped(player) { + + events.off(player, 'stopped', onPlaybackStopped); + } + + function initLegacyVolumeMethods(player) { + player.getVolume = function () { + return player.volume(); + }; + player.setVolume = function (val) { + return player.volume(val); + }; + } + + function initMediaPlayer(player) { + + players.push(player); + players.sort(function (a, b) { + + return (a.priority || 0) - (b.priority || 0); + }); + + if (player.isLocalPlayer !== false) { + player.isLocalPlayer = true; + } + + player.currentState = {}; + + if (!player.getVolume || !player.setVolume) { + initLegacyVolumeMethods(player); + } + + if (enableLocalPlaylistManagement(player)) { + events.on(player, 'error', onPlaybackError); + events.on(player, 'timeupdate', onPlaybackTimeUpdate); + events.on(player, 'pause', onPlaybackPause); + events.on(player, 'unpause', onPlaybackUnpause); + events.on(player, 'volumechange', onPlaybackVolumeChange); + events.on(player, 'repeatmodechange', onRepeatModeChange); + events.on(player, 'playlistitemmove', onPlaylistItemMove); + events.on(player, 'playlistitemremove', onPlaylistItemRemove); + events.on(player, 'playlistitemadd', onPlaylistItemAdd); + } else if (player.isLocalPlayer) { + + events.on(player, 'itemstarted', onPlaybackStartedFromSelfManagingPlayer); + events.on(player, 'itemstopped', onPlaybackStoppedFromSelfManagingPlayer); + events.on(player, 'timeupdate', onPlaybackTimeUpdate); + events.on(player, 'pause', onPlaybackPause); + events.on(player, 'unpause', onPlaybackUnpause); + events.on(player, 'volumechange', onPlaybackVolumeChange); + events.on(player, 'repeatmodechange', onRepeatModeChange); + events.on(player, 'playlistitemmove', onPlaylistItemMove); + events.on(player, 'playlistitemremove', onPlaylistItemRemove); + events.on(player, 'playlistitemadd', onPlaylistItemAdd); + } + + if (player.isLocalPlayer) { + bindToFullscreenChange(player); + } + bindStopped(player); + } + + events.on(pluginManager, 'registered', function (e, plugin) { + + if (plugin.type === 'mediaplayer') { + + initMediaPlayer(plugin); + } + }); + + pluginManager.ofType('mediaplayer').map(initMediaPlayer); + + function sendProgressUpdate(player, progressEventName, reportPlaylist) { + + if (!player) { + throw new Error('player cannot be null'); + } + + var state = self.getPlayerState(player); + + if (state.NowPlayingItem) { + var serverId = state.NowPlayingItem.ServerId; + + var streamInfo = getPlayerData(player).streamInfo; + + if (streamInfo && streamInfo.started && !streamInfo.ended) { + reportPlayback(self, state, player, reportPlaylist, serverId, 'reportPlaybackProgress', progressEventName); + } + + if (streamInfo && streamInfo.liveStreamId) { + + if (new Date().getTime() - (streamInfo.lastMediaInfoQuery || 0) >= 600000) { + getLiveStreamMediaInfo(player, streamInfo, self.currentMediaSource(player), streamInfo.liveStreamId, serverId); + } + } + } + } + + function getLiveStreamMediaInfo(player, streamInfo, mediaSource, liveStreamId, serverId) { + + console.log('getLiveStreamMediaInfo'); + + streamInfo.lastMediaInfoQuery = new Date().getTime(); + + var apiClient = connectionManager.getApiClient(serverId); + + if (!apiClient.isMinServerVersion('3.2.70.7')) { + return; + } + + connectionManager.getApiClient(serverId).getLiveStreamMediaInfo(liveStreamId).then(function (info) { + + mediaSource.MediaStreams = info.MediaStreams; + events.trigger(player, 'mediastreamschange'); + + }, function () { + + }); + } + + self.onAppClose = function () { + + var player = this._currentPlayer; + + // Try to report playback stopped before the app closes + if (player && this.isPlaying(player)) { + this._playNextAfterEnded = false; + onPlaybackStopped.call(player); + } + }; + + self.playbackStartTime = function (player) { + + player = player || this._currentPlayer; + if (player && !enableLocalPlaylistManagement(player) && !player.isLocalPlayer) { + return player.playbackStartTime(); + } + + var streamInfo = getPlayerData(player).streamInfo; + return streamInfo ? streamInfo.playbackStartTimeTicks : null; + }; + + if (apphost.supports('remotecontrol')) { + + require(['serverNotifications'], function (serverNotifications) { + events.on(serverNotifications, 'ServerShuttingDown', self.setDefaultPlayerActive.bind(self)); + events.on(serverNotifications, 'ServerRestarting', self.setDefaultPlayerActive.bind(self)); + }); + } + } + + PlaybackManager.prototype.getCurrentPlayer = function () { + return this._currentPlayer; + }; + + PlaybackManager.prototype.currentTime = function (player) { + + player = player || this._currentPlayer; + if (player && !enableLocalPlaylistManagement(player) && !player.isLocalPlayer) { + return player.currentTime(); + } + + return this.getCurrentTicks(player); + }; + + PlaybackManager.prototype.nextItem = function (player) { + + player = player || this._currentPlayer; + + if (player && !enableLocalPlaylistManagement(player)) { + return player.nextItem(); + } + + var nextItem = this._playQueueManager.getNextItemInfo(); + + if (!nextItem || !nextItem.item) { + return Promise.reject(); + } + + var apiClient = connectionManager.getApiClient(nextItem.item.ServerId); + return apiClient.getItem(apiClient.getCurrentUserId(), nextItem.item.Id); + }; + + PlaybackManager.prototype.canQueue = function (item) { + + if (item.Type === 'MusicAlbum' || item.Type === 'MusicArtist' || item.Type === 'MusicGenre') { + return this.canQueueMediaType('Audio'); + } + return this.canQueueMediaType(item.MediaType); + }; + + PlaybackManager.prototype.canQueueMediaType = function (mediaType) { + + if (this._currentPlayer) { + return this._currentPlayer.canPlayMediaType(mediaType); + } + + return false; + }; + + PlaybackManager.prototype.isMuted = function (player) { + + player = player || this._currentPlayer; + + if (player) { + return player.isMuted(); + } + + return false; + }; + + PlaybackManager.prototype.setMute = function (mute, player) { + + player = player || this._currentPlayer; + + if (player) { + player.setMute(mute); + } + }; + + PlaybackManager.prototype.toggleMute = function (mute, player) { + + player = player || this._currentPlayer; + if (player) { + + if (player.toggleMute) { + player.toggleMute(); + } else { + player.setMute(!player.isMuted()); + } + } + }; + + PlaybackManager.prototype.toggleDisplayMirroring = function () { + this.enableDisplayMirroring(!this.enableDisplayMirroring()); + }; + + PlaybackManager.prototype.enableDisplayMirroring = function (enabled) { + + if (enabled != null) { + + var val = enabled ? '1' : '0'; + appSettings.set('displaymirror', val); + return; + } + + return (appSettings.get('displaymirror') || '') !== '0'; + }; + + PlaybackManager.prototype.nextChapter = function (player) { + + player = player || this._currentPlayer; + var item = this.currentItem(player); + + var ticks = this.getCurrentTicks(player); + + var nextChapter = (item.Chapters || []).filter(function (i) { + + return i.StartPositionTicks > ticks; + + })[0]; + + if (nextChapter) { + this.seek(nextChapter.StartPositionTicks, player); + } else { + this.nextTrack(player); + } + }; + + PlaybackManager.prototype.previousChapter = function (player) { + + player = player || this._currentPlayer; + var item = this.currentItem(player); + + var ticks = this.getCurrentTicks(player); + + // Go back 10 seconds + ticks -= 100000000; + + // If there's no previous track, then at least rewind to beginning + if (this.getCurrentPlaylistIndex(player) === 0) { + ticks = Math.max(ticks, 0); + } + + var previousChapters = (item.Chapters || []).filter(function (i) { + + return i.StartPositionTicks <= ticks; + }); + + if (previousChapters.length) { + this.seek(previousChapters[previousChapters.length - 1].StartPositionTicks, player); + } else { + this.previousTrack(player); + } + }; + + PlaybackManager.prototype.fastForward = function (player) { + + player = player || this._currentPlayer; + + if (player.fastForward != null) { + player.fastForward(userSettings.skipForwardLength()); + return; + } + + // Go back 15 seconds + var offsetTicks = userSettings.skipForwardLength() * 10000; + + this.seekRelative(offsetTicks, player); + }; + + PlaybackManager.prototype.rewind = function (player) { + + player = player || this._currentPlayer; + + if (player.rewind != null) { + player.rewind(userSettings.skipBackLength()); + return; + } + + // Go back 15 seconds + var offsetTicks = 0 - (userSettings.skipBackLength() * 10000); + + this.seekRelative(offsetTicks, player); + }; + + PlaybackManager.prototype.seekPercent = function (percent, player) { + + player = player || this._currentPlayer; + + var ticks = this.duration(player) || 0; + + percent /= 100; + ticks *= percent; + this.seek(parseInt(ticks), player); + }; + + PlaybackManager.prototype.playTrailers = function (item) { + + var player = this._currentPlayer; + + if (player && player.playTrailers) { + return player.playTrailers(item); + } + + var apiClient = connectionManager.getApiClient(item.ServerId); + var instance = this; - apiClient.getInstantMixFromItem(item.Id, options).then(function(result) { + + if (item.LocalTrailerCount) { + return apiClient.getLocalTrailers(apiClient.getCurrentUserId(), item.Id).then(function (result) { + return instance.play({ + items: result + }); + }); + } else { + var remoteTrailers = item.RemoteTrailers || []; + + if (!remoteTrailers.length) { + return Promise.reject(); + } + + return this.play({ + items: remoteTrailers.map(function (t) { + return { + Name: t.Name || (item.Name + ' Trailer'), + Url: t.Url, + MediaType: 'Video', + Type: 'Trailer', + ServerId: apiClient.serverId() + }; + }) + }); + } + }; + + PlaybackManager.prototype.getSubtitleUrl = function (textStream, serverId) { + + var apiClient = connectionManager.getApiClient(serverId); + var textStreamUrl = !textStream.IsExternalUrl ? apiClient.getUrl(textStream.DeliveryUrl) : textStream.DeliveryUrl; + return textStreamUrl; + }; + + PlaybackManager.prototype.stop = function (player) { + + player = player || this._currentPlayer; + + if (player) { + + if (enableLocalPlaylistManagement(player)) { + this._playNextAfterEnded = false; + } + + // TODO: remove second param + return player.stop(true, true); + } + + return Promise.resolve(); + }; + + PlaybackManager.prototype.getBufferedRanges = function (player) { + + player = player || this._currentPlayer; + + if (player) { + + if (player.getBufferedRanges) { + return player.getBufferedRanges(); + } + } + + return []; + }; + + PlaybackManager.prototype.playPause = function (player) { + + player = player || this._currentPlayer; + + if (player) { + + if (player.playPause) { + return player.playPause(); + } + + if (player.paused()) { + return this.unpause(player); + } else { + return this.pause(player); + } + } + }; + + PlaybackManager.prototype.paused = function (player) { + + player = player || this._currentPlayer; + + if (player) { + return player.paused(); + } + }; + + PlaybackManager.prototype.pause = function (player) { + player = player || this._currentPlayer; + + if (player) { + player.pause(); + } + }; + + PlaybackManager.prototype.unpause = function (player) { + player = player || this._currentPlayer; + + if (player) { + player.unpause(); + } + }; + + PlaybackManager.prototype.instantMix = function (item, player) { + + player = player || this._currentPlayer; + if (player && player.instantMix) { + return player.instantMix(item); + } + + var apiClient = connectionManager.getApiClient(item.ServerId); + + var options = {}; + options.UserId = apiClient.getCurrentUserId(); + options.Limit = 200; + + var instance = this; + + apiClient.getInstantMixFromItem(item.Id, options).then(function (result) { instance.play({ items: result.Items - }) - }) - }, PlaybackManager.prototype.shuffle = function(shuffleItem, player, queryOptions) { - return player = player || this._currentPlayer, player && player.shuffle ? player.shuffle(shuffleItem) : this.play({ - items: [shuffleItem], - shuffle: !0 - }) - }, PlaybackManager.prototype.audioTracks = function(player) { - if (player = player || this._currentPlayer, player.audioTracks) { + }); + }); + }; + + PlaybackManager.prototype.shuffle = function (shuffleItem, player, queryOptions) { + + player = player || this._currentPlayer; + if (player && player.shuffle) { + return player.shuffle(shuffleItem); + } + + return this.play({ items: [shuffleItem], shuffle: true }); + }; + + PlaybackManager.prototype.audioTracks = function (player) { + + player = player || this._currentPlayer; + if (player.audioTracks) { var result = player.audioTracks(); - if (result) return result + if (result) { + return result; + } } - return ((this.currentMediaSource(player) || {}).MediaStreams || []).filter(function(s) { - return "Audio" === s.Type - }) - }, PlaybackManager.prototype.subtitleTracks = function(player) { - if (player = player || this._currentPlayer, player.subtitleTracks) { + + var mediaSource = this.currentMediaSource(player); + + var mediaStreams = (mediaSource || {}).MediaStreams || []; + return mediaStreams.filter(function (s) { + return s.Type === 'Audio'; + }); + }; + + PlaybackManager.prototype.subtitleTracks = function (player) { + + player = player || this._currentPlayer; + if (player.subtitleTracks) { var result = player.subtitleTracks(); - if (result) return result + if (result) { + return result; + } } - return ((this.currentMediaSource(player) || {}).MediaStreams || []).filter(function(s) { - return "Subtitle" === s.Type - }) - }, PlaybackManager.prototype.getSupportedCommands = function(player) { - if (player = player || this._currentPlayer || { - isLocalPlayer: !0 - }, player.isLocalPlayer) { - var list = ["GoHome", "GoToSettings", "VolumeUp", "VolumeDown", "Mute", "Unmute", "ToggleMute", "SetVolume", "SetAudioStreamIndex", "SetSubtitleStreamIndex", "SetMaxStreamingBitrate", "DisplayContent", "GoToSearch", "DisplayMessage", "SetRepeatMode", "PlayMediaSource", "PlayTrailers"]; - return apphost.supports("fullscreenchange") && list.push("ToggleFullscreen"), player.supports && (player.supports("PictureInPicture") && list.push("PictureInPicture"), player.supports("SetBrightness") && list.push("SetBrightness"), player.supports("SetAspectRatio") && list.push("SetAspectRatio")), list + + var mediaSource = this.currentMediaSource(player); + + var mediaStreams = (mediaSource || {}).MediaStreams || []; + return mediaStreams.filter(function (s) { + return s.Type === 'Subtitle'; + }); + }; + + PlaybackManager.prototype.getSupportedCommands = function (player) { + + player = player || this._currentPlayer || { isLocalPlayer: true }; + + if (player.isLocalPlayer) { + // Full list + // https://github.com/MediaBrowser/MediaBrowser/blob/master/MediaBrowser.Model/Session/GeneralCommand.cs + var list = [ + "GoHome", + "GoToSettings", + "VolumeUp", + "VolumeDown", + "Mute", + "Unmute", + "ToggleMute", + "SetVolume", + "SetAudioStreamIndex", + "SetSubtitleStreamIndex", + "SetMaxStreamingBitrate", + "DisplayContent", + "GoToSearch", + "DisplayMessage", + "SetRepeatMode", + "PlayMediaSource", + "PlayTrailers" + ]; + + if (apphost.supports('fullscreenchange')) { + list.push('ToggleFullscreen'); + } + + if (player.supports) { + if (player.supports('PictureInPicture')) { + list.push('PictureInPicture'); + } + if (player.supports('SetBrightness')) { + list.push('SetBrightness'); + } + if (player.supports('SetAspectRatio')) { + list.push('SetAspectRatio'); + } + } + + return list; } + var info = this.getPlayerInfo(); - return info ? info.supportedCommands : [] - }, PlaybackManager.prototype.setRepeatMode = function(value, player) { - if ((player = player || this._currentPlayer) && !enableLocalPlaylistManagement(player)) return player.setRepeatMode(value); - this._playQueueManager.setRepeatMode(value), events.trigger(player, "repeatmodechange") - }, PlaybackManager.prototype.getRepeatMode = function(player) { - return player = player || this._currentPlayer, player && !enableLocalPlaylistManagement(player) ? player.getRepeatMode() : this._playQueueManager.getRepeatMode() - }, PlaybackManager.prototype.trySetActiveDeviceName = function(name) { + return info ? info.supportedCommands : []; + }; + + PlaybackManager.prototype.setRepeatMode = function (value, player) { + + player = player || this._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + return player.setRepeatMode(value); + } + + this._playQueueManager.setRepeatMode(value); + events.trigger(player, 'repeatmodechange'); + }; + + PlaybackManager.prototype.getRepeatMode = function (player) { + + player = player || this._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + return player.getRepeatMode(); + } + + return this._playQueueManager.getRepeatMode(); + }; + + PlaybackManager.prototype.trySetActiveDeviceName = function (name) { + name = normalizeName(name); + var instance = this; - instance.getTargets().then(function(result) { - var target = result.filter(function(p) { - return normalizeName(p.name) === name + instance.getTargets().then(function (result) { + + var target = result.filter(function (p) { + return normalizeName(p.name) === name; })[0]; - target && instance.trySetActivePlayer(target.playerName, target) - }) - }, PlaybackManager.prototype.displayContent = function(options, player) { - (player = player || this._currentPlayer) && player.displayContent && player.displayContent(options) - }, PlaybackManager.prototype.beginPlayerUpdates = function(player) { - player.beginPlayerUpdates && player.beginPlayerUpdates() - }, PlaybackManager.prototype.endPlayerUpdates = function(player) { - player.endPlayerUpdates && player.endPlayerUpdates() - }, PlaybackManager.prototype.setDefaultPlayerActive = function() { - this.setActivePlayer("localplayer") - }, PlaybackManager.prototype.removeActivePlayer = function(name) { + + if (target) { + instance.trySetActivePlayer(target.playerName, target); + } + + }); + }; + + PlaybackManager.prototype.displayContent = function (options, player) { + player = player || this._currentPlayer; + if (player && player.displayContent) { + player.displayContent(options); + } + }; + + PlaybackManager.prototype.beginPlayerUpdates = function (player) { + if (player.beginPlayerUpdates) { + player.beginPlayerUpdates(); + } + }; + + PlaybackManager.prototype.endPlayerUpdates = function (player) { + if (player.endPlayerUpdates) { + player.endPlayerUpdates(); + } + }; + + PlaybackManager.prototype.setDefaultPlayerActive = function () { + + this.setActivePlayer('localplayer'); + }; + + PlaybackManager.prototype.removeActivePlayer = function (name) { + var playerInfo = this.getPlayerInfo(); - playerInfo && playerInfo.name === name && this.setDefaultPlayerActive() - }, PlaybackManager.prototype.removeActiveTarget = function(id) { + if (playerInfo) { + if (playerInfo.name === name) { + this.setDefaultPlayerActive(); + } + } + }; + + PlaybackManager.prototype.removeActiveTarget = function (id) { + var playerInfo = this.getPlayerInfo(); - playerInfo && playerInfo.id === id && this.setDefaultPlayerActive() - }, PlaybackManager.prototype.sendCommand = function(cmd, player) { - switch (console.log("MediaController received command: " + cmd.Name), cmd.Name) { - case "SetRepeatMode": + if (playerInfo) { + if (playerInfo.id === id) { + this.setDefaultPlayerActive(); + } + } + }; + + PlaybackManager.prototype.sendCommand = function (cmd, player) { + + // Full list + // https://github.com/MediaBrowser/MediaBrowser/blob/master/MediaBrowser.Model/Session/GeneralCommand.cs#L23 + console.log('MediaController received command: ' + cmd.Name); + switch (cmd.Name) { + + case 'SetRepeatMode': this.setRepeatMode(cmd.Arguments.RepeatMode, player); break; - case "VolumeUp": + case 'VolumeUp': this.volumeUp(player); break; - case "VolumeDown": + case 'VolumeDown': this.volumeDown(player); break; - case "Mute": - this.setMute(!0, player); + case 'Mute': + this.setMute(true, player); break; - case "Unmute": - this.setMute(!1, player); + case 'Unmute': + this.setMute(false, player); break; - case "ToggleMute": + case 'ToggleMute': this.toggleMute(player); break; - case "SetVolume": + case 'SetVolume': this.setVolume(cmd.Arguments.Volume, player); break; - case "SetAspectRatio": + case 'SetAspectRatio': this.setAspectRatio(cmd.Arguments.AspectRatio, player); break; - case "SetBrightness": + case 'SetBrightness': this.setBrightness(cmd.Arguments.Brightness, player); break; - case "SetAudioStreamIndex": + case 'SetAudioStreamIndex': this.setAudioStreamIndex(parseInt(cmd.Arguments.Index), player); break; - case "SetSubtitleStreamIndex": + case 'SetSubtitleStreamIndex': this.setSubtitleStreamIndex(parseInt(cmd.Arguments.Index), player); break; - case "SetMaxStreamingBitrate": + case 'SetMaxStreamingBitrate': + // todo + //this.setMaxStreamingBitrate(parseInt(cmd.Arguments.Bitrate), player); break; - case "ToggleFullscreen": + case 'ToggleFullscreen': this.toggleFullscreen(player); break; default: - player.sendCommand && player.sendCommand(cmd) + { + if (player.sendCommand) { + player.sendCommand(cmd); + } + break; + } } - }, new PlaybackManager + }; + + return new PlaybackManager(); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/playback/playbackorientation.js b/src/bower_components/emby-webcomponents/playback/playbackorientation.js index b682f7c564..30997dbdac 100644 --- a/src/bower_components/emby-webcomponents/playback/playbackorientation.js +++ b/src/bower_components/emby-webcomponents/playback/playbackorientation.js @@ -1,35 +1,57 @@ -define(["playbackManager", "layoutManager", "events"], function(playbackManager, layoutManager, events) { +define(['playbackManager', 'layoutManager', 'events'], function (playbackManager, layoutManager, events) { "use strict"; + var orientationLocked; + function onOrientationChangeSuccess() { - orientationLocked = !0 + orientationLocked = true; } function onOrientationChangeError(err) { - orientationLocked = !1, console.log("error locking orientation: " + err) + orientationLocked = false; + console.log('error locking orientation: ' + err); } - var orientationLocked; - events.on(playbackManager, "playbackstart", function(e, player, state) { - if (player.isLocalPlayer && !player.isExternalPlayer && playbackManager.isPlayingVideo(player) && layoutManager.mobile) { - var lockOrientation = screen.lockOrientation || screen.mozLockOrientation || screen.msLockOrientation || screen.orientation && screen.orientation.lock; - if (lockOrientation) try { - var promise = lockOrientation("landscape"); - promise.then ? promise.then(onOrientationChangeSuccess, onOrientationChangeError) : orientationLocked = promise - } catch (err) { - onOrientationChangeError(err) + + events.on(playbackManager, 'playbackstart', function (e, player, state) { + + var isLocalVideo = player.isLocalPlayer && !player.isExternalPlayer && playbackManager.isPlayingVideo(player); + + if (isLocalVideo && layoutManager.mobile) { + var lockOrientation = screen.lockOrientation || screen.mozLockOrientation || screen.msLockOrientation || (screen.orientation && screen.orientation.lock); + + if (lockOrientation) { + + try { + var promise = lockOrientation('landscape'); + if (promise.then) { + promise.then(onOrientationChangeSuccess, onOrientationChangeError); + } else { + // returns a boolean + orientationLocked = promise; + } + } + catch (err) { + onOrientationChangeError(err); + } } } - }), events.on(playbackManager, "playbackstop", function(e, playbackStopInfo) { + }); + + events.on(playbackManager, 'playbackstop', function (e, playbackStopInfo) { + if (orientationLocked && !playbackStopInfo.nextMediaType) { - var unlockOrientation = screen.unlockOrientation || screen.mozUnlockOrientation || screen.msUnlockOrientation || screen.orientation && screen.orientation.unlock; + + var unlockOrientation = screen.unlockOrientation || screen.mozUnlockOrientation || screen.msUnlockOrientation || (screen.orientation && screen.orientation.unlock); + if (unlockOrientation) { try { - unlockOrientation() - } catch (err) { - console.log("error unlocking orientation: " + err) + unlockOrientation(); } - orientationLocked = !1 + catch (err) { + console.log('error unlocking orientation: ' + err); + } + orientationLocked = false; } } - }) + }); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/playback/playbackvalidation.js b/src/bower_components/emby-webcomponents/playback/playbackvalidation.js index 6fa070ab3b..c36d5c1b5a 100644 --- a/src/bower_components/emby-webcomponents/playback/playbackvalidation.js +++ b/src/bower_components/emby-webcomponents/playback/playbackvalidation.js @@ -1,43 +1,80 @@ -define(["playbackManager", "itemHelper"], function(playbackManager, itemHelper) { +define(['playbackManager', 'itemHelper'], function (playbackManager, itemHelper) { "use strict"; function getRequirePromise(deps) { - return new Promise(function(resolve, reject) { - require(deps, resolve) - }) + + return new Promise(function (resolve, reject) { + + require(deps, resolve); + }); } function validatePlayback(options) { - var feature = "playback"; - if (!options.item || "TvChannel" !== options.item.Type && "Recording" !== options.item.Type || (feature = "livetv"), "playback" === feature) { - var player = playbackManager.getCurrentPlayer(); - if (player && !player.isLocalPlayer) return Promise.resolve() + + var feature = 'playback'; + if (options.item && (options.item.Type === 'TvChannel' || options.item.Type === 'Recording')) { + feature = 'livetv'; } - return getRequirePromise(["registrationServices"]).then(function(registrationServices) { - return registrationServices.validateFeature(feature, options).then(function(result) { - result && result.enableTimeLimit && startAutoStopTimer() - }) - }) + + if (feature === 'playback') { + var player = playbackManager.getCurrentPlayer(); + if (player && !player.isLocalPlayer) { + return Promise.resolve(); + } + } + + return getRequirePromise(["registrationServices"]).then(function (registrationServices) { + + return registrationServices.validateFeature(feature, options).then(function (result) { + + if (result && result.enableTimeLimit) { + startAutoStopTimer(); + } + }); + }); } + var autoStopTimeout; function startAutoStopTimer() { - stopAutoStopTimer(), autoStopTimeout = setTimeout(onAutoStopTimeout, 63e3) + stopAutoStopTimer(); + autoStopTimeout = setTimeout(onAutoStopTimeout, 63000); } function onAutoStopTimeout() { - stopAutoStopTimer(), playbackManager.stop() + stopAutoStopTimer(); + playbackManager.stop(); } function stopAutoStopTimer() { + var timeout = autoStopTimeout; - timeout && (clearTimeout(timeout), autoStopTimeout = null) + if (timeout) { + clearTimeout(timeout); + autoStopTimeout = null; + } } function PlaybackValidation() { - this.name = "Playback validation", this.type = "preplayintercept", this.id = "playbackvalidation", this.order = -1 + + this.name = 'Playback validation'; + this.type = 'preplayintercept'; + this.id = 'playbackvalidation'; + this.order = -1; } - var autoStopTimeout; - return PlaybackValidation.prototype.intercept = function(options) { - return options.fullscreen ? options.item && itemHelper.isLocalItem(options.item) ? Promise.resolve() : validatePlayback(options) : Promise.resolve() - }, PlaybackValidation + + PlaybackValidation.prototype.intercept = function (options) { + + // Don't care about video backdrops, or theme music or any kind of non-fullscreen playback + if (!options.fullscreen) { + return Promise.resolve(); + } + + if (options.item && itemHelper.isLocalItem(options.item)) { + return Promise.resolve(); + } + + return validatePlayback(options); + }; + + return PlaybackValidation; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/playback/playerselection.js b/src/bower_components/emby-webcomponents/playback/playerselection.js index 0a2445f676..86a0b9daab 100644 --- a/src/bower_components/emby-webcomponents/playback/playerselection.js +++ b/src/bower_components/emby-webcomponents/playback/playerselection.js @@ -1,158 +1,323 @@ -define(["appSettings", "events", "browser", "loading", "playbackManager", "appRouter", "globalize", "apphost"], function(appSettings, events, browser, loading, playbackManager, appRouter, globalize, appHost) { - "use strict"; +define(['appSettings', 'events', 'browser', 'loading', 'playbackManager', 'appRouter', 'globalize', 'apphost'], function (appSettings, events, browser, loading, playbackManager, appRouter, globalize, appHost) { + 'use strict'; function mirrorItem(info, player) { + var item = info.item; + playbackManager.displayContent({ + ItemName: item.Name, ItemId: item.Id, ItemType: item.Type, Context: info.context - }, player) + }, player); } function mirrorIfEnabled(info) { + if (info && playbackManager.enableDisplayMirroring()) { + var getPlayerInfo = playbackManager.getPlayerInfo(); - getPlayerInfo && (getPlayerInfo.isLocalPlayer || -1 === getPlayerInfo.supportedCommands.indexOf("DisplayContent") || mirrorItem(info, playbackManager.getCurrentPlayer())) + + if (getPlayerInfo) { + if (!getPlayerInfo.isLocalPlayer && getPlayerInfo.supportedCommands.indexOf('DisplayContent') !== -1) { + mirrorItem(info, playbackManager.getCurrentPlayer()); + } + } } } - function emptyCallback() {} + function emptyCallback() { + // avoid console logs about uncaught promises + } function getTargetSecondaryText(target) { - return target.user ? target.user.Name : null + + if (target.user) { + + return target.user.Name; + } + + return null; } function getIcon(target) { + var deviceType = target.deviceType; - switch (!deviceType && target.isLocalPlayer && (deviceType = browser.tv ? "tv" : browser.mobile ? "smartphone" : "desktop"), deviceType || (deviceType = "tv"), deviceType) { - case "smartphone": - return ""; - case "tablet": - return ""; - case "tv": - return ""; - case "cast": - return ""; - case "desktop": - return ""; + + if (!deviceType && target.isLocalPlayer) { + if (browser.tv) { + deviceType = 'tv'; + } else if (browser.mobile) { + deviceType = 'smartphone'; + } else { + deviceType = 'desktop'; + } + } + + if (!deviceType) { + deviceType = 'tv'; + } + + switch (deviceType) { + + case 'smartphone': + return ''; + case 'tablet': + return ''; + case 'tv': + return ''; + case 'cast': + return ''; + case 'desktop': + return ''; default: - return "" + return ''; } } function showPlayerSelection(button) { + var currentPlayerInfo = playbackManager.getPlayerInfo(); - if (currentPlayerInfo && !currentPlayerInfo.isLocalPlayer) return void showActivePlayerMenu(currentPlayerInfo); + + if (currentPlayerInfo) { + if (!currentPlayerInfo.isLocalPlayer) { + showActivePlayerMenu(currentPlayerInfo); + return; + } + } + var currentPlayerId = currentPlayerInfo ? currentPlayerInfo.id : null; - loading.show(), playbackManager.getTargets().then(function(targets) { - var menuItems = targets.map(function(t) { + + loading.show(); + + playbackManager.getTargets().then(function (targets) { + + var menuItems = targets.map(function (t) { + var name = t.name; - return t.appName && t.appName !== t.name && (name += " - " + t.appName), { + + if (t.appName && t.appName !== t.name) { + name += " - " + t.appName; + } + + return { name: name, id: t.id, selected: currentPlayerId === t.id, secondaryText: getTargetSecondaryText(t), icon: getIcon(t) - } + }; + }); - require(["actionsheet"], function(actionsheet) { + + require(['actionsheet'], function (actionsheet) { + loading.hide(); + var menuOptions = { - title: globalize.translate("sharedcomponents#HeaderPlayOn"), + title: globalize.translate('sharedcomponents#HeaderPlayOn'), items: menuItems, positionTo: button, - resolveOnClick: !0, - border: !0 + + resolveOnClick: true, + border: true }; - browser.chrome && !appHost.supports("castmenuhashchange") && (menuOptions.enableHistory = !1), actionsheet.show(menuOptions).then(function(id) { - var target = targets.filter(function(t) { - return t.id === id + + // Unfortunately we can't allow the url to change or chromecast will throw a security error + // Might be able to solve this in the future by moving the dialogs to hashbangs + if (!(!browser.chrome || appHost.supports('castmenuhashchange'))) { + menuOptions.enableHistory = false; + } + + actionsheet.show(menuOptions).then(function (id) { + + var target = targets.filter(function (t) { + return t.id === id; })[0]; - playbackManager.trySetActivePlayer(target.playerName, target), mirrorIfEnabled() - }, emptyCallback) - }) - }) + + playbackManager.trySetActivePlayer(target.playerName, target); + + mirrorIfEnabled(); + + }, emptyCallback); + }); + }); } function showActivePlayerMenu(playerInfo) { - require(["dialogHelper", "dialog", "emby-checkbox", "emby-button"], function(dialogHelper) { - showActivePlayerMenuInternal(dialogHelper, playerInfo) - }) + + require(['dialogHelper', 'dialog', 'emby-checkbox', 'emby-button'], function (dialogHelper) { + showActivePlayerMenuInternal(dialogHelper, playerInfo); + }); } + function disconnectFromPlayer(currentDeviceName) { - -1 !== playbackManager.getSupportedCommands().indexOf("EndSession") ? require(["dialog"], function(dialog) { - var menuItems = []; - menuItems.push({ - name: globalize.translate("sharedcomponents#Yes"), - id: "yes" - }), menuItems.push({ - name: globalize.translate("sharedcomponents#No"), - id: "no" - }), dialog({ - buttons: menuItems, - text: globalize.translate("sharedcomponents#ConfirmEndPlayerSession", currentDeviceName) - }).then(function(id) { - switch (id) { - case "yes": - playbackManager.getCurrentPlayer().endSession(), playbackManager.setDefaultPlayerActive(); - break; - case "no": - playbackManager.setDefaultPlayerActive() - } - }) - }) : playbackManager.setDefaultPlayerActive() + + if (playbackManager.getSupportedCommands().indexOf('EndSession') !== -1) { + + require(['dialog'], function (dialog) { + + var menuItems = []; + + menuItems.push({ + name: globalize.translate('sharedcomponents#Yes'), + id: 'yes' + }); + menuItems.push({ + name: globalize.translate('sharedcomponents#No'), + id: 'no' + }); + + dialog({ + buttons: menuItems, + //positionTo: positionTo, + text: globalize.translate('sharedcomponents#ConfirmEndPlayerSession', currentDeviceName) + + }).then(function (id) { + switch (id) { + + case 'yes': + playbackManager.getCurrentPlayer().endSession(); + playbackManager.setDefaultPlayerActive(); + break; + case 'no': + playbackManager.setDefaultPlayerActive(); + break; + default: + break; + } + }); + + }); + + + } else { + + playbackManager.setDefaultPlayerActive(); + } } function showActivePlayerMenuInternal(dialogHelper, playerInfo) { - var html = "", - dialogOptions = { - removeOnClose: !0 - }; - dialogOptions.modal = !1, dialogOptions.entryAnimationDuration = 160, dialogOptions.exitAnimationDuration = 160, dialogOptions.autoFocus = !1; + + var html = ''; + + var dialogOptions = { + removeOnClose: true + }; + + dialogOptions.modal = false; + dialogOptions.entryAnimationDuration = 160; + dialogOptions.exitAnimationDuration = 160; + dialogOptions.autoFocus = false; + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("promptDialog"); - var currentDeviceName = playerInfo.deviceName || playerInfo.name; - if (html += '
', html += '

', html += currentDeviceName, html += "

", html += "
", -1 !== playerInfo.supportedCommands.indexOf("DisplayContent")) { + + dlg.classList.add('promptDialog'); + + var currentDeviceName = (playerInfo.deviceName || playerInfo.name); + + html += '
'; + html += '

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

'; + + html += '
'; + + if (playerInfo.supportedCommands.indexOf('DisplayContent') !== -1) { + html += '" + var checkedHtml = playbackManager.enableDisplayMirroring() ? ' checked' : ''; + html += ''; + html += '' + globalize.translate('sharedcomponents#EnableDisplayMirroring') + ''; + html += ''; } - html += "
", html += '
', html += '", html += '", html += '", html += "
", html += "
", dlg.innerHTML = html; - var chkMirror = dlg.querySelector(".chkMirror"); - chkMirror && chkMirror.addEventListener("change", onMirrorChange); - var destination = "", - btnRemoteControl = dlg.querySelector(".btnRemoteControl"); - btnRemoteControl && btnRemoteControl.addEventListener("click", function() { - destination = "nowplaying", dialogHelper.close(dlg) - }), dlg.querySelector(".btnDisconnect").addEventListener("click", function() { - destination = "disconnectFromPlayer", dialogHelper.close(dlg) - }), dlg.querySelector(".btnCancel").addEventListener("click", function() { - dialogHelper.close(dlg) - }), dialogHelper.open(dlg).then(function() { - "nowplaying" === destination ? appRouter.showNowPlaying() : "disconnectFromPlayer" === destination && disconnectFromPlayer(currentDeviceName) - }, emptyCallback) + + html += '
'; + + html += '
'; + + html += ''; + html += ''; + html += ''; + html += '
'; + + html += '
'; + dlg.innerHTML = html; + + var chkMirror = dlg.querySelector('.chkMirror'); + + if (chkMirror) { + chkMirror.addEventListener('change', onMirrorChange); + } + + var destination = ''; + + var btnRemoteControl = dlg.querySelector('.btnRemoteControl'); + if (btnRemoteControl) { + btnRemoteControl.addEventListener('click', function () { + destination = 'nowplaying'; + dialogHelper.close(dlg); + }); + } + + dlg.querySelector('.btnDisconnect').addEventListener('click', function () { + destination = 'disconnectFromPlayer'; + dialogHelper.close(dlg); + }); + + dlg.querySelector('.btnCancel').addEventListener('click', function () { + dialogHelper.close(dlg); + }); + + dialogHelper.open(dlg).then(function () { + if (destination === 'nowplaying') { + appRouter.showNowPlaying(); + } + else if (destination === 'disconnectFromPlayer') { + disconnectFromPlayer(currentDeviceName); + } + }, emptyCallback); } function onMirrorChange() { - playbackManager.enableDisplayMirroring(this.checked) + playbackManager.enableDisplayMirroring(this.checked); } - return document.addEventListener("viewshow", function(e) { - var state = e.detail.state || {}, - item = state.item; - if (item && item.ServerId) return void mirrorIfEnabled({ - item: item - }) - }), events.on(appSettings, "change", function(e, name) { - "displaymirror" === name && mirrorIfEnabled() - }), events.on(playbackManager, "pairing", function(e) { - loading.show() - }), events.on(playbackManager, "paired", function(e) { - loading.hide() - }), events.on(playbackManager, "pairerror", function(e) { - loading.hide() - }), { + + document.addEventListener('viewshow', function (e) { + + var state = e.detail.state || {}; + var item = state.item; + + if (item && item.ServerId) { + mirrorIfEnabled({ + item: item + }); + return; + } + }); + + events.on(appSettings, 'change', function (e, name) { + if (name === 'displaymirror') { + mirrorIfEnabled(); + } + }); + + events.on(playbackManager, 'pairing', function (e) { + loading.show(); + }); + + events.on(playbackManager, 'paired', function (e) { + loading.hide(); + }); + + events.on(playbackManager, 'pairerror', function (e) { + loading.hide(); + }); + + return { show: showPlayerSelection - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/playback/playersettingsmenu.js b/src/bower_components/emby-webcomponents/playback/playersettingsmenu.js index f75ad2a65b..00d05d9d2b 100644 --- a/src/bower_components/emby-webcomponents/playback/playersettingsmenu.js +++ b/src/bower_components/emby-webcomponents/playback/playersettingsmenu.js @@ -1,195 +1,315 @@ -define(["connectionManager", "actionsheet", "datetime", "playbackManager", "globalize", "appSettings", "qualityoptions"], function(connectionManager, actionsheet, datetime, playbackManager, globalize, appSettings, qualityoptions) { - "use strict"; +define(['connectionManager', 'actionsheet', 'datetime', 'playbackManager', 'globalize', 'appSettings', 'qualityoptions'], function (connectionManager, actionsheet, datetime, playbackManager, globalize, appSettings, qualityoptions) { + 'use strict'; function showQualityMenu(player, btn) { - var videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function(stream) { - return "Video" === stream.Type - })[0], - videoWidth = videoStream ? videoStream.Width : null, - options = qualityoptions.getVideoQualityOptions({ - currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player), - isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player), - videoWidth: videoWidth, - enableAuto: !0 - }), - menuItems = options.map(function(o) { - var opt = { - name: o.name, - id: o.bitrate, - asideText: o.secondaryText - }; - return o.selected && (opt.selected = !0), opt - }), - selectedId = options.filter(function(o) { - return o.selected - }); - return selectedId = selectedId.length ? selectedId[0].bitrate : null, actionsheet.show({ - items: menuItems, - positionTo: btn - }).then(function(id) { - var bitrate = parseInt(id); - bitrate !== selectedId && playbackManager.setMaxStreamingBitrate({ - enableAutomaticBitrateDetection: !bitrate, - maxBitrate: bitrate - }, player) - }) - } - function showRepeatModeMenu(player, btn) { - var menuItems = [], - currentValue = playbackManager.getRepeatMode(player); - return menuItems.push({ - name: globalize.translate("sharedcomponents#RepeatAll"), - id: "RepeatAll", - selected: "RepeatAll" === currentValue - }), menuItems.push({ - name: globalize.translate("sharedcomponents#RepeatOne"), - id: "RepeatOne", - selected: "RepeatOne" === currentValue - }), menuItems.push({ - name: globalize.translate("sharedcomponents#None"), - id: "RepeatNone", - selected: "RepeatNone" === currentValue - }), actionsheet.show({ - items: menuItems, - positionTo: btn - }).then(function(mode) { - mode && playbackManager.setRepeatMode(mode, player) - }) - } + var videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) { + return stream.Type === "Video"; + })[0]; + var videoWidth = videoStream ? videoStream.Width : null; - function getQualitySecondaryText(player) { - var state = playbackManager.getPlayerState(player), - videoStream = (playbackManager.enableAutomaticBitrateDetection(player), playbackManager.getMaxStreamingBitrate(player), playbackManager.currentMediaSource(player).MediaStreams.filter(function(stream) { - return "Video" === stream.Type - })[0]), - videoWidth = videoStream ? videoStream.Width : null, - options = qualityoptions.getVideoQualityOptions({ - currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player), - isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player), - videoWidth: videoWidth, - enableAuto: !0 - }), - selectedOption = (options.map(function(o) { - var opt = { - name: o.name, - id: o.bitrate, - asideText: o.secondaryText - }; - return o.selected && (opt.selected = !0), opt - }), options.filter(function(o) { - return o.selected - })); - if (!selectedOption.length) return null; - selectedOption = selectedOption[0]; - var text = selectedOption.name; - return selectedOption.autoText && (state.PlayState && "Transcode" !== state.PlayState.PlayMethod ? text += " - Direct" : text += " " + selectedOption.autoText), text - } + var options = qualityoptions.getVideoQualityOptions({ + currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player), + isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player), + videoWidth: videoWidth, + enableAuto: true + }); + + var menuItems = options.map(function (o) { + + var opt = { + name: o.name, + id: o.bitrate, + asideText: o.secondaryText + }; + + if (o.selected) { + opt.selected = true; + } + + return opt; + }); + + var selectedId = options.filter(function (o) { + return o.selected; + }); + + selectedId = selectedId.length ? selectedId[0].bitrate : null; - function showAspectRatioMenu(player, btn) { - var currentId = playbackManager.getAspectRatio(player), - menuItems = playbackManager.getSupportedAspectRatios(player).map(function(i) { - return { - id: i.id, - name: i.name, - selected: i.id === currentId - } - }); return actionsheet.show({ items: menuItems, positionTo: btn - }).then(function(id) { - return id ? (playbackManager.setAspectRatio(id, player), Promise.resolve()) : Promise.reject() - }) + + }).then(function (id) { + var bitrate = parseInt(id); + if (bitrate !== selectedId) { + + playbackManager.setMaxStreamingBitrate({ + + enableAutomaticBitrateDetection: bitrate ? false : true, + maxBitrate: bitrate + + }, player); + } + }); + } + + function showRepeatModeMenu(player, btn) { + + var menuItems = []; + + var currentValue = playbackManager.getRepeatMode(player); + + menuItems.push({ + name: globalize.translate('sharedcomponents#RepeatAll'), + id: 'RepeatAll', + selected: currentValue === 'RepeatAll' + }); + menuItems.push({ + name: globalize.translate('sharedcomponents#RepeatOne'), + id: 'RepeatOne', + selected: currentValue === 'RepeatOne' + }); + + menuItems.push({ + name: globalize.translate('sharedcomponents#None'), + id: 'RepeatNone', + selected: currentValue === 'RepeatNone' + }); + + return actionsheet.show({ + items: menuItems, + positionTo: btn + + }).then(function (mode) { + + if (mode) { + playbackManager.setRepeatMode(mode, player); + } + }); + } + + function getQualitySecondaryText(player) { + + var state = playbackManager.getPlayerState(player); + + var isAutoEnabled = playbackManager.enableAutomaticBitrateDetection(player); + var currentMaxBitrate = playbackManager.getMaxStreamingBitrate(player); + + var videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) { + return stream.Type === "Video"; + })[0]; + var videoWidth = videoStream ? videoStream.Width : null; + + var options = qualityoptions.getVideoQualityOptions({ + currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player), + isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player), + videoWidth: videoWidth, + enableAuto: true + }); + + var menuItems = options.map(function (o) { + + var opt = { + name: o.name, + id: o.bitrate, + asideText: o.secondaryText + }; + + if (o.selected) { + opt.selected = true; + } + + return opt; + }); + + var selectedOption = options.filter(function (o) { + return o.selected; + }); + + if (!selectedOption.length) { + return null; + } + + selectedOption = selectedOption[0]; + + var text = selectedOption.name; + + if (selectedOption.autoText) { + if (state.PlayState && state.PlayState.PlayMethod !== 'Transcode') { + text += ' - Direct'; + } else { + text += ' ' + selectedOption.autoText; + } + } + + return text; + } + + function showAspectRatioMenu(player, btn) { + + // Each has name/id + var currentId = playbackManager.getAspectRatio(player); + var menuItems = playbackManager.getSupportedAspectRatios(player).map(function (i) { + return { + id: i.id, + name: i.name, + selected: i.id === currentId + }; + }); + + return actionsheet.show({ + + items: menuItems, + positionTo: btn + + }).then(function (id) { + + if (id) { + playbackManager.setAspectRatio(id, player); + return Promise.resolve(); + } + + return Promise.reject(); + }); } function showWithUser(options, player, user) { - var supportedCommands = playbackManager.getSupportedCommands(player), - menuItems = (options.mediaType, []); - if (-1 !== supportedCommands.indexOf("SetAspectRatio")) { - var currentAspectRatioId = playbackManager.getAspectRatio(player), - currentAspectRatio = playbackManager.getSupportedAspectRatios(player).filter(function(i) { - return i.id === currentAspectRatioId - })[0]; + + var supportedCommands = playbackManager.getSupportedCommands(player); + + var mediaType = options.mediaType; + + var menuItems = []; + + if (supportedCommands.indexOf('SetAspectRatio') !== -1) { + + var currentAspectRatioId = playbackManager.getAspectRatio(player); + var currentAspectRatio = playbackManager.getSupportedAspectRatios(player).filter(function (i) { + return i.id === currentAspectRatioId; + })[0]; + menuItems.push({ - name: globalize.translate("sharedcomponents#AspectRatio"), - id: "aspectratio", + name: globalize.translate('sharedcomponents#AspectRatio'), + id: 'aspectratio', asideText: currentAspectRatio ? currentAspectRatio.name : null - }) + }); } - if (menuItems.push({ - name: globalize.translate("sharedcomponents#PlaybackSettings"), - id: "playbacksettings" - }), user && user.Policy.EnableVideoPlaybackTranscoding) { + + menuItems.push({ + name: globalize.translate('sharedcomponents#PlaybackSettings'), + id: 'playbacksettings' + }); + + if (user && user.Policy.EnableVideoPlaybackTranscoding) { var secondaryQualityText = getQualitySecondaryText(player); + menuItems.push({ - name: globalize.translate("sharedcomponents#Quality"), - id: "quality", + name: globalize.translate('sharedcomponents#Quality'), + id: 'quality', asideText: secondaryQualityText - }) + }); } + var repeatMode = playbackManager.getRepeatMode(player); - return -1 !== supportedCommands.indexOf("SetRepeatMode") && playbackManager.currentMediaSource(player).RunTimeTicks && menuItems.push({ - name: globalize.translate("sharedcomponents#RepeatMode"), - id: "repeatmode", - asideText: "RepeatNone" === repeatMode ? globalize.translate("sharedcomponents#None") : globalize.translate("sharedcomponents#" + repeatMode) - }), options.stats && menuItems.push({ - name: globalize.translate("sharedcomponents#StatsForNerds"), - id: "stats", - asideText: null - }), menuItems.push({ - name: globalize.translate("sharedcomponents#SubtitleSettings"), - id: "subtitlesettings" - }), actionsheet.show({ + + if (supportedCommands.indexOf('SetRepeatMode') !== -1 && playbackManager.currentMediaSource(player).RunTimeTicks) { + + menuItems.push({ + name: globalize.translate('sharedcomponents#RepeatMode'), + id: 'repeatmode', + asideText: repeatMode === 'RepeatNone' ? globalize.translate('sharedcomponents#None') : globalize.translate('sharedcomponents#' + repeatMode) + }); + } + + if (options.stats) { + + menuItems.push({ + name: globalize.translate('sharedcomponents#StatsForNerds'), + id: 'stats', + asideText: null + }); + } + + menuItems.push({ + name: globalize.translate('sharedcomponents#SubtitleSettings'), + id: 'subtitlesettings' + }); + + return actionsheet.show({ + items: menuItems, positionTo: options.positionTo - }).then(function(id) { - return handleSelectedOption(id, options, player) - }) + + }).then(function (id) { + + return handleSelectedOption(id, options, player); + }); } function show(options) { - var player = options.player, - currentItem = playbackManager.currentItem(player); - return currentItem && currentItem.ServerId ? connectionManager.getApiClient(currentItem.ServerId).getCurrentUser().then(function(user) { - return showWithUser(options, player, user) - }) : showWithUser(options, player, null) + + var player = options.player; + + var currentItem = playbackManager.currentItem(player); + + if (!currentItem || !currentItem.ServerId) { + return showWithUser(options, player, null); + } + + var apiClient = connectionManager.getApiClient(currentItem.ServerId); + + return apiClient.getCurrentUser().then(function (user) { + return showWithUser(options, player, user); + }); } function alertText(text) { - return new Promise(function(resolve, reject) { - require(["alert"], function(alert) { - alert(text).then(resolve) - }) - }) + + return new Promise(function (resolve, reject) { + + require(['alert'], function (alert) { + + alert(text).then(resolve); + }); + }); } function showSubtitleSettings(player, btn) { - return alertText(globalize.translate("sharedcomponents#SubtitleSettingsIntro")) + return alertText(globalize.translate('sharedcomponents#SubtitleSettingsIntro')); } function showPlaybackSettings(player, btn) { - return alertText(globalize.translate("sharedcomponents#PlaybackSettingsIntro")) + return alertText(globalize.translate('sharedcomponents#PlaybackSettingsIntro')); } function handleSelectedOption(id, options, player) { + switch (id) { - case "quality": + + case 'quality': return showQualityMenu(player, options.positionTo); - case "aspectratio": + case 'aspectratio': return showAspectRatioMenu(player, options.positionTo); - case "repeatmode": + case 'repeatmode': return showRepeatModeMenu(player, options.positionTo); - case "subtitlesettings": + case 'subtitlesettings': return showSubtitleSettings(player, options.positionTo); - case "playbacksettings": + case 'playbacksettings': return showPlaybackSettings(player, options.positionTo); - case "stats": - return options.onOption && options.onOption("stats"), Promise.resolve() + case 'stats': + if (options.onOption) { + options.onOption('stats'); + } + return Promise.resolve(); + default: + break; } - return Promise.reject() + + return Promise.reject(); } + return { show: show - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/playback/playmethodhelper.js b/src/bower_components/emby-webcomponents/playback/playmethodhelper.js index 56e5f45dfa..2caae0d4cc 100644 --- a/src/bower_components/emby-webcomponents/playback/playmethodhelper.js +++ b/src/bower_components/emby-webcomponents/playback/playmethodhelper.js @@ -1,10 +1,27 @@ -define([], function() { - "use strict"; +define([], function () { + 'use strict'; function getDisplayPlayMethod(session) { - return session.NowPlayingItem ? session.TranscodingInfo && session.TranscodingInfo.IsVideoDirect ? "DirectStream" : "Transcode" === session.PlayState.PlayMethod ? "Transcode" : "DirectStream" === session.PlayState.PlayMethod ? "DirectPlay" : "DirectPlay" === session.PlayState.PlayMethod ? "DirectPlay" : void 0 : null + + if (!session.NowPlayingItem) { + return null; + } + + if (session.TranscodingInfo && session.TranscodingInfo.IsVideoDirect) { + return 'DirectStream'; + } + else if (session.PlayState.PlayMethod === 'Transcode') { + return 'Transcode'; + } + else if (session.PlayState.PlayMethod === 'DirectStream') { + return 'DirectPlay'; + } + else if (session.PlayState.PlayMethod === 'DirectPlay') { + return 'DirectPlay'; + } } + return { getDisplayPlayMethod: getDisplayPlayMethod - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/playback/playqueuemanager.js b/src/bower_components/emby-webcomponents/playback/playqueuemanager.js index 0962bfc4b0..a804af0ec5 100644 --- a/src/bower_components/emby-webcomponents/playback/playqueuemanager.js +++ b/src/bower_components/emby-webcomponents/playback/playqueuemanager.js @@ -1,103 +1,233 @@ -define([], function() { - "use strict"; +define([], function () { + 'use strict'; + var currentId = 0; function addUniquePlaylistItemId(item) { - item.PlaylistItemId || (item.PlaylistItemId = "playlistItem" + currentId, currentId++) + + if (!item.PlaylistItemId) { + + item.PlaylistItemId = "playlistItem" + currentId; + currentId++; + } } function findPlaylistIndex(playlistItemId, list) { - for (var i = 0, length = list.length; i < length; i++) - if (list[i].PlaylistItemId === playlistItemId) return i; - return -1 + + for (var i = 0, length = list.length; i < length; i++) { + if (list[i].PlaylistItemId === playlistItemId) { + return i; + } + } + + return -1; } function PlayQueueManager() { - this._playlist = [], this._repeatMode = "RepeatNone" + + this._playlist = []; + this._repeatMode = 'RepeatNone'; } + PlayQueueManager.prototype.getPlaylist = function () { + return this._playlist.slice(0); + }; + + PlayQueueManager.prototype.setPlaylist = function (items) { + + items = items.slice(0); + + for (var i = 0, length = items.length; i < length; i++) { + + addUniquePlaylistItemId(items[i]); + } + + this._currentPlaylistItemId = null; + this._playlist = items; + this._repeatMode = 'RepeatNone'; + }; + + PlayQueueManager.prototype.queue = function (items) { + + for (var i = 0, length = items.length; i < length; i++) { + + addUniquePlaylistItemId(items[i]); + + this._playlist.push(items[i]); + } + }; + function arrayInsertAt(destArray, pos, arrayToInsert) { var args = []; - args.push(pos), args.push(0), args = args.concat(arrayToInsert), destArray.splice.apply(destArray, args) + args.push(pos); // where to insert + args.push(0); // nothing to remove + args = args.concat(arrayToInsert); // add on array to insert + destArray.splice.apply(destArray, args); // splice it in } - function moveInArray(array, from, to) { - array.splice(to, 0, array.splice(from, 1)[0]) - } - var currentId = 0; - return PlayQueueManager.prototype.getPlaylist = function() { - return this._playlist.slice(0) - }, PlayQueueManager.prototype.setPlaylist = function(items) { - items = items.slice(0); - for (var i = 0, length = items.length; i < length; i++) addUniquePlaylistItemId(items[i]); - this._currentPlaylistItemId = null, this._playlist = items, this._repeatMode = "RepeatNone" - }, PlayQueueManager.prototype.queue = function(items) { - for (var i = 0, length = items.length; i < length; i++) addUniquePlaylistItemId(items[i]), this._playlist.push(items[i]) - }, PlayQueueManager.prototype.queueNext = function(items) { + PlayQueueManager.prototype.queueNext = function (items) { + var i, length; - for (i = 0, length = items.length; i < length; i++) addUniquePlaylistItemId(items[i]); - var currentIndex = this.getCurrentPlaylistIndex(); - 1 === currentIndex ? currentIndex = this._playlist.length : currentIndex++, arrayInsertAt(this._playlist, currentIndex, items) - }, PlayQueueManager.prototype.getCurrentPlaylistIndex = function() { - return findPlaylistIndex(this.getCurrentPlaylistItemId(), this._playlist) - }, PlayQueueManager.prototype.getCurrentItem = function() { - var index = findPlaylistIndex(this.getCurrentPlaylistItemId(), this._playlist); - return -1 === index ? null : this._playlist[index] - }, PlayQueueManager.prototype.getCurrentPlaylistItemId = function() { - return this._currentPlaylistItemId - }, PlayQueueManager.prototype.setPlaylistState = function(playlistItemId, playlistIndex) { - this._currentPlaylistItemId = playlistItemId - }, PlayQueueManager.prototype.setPlaylistIndex = function(playlistIndex) { - playlistIndex < 0 ? this.setPlaylistState(null) : this.setPlaylistState(this._playlist[playlistIndex].PlaylistItemId) - }, PlayQueueManager.prototype.removeFromPlaylist = function(playlistItemIds) { - var playlist = this.getPlaylist(); - if (playlist.length <= playlistItemIds.length) return { - result: "empty" - }; - var currentPlaylistItemId = this.getCurrentPlaylistItemId(), - isCurrentIndex = -1 !== playlistItemIds.indexOf(currentPlaylistItemId); - return this._playlist = playlist.filter(function(item) { - return -1 === playlistItemIds.indexOf(item.PlaylistItemId) - }), { - result: "removed", - isCurrentIndex: isCurrentIndex + + for (i = 0, length = items.length; i < length; i++) { + + addUniquePlaylistItemId(items[i]); } - }, PlayQueueManager.prototype.movePlaylistItem = function(playlistItemId, newIndex) { - for (var oldIndex, playlist = this.getPlaylist(), i = 0, length = playlist.length; i < length; i++) + + var currentIndex = this.getCurrentPlaylistIndex(); + + if (currentIndex === -1) { + currentIndex = this._playlist.length; + } else { + currentIndex++; + } + + arrayInsertAt(this._playlist, currentIndex, items); + }; + + PlayQueueManager.prototype.getCurrentPlaylistIndex = function () { + + return findPlaylistIndex(this.getCurrentPlaylistItemId(), this._playlist); + }; + + PlayQueueManager.prototype.getCurrentItem = function () { + + var index = findPlaylistIndex(this.getCurrentPlaylistItemId(), this._playlist); + + return index === -1 ? null : this._playlist[index]; + }; + + PlayQueueManager.prototype.getCurrentPlaylistItemId = function () { + return this._currentPlaylistItemId; + }; + + PlayQueueManager.prototype.setPlaylistState = function (playlistItemId, playlistIndex) { + + this._currentPlaylistItemId = playlistItemId; + }; + + PlayQueueManager.prototype.setPlaylistIndex = function (playlistIndex) { + + if (playlistIndex < 0) { + this.setPlaylistState(null); + } else { + this.setPlaylistState(this._playlist[playlistIndex].PlaylistItemId); + } + }; + + PlayQueueManager.prototype.removeFromPlaylist = function (playlistItemIds) { + + var playlist = this.getPlaylist(); + + if (playlist.length <= playlistItemIds.length) { + return { + result: 'empty' + }; + } + + var currentPlaylistItemId = this.getCurrentPlaylistItemId(); + var isCurrentIndex = playlistItemIds.indexOf(currentPlaylistItemId) !== -1; + + this._playlist = playlist.filter(function (item) { + return playlistItemIds.indexOf(item.PlaylistItemId) === -1; + }); + + return { + result: 'removed', + isCurrentIndex: isCurrentIndex + }; + }; + + function moveInArray(array, from, to) { + array.splice(to, 0, array.splice(from, 1)[0]); + } + + PlayQueueManager.prototype.movePlaylistItem = function (playlistItemId, newIndex) { + + var playlist = this.getPlaylist(); + + var oldIndex; + for (var i = 0, length = playlist.length; i < length; i++) { if (playlist[i].PlaylistItemId === playlistItemId) { oldIndex = i; - break - } if (-1 === oldIndex || oldIndex === newIndex) return { - result: "noop" - }; - if (newIndex >= playlist.length) throw new Error("newIndex out of bounds"); - return moveInArray(playlist, oldIndex, newIndex), this._playlist = playlist, { - result: "moved", + break; + } + } + + if (oldIndex === -1 || oldIndex === newIndex) { + return { + result: 'noop' + }; + } + + if (newIndex >= playlist.length) { + throw new Error('newIndex out of bounds'); + } + + moveInArray(playlist, oldIndex, newIndex); + + this._playlist = playlist; + + return { + result: 'moved', playlistItemId: playlistItemId, newIndex: newIndex - } - }, PlayQueueManager.prototype.reset = function() { - this._playlist = [], this._currentPlaylistItemId = null, this._repeatMode = "RepeatNone" - }, PlayQueueManager.prototype.setRepeatMode = function(value) { - this._repeatMode = value - }, PlayQueueManager.prototype.getRepeatMode = function() { - return this._repeatMode - }, PlayQueueManager.prototype.getNextItemInfo = function() { - var newIndex, playlist = this.getPlaylist(), - playlistLength = playlist.length; + }; + }; + + PlayQueueManager.prototype.reset = function () { + + this._playlist = []; + this._currentPlaylistItemId = null; + this._repeatMode = 'RepeatNone'; + }; + + PlayQueueManager.prototype.setRepeatMode = function (value) { + + this._repeatMode = value; + }; + + PlayQueueManager.prototype.getRepeatMode = function () { + + return this._repeatMode; + }; + + PlayQueueManager.prototype.getNextItemInfo = function () { + + var newIndex; + var playlist = this.getPlaylist(); + var playlistLength = playlist.length; + switch (this.getRepeatMode()) { - case "RepeatOne": + + case 'RepeatOne': newIndex = this.getCurrentPlaylistIndex(); break; - case "RepeatAll": - newIndex = this.getCurrentPlaylistIndex() + 1, newIndex >= playlistLength && (newIndex = 0); + case 'RepeatAll': + newIndex = this.getCurrentPlaylistIndex() + 1; + if (newIndex >= playlistLength) { + newIndex = 0; + } break; default: - newIndex = this.getCurrentPlaylistIndex() + 1 + newIndex = this.getCurrentPlaylistIndex() + 1; + break; } - if (newIndex < 0 || newIndex >= playlistLength) return null; + + if (newIndex < 0 || newIndex >= playlistLength) { + return null; + } + var item = playlist[newIndex]; - return item ? { + + if (!item) { + return null; + } + + return { item: item, index: newIndex - } : null - }, PlayQueueManager + }; + }; + + return PlayQueueManager; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/playback/remotecontrolautoplay.js b/src/bower_components/emby-webcomponents/playback/remotecontrolautoplay.js index 6cc1929497..5fecf48537 100644 --- a/src/bower_components/emby-webcomponents/playback/remotecontrolautoplay.js +++ b/src/bower_components/emby-webcomponents/playback/remotecontrolautoplay.js @@ -1,22 +1,47 @@ -define(["events", "playbackManager"], function(events, playbackManager) { - "use strict"; +define(['events', 'playbackManager'], function (events, playbackManager) { + 'use strict'; function transferPlayback(oldPlayer, newPlayer) { - var state = playbackManager.getPlayerState(oldPlayer), - item = state.NowPlayingItem; - if (item) { - var playState = state.PlayState || {}, - resumePositionTicks = playState.PositionTicks || 0; - playbackManager.stop(oldPlayer).then(function() { - playbackManager.play({ - ids: [item.Id], - serverId: item.ServerId, - startPositionTicks: resumePositionTicks - }, newPlayer) - }) + + var state = playbackManager.getPlayerState(oldPlayer); + + var item = state.NowPlayingItem; + + if (!item) { + return; } + + var playState = state.PlayState || {}; + var resumePositionTicks = playState.PositionTicks || 0; + + playbackManager.stop(oldPlayer).then(function () { + + playbackManager.play({ + ids: [item.Id], + serverId: item.ServerId, + startPositionTicks: resumePositionTicks + + }, newPlayer); + }); } - events.on(playbackManager, "playerchange", function(e, newPlayer, newTarget, oldPlayer) { - if (oldPlayer && newPlayer) return oldPlayer.isLocalPlayer ? newPlayer.isLocalPlayer ? void console.log("Skipping remote control autoplay because newPlayer is a local player") : void transferPlayback(oldPlayer, newPlayer) : void console.log("Skipping remote control autoplay because oldPlayer is not a local player") - }) + + events.on(playbackManager, 'playerchange', function (e, newPlayer, newTarget, oldPlayer) { + + if (!oldPlayer || !newPlayer) { + return; + } + + if (!oldPlayer.isLocalPlayer) { + console.log('Skipping remote control autoplay because oldPlayer is not a local player'); + return; + } + + if (newPlayer.isLocalPlayer) { + console.log('Skipping remote control autoplay because newPlayer is a local player'); + return; + } + + transferPlayback(oldPlayer, newPlayer); + }); + }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/playback/volumeosd.js b/src/bower_components/emby-webcomponents/playback/volumeosd.js index 0987947f6f..c967a34fe2 100644 --- a/src/bower_components/emby-webcomponents/playback/volumeosd.js +++ b/src/bower_components/emby-webcomponents/playback/volumeosd.js @@ -1,63 +1,158 @@ -define(["events", "playbackManager", "dom", "browser", "css!./iconosd", "material-icons"], function(events, playbackManager, dom, browser) { - "use strict"; +define(['events', 'playbackManager', 'dom', 'browser', 'css!./iconosd', 'material-icons'], function (events, playbackManager, dom, browser) { + 'use strict'; + + var currentPlayer; + var osdElement; + var iconElement; + var progressElement; + + var enableAnimation; function getOsdElementHtml() { - var html = ""; - return html += '', html += '
' + var html = ''; + + html += ''; + + html += '
'; + + return html; } function ensureOsdElement() { + var elem = osdElement; - elem || (enableAnimation = browser.supportsCssAnimation(), elem = document.createElement("div"), elem.classList.add("hide"), elem.classList.add("iconOsd"), elem.classList.add("iconOsd-hidden"), elem.classList.add("volumeOsd"), elem.innerHTML = getOsdElementHtml(), iconElement = elem.querySelector("i"), progressElement = elem.querySelector(".iconOsdProgressInner"), document.body.appendChild(elem), osdElement = elem) + if (!elem) { + + enableAnimation = browser.supportsCssAnimation(); + + elem = document.createElement('div'); + elem.classList.add('hide'); + elem.classList.add('iconOsd'); + elem.classList.add('iconOsd-hidden'); + elem.classList.add('volumeOsd'); + elem.innerHTML = getOsdElementHtml(); + + iconElement = elem.querySelector('i'); + progressElement = elem.querySelector('.iconOsdProgressInner'); + + document.body.appendChild(elem); + osdElement = elem; + } } function onHideComplete() { - this.classList.add("hide") + this.classList.add('hide'); } + var hideTimeout; function showOsd() { + clearHideTimeout(); + var elem = osdElement; + dom.removeEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { - once: !0 - }), elem.classList.remove("hide"), elem.offsetWidth, requestAnimationFrame(function() { - elem.classList.remove("iconOsd-hidden"), hideTimeout = setTimeout(hideOsd, 3e3) - }) + once: true + }); + + elem.classList.remove('hide'); + + // trigger reflow + void elem.offsetWidth; + + requestAnimationFrame(function () { + elem.classList.remove('iconOsd-hidden'); + + hideTimeout = setTimeout(hideOsd, 3000); + }); } function clearHideTimeout() { - hideTimeout && (clearTimeout(hideTimeout), hideTimeout = null) + if (hideTimeout) { + clearTimeout(hideTimeout); + hideTimeout = null; + } } function hideOsd() { + clearHideTimeout(); + var elem = osdElement; - elem && (enableAnimation ? (elem.offsetWidth, requestAnimationFrame(function() { - elem.classList.add("iconOsd-hidden"), dom.addEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { - once: !0 - }) - })) : onHideComplete.call(elem)) + if (elem) { + + if (enableAnimation) { + // trigger reflow + void elem.offsetWidth; + + requestAnimationFrame(function () { + elem.classList.add('iconOsd-hidden'); + + dom.addEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { + once: true + }); + }); + } else { + onHideComplete.call(elem); + } + } } function updatePlayerVolumeState(isMuted, volume) { - iconElement && (iconElement.innerHTML = isMuted ? "" : ""), progressElement && (progressElement.style.width = (volume || 0) + "%") + + if (iconElement) { + iconElement.innerHTML = isMuted ? '' : ''; + } + if (progressElement) { + progressElement.style.width = (volume || 0) + '%'; + } } function releaseCurrentPlayer() { + var player = currentPlayer; - player && (events.off(player, "volumechange", onVolumeChanged), events.off(player, "playbackstop", hideOsd), currentPlayer = null) + + if (player) { + events.off(player, 'volumechange', onVolumeChanged); + events.off(player, 'playbackstop', hideOsd); + currentPlayer = null; + } } function onVolumeChanged(e) { + var player = this; - ensureOsdElement(), updatePlayerVolumeState(player.isMuted(), player.getVolume()), showOsd() + + ensureOsdElement(); + + updatePlayerVolumeState(player.isMuted(), player.getVolume()); + + showOsd(); } function bindToPlayer(player) { - player !== currentPlayer && (releaseCurrentPlayer(), currentPlayer = player, player && (hideOsd(), events.on(player, "volumechange", onVolumeChanged), events.on(player, "playbackstop", hideOsd))) + + if (player === currentPlayer) { + return; + } + + releaseCurrentPlayer(); + + currentPlayer = player; + + if (!player) { + return; + } + + hideOsd(); + events.on(player, 'volumechange', onVolumeChanged); + events.on(player, 'playbackstop', hideOsd); } - var currentPlayer, osdElement, iconElement, progressElement, enableAnimation, hideTimeout; - events.on(playbackManager, "playerchange", function() { - bindToPlayer(playbackManager.getCurrentPlayer()) - }), bindToPlayer(playbackManager.getCurrentPlayer()) + + events.on(playbackManager, 'playerchange', function () { + bindToPlayer(playbackManager.getCurrentPlayer()); + }); + + bindToPlayer(playbackManager.getCurrentPlayer()); + }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/playbacksettings/playbacksettings.js b/src/bower_components/emby-webcomponents/playbacksettings/playbacksettings.js index bb74207382..6e0cb5e30f 100644 --- a/src/bower_components/emby-webcomponents/playbacksettings/playbacksettings.js +++ b/src/bower_components/emby-webcomponents/playbacksettings/playbacksettings.js @@ -1,135 +1,345 @@ -define(["require", "browser", "appSettings", "apphost", "focusManager", "qualityoptions", "globalize", "loading", "connectionManager", "dom", "events", "emby-select", "emby-checkbox"], function(require, browser, appSettings, appHost, focusManager, qualityoptions, globalize, loading, connectionManager, dom, events) { +define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'qualityoptions', 'globalize', 'loading', 'connectionManager', 'dom', 'events', 'emby-select', 'emby-checkbox'], function (require, browser, appSettings, appHost, focusManager, qualityoptions, globalize, loading, connectionManager, dom, events) { "use strict"; function fillSkipLengths(select) { + var options = [5, 10, 15, 20, 25, 30]; - select.innerHTML = options.map(function(option) { + + select.innerHTML = options.map(function (option) { return { - name: globalize.translate("sharedcomponents#ValueSeconds", option), - value: 1e3 * option - } - }).map(function(o) { - return '" - }).join("") + name: globalize.translate('sharedcomponents#ValueSeconds', option), + value: option * 1000 + }; + }).map(function (o) { + return ''; + }).join(''); } function populateLanguages(select, languages) { + var html = ""; - html += ""; + + html += ""; + for (var i = 0, length = languages.length; i < length; i++) { + var culture = languages[i]; - html += "" + + html += ""; } - select.innerHTML = html + + select.innerHTML = html; } function setMaxBitrateIntoField(select, isInNetwork, mediatype) { - var options = "Audio" === mediatype ? qualityoptions.getAudioQualityOptions({ + + var options = mediatype === 'Audio' ? qualityoptions.getAudioQualityOptions({ + currentMaxBitrate: appSettings.maxStreamingBitrate(isInNetwork, mediatype), isAutomaticBitrateEnabled: appSettings.enableAutomaticBitrateDetection(isInNetwork, mediatype), - enableAuto: !0 + enableAuto: true + }) : qualityoptions.getVideoQualityOptions({ + currentMaxBitrate: appSettings.maxStreamingBitrate(isInNetwork, mediatype), isAutomaticBitrateEnabled: appSettings.enableAutomaticBitrateDetection(isInNetwork, mediatype), - enableAuto: !0 + enableAuto: true + }); - select.innerHTML = options.map(function(i) { - return '" - }).join(""), appSettings.enableAutomaticBitrateDetection(isInNetwork, mediatype) ? select.value = "" : select.value = appSettings.maxStreamingBitrate(isInNetwork, mediatype) + + select.innerHTML = options.map(function (i) { + + // render empty string instead of 0 for the auto option + return ''; + }).join(''); + + if (appSettings.enableAutomaticBitrateDetection(isInNetwork, mediatype)) { + select.value = ''; + } else { + select.value = appSettings.maxStreamingBitrate(isInNetwork, mediatype); + } } function fillChromecastQuality(select) { + var options = qualityoptions.getVideoQualityOptions({ + currentMaxBitrate: appSettings.maxChromecastBitrate(), isAutomaticBitrateEnabled: !appSettings.maxChromecastBitrate(), - enableAuto: !0 + enableAuto: true }); - select.innerHTML = options.map(function(i) { - return '" - }).join(""), select.value = appSettings.maxChromecastBitrate() || "" + + select.innerHTML = options.map(function (i) { + + // render empty string instead of 0 for the auto option + return ''; + }).join(''); + + select.value = appSettings.maxChromecastBitrate() || ''; } function setMaxBitrateFromField(select, isInNetwork, mediatype, value) { - select.value ? (appSettings.maxStreamingBitrate(isInNetwork, mediatype, select.value), appSettings.enableAutomaticBitrateDetection(isInNetwork, mediatype, !1)) : appSettings.enableAutomaticBitrateDetection(isInNetwork, mediatype, !0) + + if (select.value) { + appSettings.maxStreamingBitrate(isInNetwork, mediatype, select.value); + appSettings.enableAutomaticBitrateDetection(isInNetwork, mediatype, false); + } else { + appSettings.enableAutomaticBitrateDetection(isInNetwork, mediatype, true); + } } function showHideQualityFields(context, user, apiClient) { - if (user.Policy.EnableVideoPlaybackTranscoding ? context.querySelector(".videoQualitySection").classList.remove("hide") : context.querySelector(".videoQualitySection").classList.add("hide"), appHost.supports("multiserver")) return context.querySelector(".fldVideoInNetworkQuality").classList.remove("hide"), context.querySelector(".fldVideoInternetQuality").classList.remove("hide"), void(user.Policy.EnableAudioPlaybackTranscoding ? context.querySelector(".musicQualitySection").classList.remove("hide") : context.querySelector(".musicQualitySection").classList.add("hide")); - apiClient.getEndpointInfo().then(function(endpointInfo) { - endpointInfo.IsInNetwork ? (context.querySelector(".fldVideoInNetworkQuality").classList.remove("hide"), context.querySelector(".fldVideoInternetQuality").classList.add("hide"), context.querySelector(".musicQualitySection").classList.add("hide")) : (context.querySelector(".fldVideoInNetworkQuality").classList.add("hide"), context.querySelector(".fldVideoInternetQuality").classList.remove("hide"), user.Policy.EnableAudioPlaybackTranscoding ? context.querySelector(".musicQualitySection").classList.remove("hide") : context.querySelector(".musicQualitySection").classList.add("hide")) - }) + + if (user.Policy.EnableVideoPlaybackTranscoding) { + context.querySelector('.videoQualitySection').classList.remove('hide'); + } else { + context.querySelector('.videoQualitySection').classList.add('hide'); + } + + if (appHost.supports('multiserver')) { + + context.querySelector('.fldVideoInNetworkQuality').classList.remove('hide'); + context.querySelector('.fldVideoInternetQuality').classList.remove('hide'); + + if (user.Policy.EnableAudioPlaybackTranscoding) { + context.querySelector('.musicQualitySection').classList.remove('hide'); + } else { + context.querySelector('.musicQualitySection').classList.add('hide'); + } + + return; + } + + apiClient.getEndpointInfo().then(function (endpointInfo) { + + if (endpointInfo.IsInNetwork) { + + context.querySelector('.fldVideoInNetworkQuality').classList.remove('hide'); + + context.querySelector('.fldVideoInternetQuality').classList.add('hide'); + context.querySelector('.musicQualitySection').classList.add('hide'); + } else { + + context.querySelector('.fldVideoInNetworkQuality').classList.add('hide'); + + context.querySelector('.fldVideoInternetQuality').classList.remove('hide'); + + if (user.Policy.EnableAudioPlaybackTranscoding) { + context.querySelector('.musicQualitySection').classList.remove('hide'); + } else { + context.querySelector('.musicQualitySection').classList.add('hide'); + } + } + }); } function showOrHideEpisodesField(context, user, apiClient) { - if (browser.tizen || browser.web0s) return void context.querySelector(".fldEpisodeAutoPlay").classList.add("hide"); - context.querySelector(".fldEpisodeAutoPlay").classList.remove("hide") + + if (browser.tizen || browser.web0s) { + context.querySelector('.fldEpisodeAutoPlay').classList.add('hide'); + return; + } + + context.querySelector('.fldEpisodeAutoPlay').classList.remove('hide'); } function loadForm(context, user, userSettings, apiClient) { - var loggedInUserId = apiClient.getCurrentUserId(), - userId = user.Id; - showHideQualityFields(context, user, apiClient), apiClient.getCultures().then(function(allCultures) { - populateLanguages(context.querySelector("#selectAudioLanguage"), allCultures), context.querySelector("#selectAudioLanguage", context).value = user.Configuration.AudioLanguagePreference || "", context.querySelector(".chkEpisodeAutoPlay").checked = user.Configuration.EnableNextEpisodeAutoPlay || !1 - }), apiClient.getNamedConfiguration("cinemamode").then(function(cinemaConfig) { - cinemaConfig.EnableIntrosForMovies || cinemaConfig.EnableIntrosForEpisodes ? context.querySelector(".cinemaModeOptions").classList.remove("hide") : context.querySelector(".cinemaModeOptions").classList.add("hide") - }), appHost.supports("externalplayerintent") && userId === loggedInUserId ? context.querySelector(".fldExternalPlayer").classList.remove("hide") : context.querySelector(".fldExternalPlayer").classList.add("hide"), userId === loggedInUserId && (user.Policy.EnableVideoPlaybackTranscoding || user.Policy.EnableAudioPlaybackTranscoding) ? (context.querySelector(".qualitySections").classList.remove("hide"), appHost.supports("chromecast") && user.Policy.EnableVideoPlaybackTranscoding ? context.querySelector(".fldChromecastQuality").classList.remove("hide") : context.querySelector(".fldChromecastQuality").classList.add("hide")) : (context.querySelector(".qualitySections").classList.add("hide"), context.querySelector(".fldChromecastQuality").classList.add("hide")), browser.tizen || browser.web0s ? context.querySelector(".fldEnableNextVideoOverlay").classList.add("hide") : context.querySelector(".fldEnableNextVideoOverlay").classList.remove("hide"), context.querySelector(".chkPlayDefaultAudioTrack").checked = user.Configuration.PlayDefaultAudioTrack || !1, context.querySelector(".chkEnableCinemaMode").checked = userSettings.enableCinemaMode(), context.querySelector(".chkEnableNextVideoOverlay").checked = userSettings.enableNextVideoInfoOverlay(), context.querySelector(".chkExternalVideoPlayer").checked = appSettings.enableSystemExternalPlayers(), setMaxBitrateIntoField(context.querySelector(".selectVideoInNetworkQuality"), !0, "Video"), setMaxBitrateIntoField(context.querySelector(".selectVideoInternetQuality"), !1, "Video"), setMaxBitrateIntoField(context.querySelector(".selectMusicInternetQuality"), !1, "Audio"), fillChromecastQuality(context.querySelector(".selectChromecastVideoQuality")); - var selectSkipForwardLength = context.querySelector(".selectSkipForwardLength"); - fillSkipLengths(selectSkipForwardLength), selectSkipForwardLength.value = userSettings.skipForwardLength(); - var selectSkipBackLength = context.querySelector(".selectSkipBackLength"); - fillSkipLengths(selectSkipBackLength), selectSkipBackLength.value = userSettings.skipBackLength(), showOrHideEpisodesField(context, user, apiClient), loading.hide() + + var loggedInUserId = apiClient.getCurrentUserId(); + var userId = user.Id; + + showHideQualityFields(context, user, apiClient); + + apiClient.getCultures().then(function (allCultures) { + + populateLanguages(context.querySelector('#selectAudioLanguage'), allCultures); + + context.querySelector('#selectAudioLanguage', context).value = user.Configuration.AudioLanguagePreference || ""; + context.querySelector('.chkEpisodeAutoPlay').checked = user.Configuration.EnableNextEpisodeAutoPlay || false; + }); + + // hide cinema mode options if disabled at server level + apiClient.getNamedConfiguration("cinemamode").then(function (cinemaConfig) { + + if (cinemaConfig.EnableIntrosForMovies || cinemaConfig.EnableIntrosForEpisodes) { + context.querySelector('.cinemaModeOptions').classList.remove('hide'); + } else { + context.querySelector('.cinemaModeOptions').classList.add('hide'); + } + }); + + if (appHost.supports('externalplayerintent') && userId === loggedInUserId) { + context.querySelector('.fldExternalPlayer').classList.remove('hide'); + } else { + context.querySelector('.fldExternalPlayer').classList.add('hide'); + } + + if (userId === loggedInUserId && (user.Policy.EnableVideoPlaybackTranscoding || user.Policy.EnableAudioPlaybackTranscoding)) { + context.querySelector('.qualitySections').classList.remove('hide'); + + if (appHost.supports('chromecast') && user.Policy.EnableVideoPlaybackTranscoding) { + context.querySelector('.fldChromecastQuality').classList.remove('hide'); + } else { + context.querySelector('.fldChromecastQuality').classList.add('hide'); + } + } else { + context.querySelector('.qualitySections').classList.add('hide'); + context.querySelector('.fldChromecastQuality').classList.add('hide'); + } + + if (browser.tizen || browser.web0s) { + context.querySelector('.fldEnableNextVideoOverlay').classList.add('hide'); + } else { + context.querySelector('.fldEnableNextVideoOverlay').classList.remove('hide'); + } + + context.querySelector('.chkPlayDefaultAudioTrack').checked = user.Configuration.PlayDefaultAudioTrack || false; + context.querySelector('.chkEnableCinemaMode').checked = userSettings.enableCinemaMode(); + context.querySelector('.chkEnableNextVideoOverlay').checked = userSettings.enableNextVideoInfoOverlay(); + context.querySelector('.chkExternalVideoPlayer').checked = appSettings.enableSystemExternalPlayers(); + + setMaxBitrateIntoField(context.querySelector('.selectVideoInNetworkQuality'), true, 'Video'); + setMaxBitrateIntoField(context.querySelector('.selectVideoInternetQuality'), false, 'Video'); + setMaxBitrateIntoField(context.querySelector('.selectMusicInternetQuality'), false, 'Audio'); + + fillChromecastQuality(context.querySelector('.selectChromecastVideoQuality')); + + var selectSkipForwardLength = context.querySelector('.selectSkipForwardLength'); + fillSkipLengths(selectSkipForwardLength); + selectSkipForwardLength.value = userSettings.skipForwardLength(); + + var selectSkipBackLength = context.querySelector('.selectSkipBackLength'); + fillSkipLengths(selectSkipBackLength); + selectSkipBackLength.value = userSettings.skipBackLength(); + + showOrHideEpisodesField(context, user, apiClient); + + loading.hide(); } function saveUser(context, user, userSettingsInstance, apiClient) { - return appSettings.enableSystemExternalPlayers(context.querySelector(".chkExternalVideoPlayer").checked), appSettings.maxChromecastBitrate(context.querySelector(".selectChromecastVideoQuality").value), setMaxBitrateFromField(context.querySelector(".selectVideoInNetworkQuality"), !0, "Video"), setMaxBitrateFromField(context.querySelector(".selectVideoInternetQuality"), !1, "Video"), setMaxBitrateFromField(context.querySelector(".selectMusicInternetQuality"), !1, "Audio"), user.Configuration.AudioLanguagePreference = context.querySelector("#selectAudioLanguage").value, user.Configuration.PlayDefaultAudioTrack = context.querySelector(".chkPlayDefaultAudioTrack").checked, user.Configuration.EnableNextEpisodeAutoPlay = context.querySelector(".chkEpisodeAutoPlay").checked, userSettingsInstance.enableCinemaMode(context.querySelector(".chkEnableCinemaMode").checked), userSettingsInstance.enableNextVideoInfoOverlay(context.querySelector(".chkEnableNextVideoOverlay").checked), userSettingsInstance.skipForwardLength(context.querySelector(".selectSkipForwardLength").value), userSettingsInstance.skipBackLength(context.querySelector(".selectSkipBackLength").value), apiClient.updateUserConfiguration(user.Id, user.Configuration) + + appSettings.enableSystemExternalPlayers(context.querySelector('.chkExternalVideoPlayer').checked); + + appSettings.maxChromecastBitrate(context.querySelector('.selectChromecastVideoQuality').value); + + setMaxBitrateFromField(context.querySelector('.selectVideoInNetworkQuality'), true, 'Video'); + setMaxBitrateFromField(context.querySelector('.selectVideoInternetQuality'), false, 'Video'); + setMaxBitrateFromField(context.querySelector('.selectMusicInternetQuality'), false, 'Audio'); + + user.Configuration.AudioLanguagePreference = context.querySelector('#selectAudioLanguage').value; + user.Configuration.PlayDefaultAudioTrack = context.querySelector('.chkPlayDefaultAudioTrack').checked; + user.Configuration.EnableNextEpisodeAutoPlay = context.querySelector('.chkEpisodeAutoPlay').checked; + + userSettingsInstance.enableCinemaMode(context.querySelector('.chkEnableCinemaMode').checked); + + userSettingsInstance.enableNextVideoInfoOverlay(context.querySelector('.chkEnableNextVideoOverlay').checked); + userSettingsInstance.skipForwardLength(context.querySelector('.selectSkipForwardLength').value); + userSettingsInstance.skipBackLength(context.querySelector('.selectSkipBackLength').value); + + return apiClient.updateUserConfiguration(user.Id, user.Configuration); } function save(instance, context, userId, userSettings, apiClient, enableSaveConfirmation) { - loading.show(), apiClient.getUser(userId).then(function(user) { - saveUser(context, user, userSettings, apiClient).then(function() { - loading.hide(), enableSaveConfirmation && require(["toast"], function(toast) { - toast(globalize.translate("sharedcomponents#SettingsSaved")) - }), events.trigger(instance, "saved") - }, function() { - loading.hide() - }) - }) + + loading.show(); + + apiClient.getUser(userId).then(function (user) { + + saveUser(context, user, userSettings, apiClient).then(function () { + + loading.hide(); + if (enableSaveConfirmation) { + require(['toast'], function (toast) { + toast(globalize.translate('sharedcomponents#SettingsSaved')); + }); + } + + events.trigger(instance, 'saved'); + + }, function () { + loading.hide(); + }); + }); } function onSubmit(e) { - var self = this, - apiClient = connectionManager.getApiClient(self.options.serverId), - userId = self.options.userId, - userSettings = self.options.userSettings; - return userSettings.setUserInfo(userId, apiClient).then(function() { + + var self = this; + var apiClient = connectionManager.getApiClient(self.options.serverId); + var userId = self.options.userId; + var userSettings = self.options.userSettings; + + userSettings.setUserInfo(userId, apiClient).then(function () { + var enableSaveConfirmation = self.options.enableSaveConfirmation; - save(self, self.options.element, userId, userSettings, apiClient, enableSaveConfirmation) - }), e && e.preventDefault(), !1 + save(self, self.options.element, userId, userSettings, apiClient, enableSaveConfirmation); + }); + + // Disable default form submission + if (e) { + e.preventDefault(); + } + return false; } function embed(options, self) { - require(["text!./playbacksettings.template.html"], function(template) { - options.element.innerHTML = globalize.translateDocument(template, "sharedcomponents"), options.element.querySelector("form").addEventListener("submit", onSubmit.bind(self)), options.enableSaveButton && options.element.querySelector(".btnSave").classList.remove("hide"), self.loadData(), options.autoFocus && focusManager.autoFocus(options.element) - }) + + require(['text!./playbacksettings.template.html'], function (template) { + + options.element.innerHTML = globalize.translateDocument(template, 'sharedcomponents'); + + options.element.querySelector('form').addEventListener('submit', onSubmit.bind(self)); + + if (options.enableSaveButton) { + options.element.querySelector('.btnSave').classList.remove('hide'); + } + + self.loadData(); + + if (options.autoFocus) { + focusManager.autoFocus(options.element); + } + }); } function PlaybackSettings(options) { - this.options = options, embed(options, this) + + this.options = options; + + embed(options, this); } - return PlaybackSettings.prototype.loadData = function() { - var self = this, - context = self.options.element; + + PlaybackSettings.prototype.loadData = function () { + + var self = this; + var context = self.options.element; + loading.show(); - var userId = self.options.userId, - apiClient = connectionManager.getApiClient(self.options.serverId), - userSettings = self.options.userSettings; - apiClient.getUser(userId).then(function(user) { - userSettings.setUserInfo(userId, apiClient).then(function() { - self.dataLoaded = !0, loadForm(context, user, userSettings, apiClient) - }) - }) - }, PlaybackSettings.prototype.submit = function() { - onSubmit.call(this) - }, PlaybackSettings.prototype.destroy = function() { - this.options = null - }, PlaybackSettings + + var userId = self.options.userId; + var apiClient = connectionManager.getApiClient(self.options.serverId); + var userSettings = self.options.userSettings; + + apiClient.getUser(userId).then(function (user) { + + userSettings.setUserInfo(userId, apiClient).then(function () { + + self.dataLoaded = true; + + loadForm(context, user, userSettings, apiClient); + }); + }); + }; + + PlaybackSettings.prototype.submit = function () { + onSubmit.call(this); + }; + + PlaybackSettings.prototype.destroy = function () { + + this.options = null; + }; + + return PlaybackSettings; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/playerstats/playerstats.css b/src/bower_components/emby-webcomponents/playerstats/playerstats.css index 865200c767..b9d6355bd2 100644 --- a/src/bower_components/emby-webcomponents/playerstats/playerstats.css +++ b/src/bower_components/emby-webcomponents/playerstats/playerstats.css @@ -1,24 +1,24 @@ .playerStats { - background: rgba(28, 28, 28, .8); - -webkit-border-radius: .3em; + background: rgba(28,28,28,0.8); border-radius: .3em; + color: #fff; left: 1.5em; position: absolute; top: 5em; - color: #fff + color: #fff; } .playerStats-tv { - top: 4em + top: 4em; } .playerStats-content { position: relative; - font-size: 84% + font-size: 84%; } .playerStats-content-tv { - font-size: 60% + font-size: 60%; } .playerStats-closeButton { @@ -26,38 +26,31 @@ top: .25em; right: .25em; color: #ccc; - z-index: 1 + z-index: 1; } .playerStats-stats { - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -webkit-flex-direction: column; flex-direction: column; padding: 0 3em 1em 1em; max-width: 50em; - overflow: hidden + overflow: hidden; } .playerStats-stat { - display: -webkit-box; - display: -webkit-flex; display: flex; - margin-left: 1em + margin-left: 1em; } .playerStats-stat-label { font-weight: 500; - margin: 0 .5em 0 0 + margin: 0 .5em 0 0; } .playerStats-stat-header { - margin: 1em 1em 0 0 + margin: 1em 1em 0 0; } .playerStats-stat-value { - color: #ddd -} \ No newline at end of file + color: #ddd; +} diff --git a/src/bower_components/emby-webcomponents/playerstats/playerstats.js b/src/bower_components/emby-webcomponents/playerstats/playerstats.js index a089690aa0..f0cca7c804 100644 --- a/src/bower_components/emby-webcomponents/playerstats/playerstats.js +++ b/src/bower_components/emby-webcomponents/playerstats/playerstats.js @@ -1,193 +1,466 @@ -define(["events", "globalize", "playbackManager", "connectionManager", "playMethodHelper", "layoutManager", "serverNotifications", "paper-icon-button-light", "css!./playerstats"], function(events, globalize, playbackManager, connectionManager, playMethodHelper, layoutManager, serverNotifications) { - "use strict"; +define(['events', 'globalize', 'playbackManager', 'connectionManager', 'playMethodHelper', 'layoutManager', 'serverNotifications', 'paper-icon-button-light', 'css!./playerstats'], function (events, globalize, playbackManager, connectionManager, playMethodHelper, layoutManager, serverNotifications) { + 'use strict'; function init(instance) { - var parent = document.createElement("div"); - parent.classList.add("playerStats"), layoutManager.tv && parent.classList.add("playerStats-tv"), parent.classList.add("hide"); + + var parent = document.createElement('div'); + + parent.classList.add('playerStats'); + + if (layoutManager.tv) { + parent.classList.add('playerStats-tv'); + } + + parent.classList.add('hide'); + var button; - button = layoutManager.tv ? "" : ''; - var contentClass = layoutManager.tv ? "playerStats-content playerStats-content-tv" : "playerStats-content"; - parent.innerHTML = '
' + button + '
', button = parent.querySelector(".playerStats-closeButton"), button && button.addEventListener("click", onCloseButtonClick.bind(instance)), document.body.appendChild(parent), instance.element = parent + + if (layoutManager.tv) { + button = ''; + } else { + button = ''; + } + + var contentClass = layoutManager.tv ? 'playerStats-content playerStats-content-tv' : 'playerStats-content'; + + parent.innerHTML = '
' + button + '
'; + + button = parent.querySelector('.playerStats-closeButton'); + + if (button) { + button.addEventListener('click', onCloseButtonClick.bind(instance)); + } + + document.body.appendChild(parent); + + instance.element = parent; } function onCloseButtonClick() { - this.enabled(!1) + this.enabled(false); } function renderStats(elem, categories) { - elem.querySelector(".playerStats-stats").innerHTML = categories.map(function(category) { - var categoryHtml = "", - stats = category.stats; - stats.length && category.name && (categoryHtml += '
', categoryHtml += '
', categoryHtml += category.name, categoryHtml += "
", categoryHtml += '
', categoryHtml += category.subText || "", categoryHtml += "
", categoryHtml += "
"); - for (var i = 0, length = stats.length; i < length; i++) { - categoryHtml += '
'; - var stat = stats[i]; - categoryHtml += '
', categoryHtml += stat.label, categoryHtml += "
", categoryHtml += '
', categoryHtml += stat.value, categoryHtml += "
", categoryHtml += "
" + + elem.querySelector('.playerStats-stats').innerHTML = categories.map(function (category) { + + var categoryHtml = ''; + + var stats = category.stats; + + if (stats.length && category.name) { + categoryHtml += '
'; + + categoryHtml += '
'; + categoryHtml += category.name; + categoryHtml += '
'; + + categoryHtml += '
'; + categoryHtml += category.subText || ''; + categoryHtml += '
'; + + categoryHtml += '
'; } - return categoryHtml - }).join("") + + for (var i = 0, length = stats.length; i < length; i++) { + + categoryHtml += '
'; + + var stat = stats[i]; + + categoryHtml += '
'; + categoryHtml += stat.label; + categoryHtml += '
'; + + categoryHtml += '
'; + categoryHtml += stat.value; + categoryHtml += '
'; + + categoryHtml += '
'; + } + + return categoryHtml; + + }).join(''); } function getSession(instance, player) { - if ((new Date).getTime() - (instance.lastSessionTime || 0) < 1e4) return Promise.resolve(instance.lastSession); + + var now = new Date().getTime(); + + if ((now - (instance.lastSessionTime || 0)) < 10000) { + return Promise.resolve(instance.lastSession); + } + var apiClient = connectionManager.getApiClient(playbackManager.currentItem(player).ServerId); + return apiClient.getSessions({ deviceId: apiClient.deviceId() - }).then(function(sessions) { - return instance.lastSession = sessions[0] || {}, instance.lastSessionTime = (new Date).getTime(), Promise.resolve(instance.lastSession) - }, function() { - return Promise.resolve({}) - }) + }).then(function (sessions) { + + instance.lastSession = sessions[0] || {}; + instance.lastSessionTime = new Date().getTime(); + + return Promise.resolve(instance.lastSession); + + }, function () { + return Promise.resolve({}); + }); } function translateReason(reason) { - return globalize.translate("sharedcomponents#" + reason) + + return globalize.translate('sharedcomponents#' + reason); } function getTranscodingStats(session, player, displayPlayMethod) { - var videoCodec, audioCodec, totalBitrate, sessionStats = []; - return session.TranscodingInfo && (videoCodec = session.TranscodingInfo.VideoCodec, audioCodec = session.TranscodingInfo.AudioCodec, totalBitrate = session.TranscodingInfo.Bitrate, session.TranscodingInfo.AudioChannels), videoCodec && sessionStats.push({ - label: "Video codec:", - value: session.TranscodingInfo.IsVideoDirect ? videoCodec.toUpperCase() + " (direct)" : videoCodec.toUpperCase() - }), audioCodec && sessionStats.push({ - label: "Audio codec:", - value: session.TranscodingInfo.IsAudioDirect ? audioCodec.toUpperCase() + " (direct)" : audioCodec.toUpperCase() - }), "Transcode" === displayPlayMethod && (totalBitrate && sessionStats.push({ - label: "Bitrate:", - value: getDisplayBitrate(totalBitrate) - }), session.TranscodingInfo.CompletionPercentage && sessionStats.push({ - label: "Transcoding progress:", - value: session.TranscodingInfo.CompletionPercentage.toFixed(1) + "%" - }), session.TranscodingInfo.Framerate && sessionStats.push({ - label: "Transcoding framerate:", - value: session.TranscodingInfo.Framerate + " fps" - }), session.TranscodingInfo.TranscodeReasons && session.TranscodingInfo.TranscodeReasons.length && sessionStats.push({ - label: "Reason for transcoding:", - value: session.TranscodingInfo.TranscodeReasons.map(translateReason).join("
") - })), sessionStats + var sessionStats = []; + + var videoCodec; + var audioCodec; + var totalBitrate; + var audioChannels; + + if (session.TranscodingInfo) { + + videoCodec = session.TranscodingInfo.VideoCodec; + audioCodec = session.TranscodingInfo.AudioCodec; + totalBitrate = session.TranscodingInfo.Bitrate; + audioChannels = session.TranscodingInfo.AudioChannels; + } + + if (videoCodec) { + + sessionStats.push({ + label: 'Video codec:', + value: session.TranscodingInfo.IsVideoDirect ? (videoCodec.toUpperCase() + ' (direct)') : videoCodec.toUpperCase() + }); + } + + if (audioCodec) { + + sessionStats.push({ + label: 'Audio codec:', + value: session.TranscodingInfo.IsAudioDirect ? (audioCodec.toUpperCase() + ' (direct)') : audioCodec.toUpperCase() + }); + } + + //if (audioChannels) { + + // sessionStats.push({ + // label: 'Audio channels:', + // value: audioChannels + // }); + //} + + if (displayPlayMethod === 'Transcode') { + if (totalBitrate) { + + sessionStats.push({ + label: 'Bitrate:', + value: getDisplayBitrate(totalBitrate) + }); + } + if (session.TranscodingInfo.CompletionPercentage) { + + sessionStats.push({ + label: 'Transcoding progress:', + value: session.TranscodingInfo.CompletionPercentage.toFixed(1) + '%' + }); + } + if (session.TranscodingInfo.Framerate) { + + sessionStats.push({ + label: 'Transcoding framerate:', + value: session.TranscodingInfo.Framerate + ' fps' + }); + } + if (session.TranscodingInfo.TranscodeReasons && session.TranscodingInfo.TranscodeReasons.length) { + + sessionStats.push({ + label: 'Reason for transcoding:', + value: session.TranscodingInfo.TranscodeReasons.map(translateReason).join('
') + }); + } + } + + return sessionStats; } function getDisplayBitrate(bitrate) { - return bitrate > 1e6 ? (bitrate / 1e6).toFixed(1) + " Mbps" : Math.floor(bitrate / 1e3) + " kbps" + + if (bitrate > 1000000) { + return (bitrate / 1000000).toFixed(1) + ' Mbps'; + } else { + return Math.floor(bitrate / 1000) + ' kbps'; + } } function getMediaSourceStats(session, player, displayPlayMethod) { - var sessionStats = [], - mediaSource = playbackManager.currentMediaSource(player) || {}, - totalBitrate = mediaSource.Bitrate; - mediaSource.Container && sessionStats.push({ - label: "Container:", - value: mediaSource.Container - }), totalBitrate && sessionStats.push({ - label: "Bitrate:", - value: getDisplayBitrate(totalBitrate) - }); - var mediaStreams = mediaSource.MediaStreams || [], - videoStream = mediaStreams.filter(function(s) { - return "Video" === s.Type - })[0] || {}, - videoCodec = videoStream.Codec, - audioStreamIndex = playbackManager.getAudioStreamIndex(player), - audioStream = playbackManager.audioTracks(player).filter(function(s) { - return "Audio" === s.Type && s.Index === audioStreamIndex - })[0] || {}, - audioCodec = audioStream.Codec, - audioChannels = audioStream.Channels, - videoInfos = []; - videoCodec && videoInfos.push(videoCodec.toUpperCase()), videoStream.Profile && videoInfos.push(videoStream.Profile), videoInfos.length && sessionStats.push({ - label: "Video codec:", - value: videoInfos.join(" ") - }), videoStream.BitRate && sessionStats.push({ - label: "Video bitrate:", - value: getDisplayBitrate(videoStream.BitRate) - }); + + var sessionStats = []; + + var mediaSource = playbackManager.currentMediaSource(player) || {}; + var totalBitrate = mediaSource.Bitrate; + + if (mediaSource.Container) { + sessionStats.push({ + label: 'Container:', + value: mediaSource.Container + }); + } + + if (totalBitrate) { + + sessionStats.push({ + label: 'Bitrate:', + value: getDisplayBitrate(totalBitrate) + }); + } + + var mediaStreams = mediaSource.MediaStreams || []; + var videoStream = mediaStreams.filter(function (s) { + + return s.Type === 'Video'; + + })[0] || {}; + + var videoCodec = videoStream.Codec; + + var audioStreamIndex = playbackManager.getAudioStreamIndex(player); + var audioStream = playbackManager.audioTracks(player).filter(function (s) { + + return s.Type === 'Audio' && s.Index === audioStreamIndex; + + })[0] || {}; + + var audioCodec = audioStream.Codec; + var audioChannels = audioStream.Channels; + + var videoInfos = []; + + if (videoCodec) { + videoInfos.push(videoCodec.toUpperCase()); + } + + if (videoStream.Profile) { + videoInfos.push(videoStream.Profile); + } + + if (videoInfos.length) { + sessionStats.push({ + label: 'Video codec:', + value: videoInfos.join(' ') + }); + } + + if (videoStream.BitRate) { + sessionStats.push({ + label: 'Video bitrate:', + value: getDisplayBitrate(videoStream.BitRate) + }); + } + var audioInfos = []; - return audioCodec && audioInfos.push(audioCodec.toUpperCase()), audioStream.Profile && audioInfos.push(audioStream.Profile), audioInfos.length && sessionStats.push({ - label: "Audio codec:", - value: audioInfos.join(" ") - }), audioStream.BitRate && sessionStats.push({ - label: "Audio bitrate:", - value: getDisplayBitrate(audioStream.BitRate) - }), audioChannels && sessionStats.push({ - label: "Audio channels:", - value: audioChannels - }), audioStream.SampleRate && sessionStats.push({ - label: "Audio sample rate:", - value: audioStream.SampleRate + " Hz" - }), audioStream.BitDepth && sessionStats.push({ - label: "Audio bit depth:", - value: audioStream.BitDepth - }), sessionStats + + if (audioCodec) { + audioInfos.push(audioCodec.toUpperCase()); + } + + if (audioStream.Profile) { + audioInfos.push(audioStream.Profile); + } + + if (audioInfos.length) { + sessionStats.push({ + label: 'Audio codec:', + value: audioInfos.join(' ') + }); + } + + if (audioStream.BitRate) { + sessionStats.push({ + label: 'Audio bitrate:', + value: getDisplayBitrate(audioStream.BitRate) + }); + } + + if (audioChannels) { + sessionStats.push({ + label: 'Audio channels:', + value: audioChannels + }); + } + + if (audioStream.SampleRate) { + sessionStats.push({ + label: 'Audio sample rate:', + value: audioStream.SampleRate + ' Hz' + }); + } + + if (audioStream.BitDepth) { + sessionStats.push({ + label: 'Audio bit depth:', + value: audioStream.BitDepth + }); + } + + return sessionStats; } function getStats(instance, player) { - var statsPromise = player.getStats ? player.getStats() : Promise.resolve({}), - sessionPromise = getSession(instance, player); - return Promise.all([statsPromise, sessionPromise]).then(function(responses) { - var playerStatsResult = responses[0], - playerStats = playerStatsResult.categories || [], - session = responses[1], - displayPlayMethod = playMethodHelper.getDisplayPlayMethod(session), - baseCategory = { - stats: [], - name: "Playback Info" - }; + + var statsPromise = player.getStats ? player.getStats() : Promise.resolve({}); + var sessionPromise = getSession(instance, player); + + return Promise.all([statsPromise, sessionPromise]).then(function (responses) { + + var playerStatsResult = responses[0]; + var playerStats = playerStatsResult.categories || []; + var session = responses[1]; + + var displayPlayMethod = playMethodHelper.getDisplayPlayMethod(session); + + var baseCategory = { + stats: [], + name: 'Playback Info' + }; + baseCategory.stats.unshift({ - label: "Play method:", + label: 'Play method:', value: displayPlayMethod - }), baseCategory.stats.unshift({ - label: "Player:", + }); + + baseCategory.stats.unshift({ + label: 'Player:', value: player.name }); + var categories = []; + categories.push(baseCategory); + for (var i = 0, length = playerStats.length; i < length; i++) { + var category = playerStats[i]; - "audio" === category.type ? category.name = "Audio Info" : "video" === category.type && (category.name = "Video Info"), categories.push(category) + if (category.type === 'audio') { + category.name = 'Audio Info'; + } + else if (category.type === 'video') { + category.name = 'Video Info'; + } + categories.push(category); } - return session.TranscodingInfo && categories.push({ - stats: getTranscodingStats(session, player, displayPlayMethod), - name: "Transcode" === displayPlayMethod ? "Transcoding Info" : "Direct Stream Info" - }), categories.push({ + + if (session.TranscodingInfo) { + + categories.push({ + stats: getTranscodingStats(session, player, displayPlayMethod), + name: displayPlayMethod === 'Transcode' ? 'Transcoding Info' : 'Direct Stream Info' + }); + } + + categories.push({ stats: getMediaSourceStats(session, player), - name: "Original Media Info" - }), Promise.resolve(categories) - }) + name: 'Original Media Info' + }); + + return Promise.resolve(categories); + }); } function renderPlayerStats(instance, player) { - var now = (new Date).getTime(); - now - (instance.lastRender || 0) < 700 || (instance.lastRender = now, getStats(instance, player).then(function(stats) { + + var now = new Date().getTime(); + + if ((now - (instance.lastRender || 0)) < 700) { + return; + } + + instance.lastRender = now; + + getStats(instance, player).then(function (stats) { + var elem = instance.element; - elem && renderStats(elem, stats) - })) + if (!elem) { + return; + } + + renderStats(elem, stats); + }); } function bindEvents(instance, player) { - var localOnTimeUpdate = function() { - renderPlayerStats(instance, player) + + var localOnTimeUpdate = function () { + renderPlayerStats(instance, player); }; - instance.onTimeUpdate = localOnTimeUpdate, events.on(player, "timeupdate", localOnTimeUpdate) + + instance.onTimeUpdate = localOnTimeUpdate; + events.on(player, 'timeupdate', localOnTimeUpdate); } function unbindEvents(instance, player) { + var localOnTimeUpdate = instance.onTimeUpdate; - localOnTimeUpdate && events.off(player, "timeupdate", localOnTimeUpdate) + + if (localOnTimeUpdate) { + events.off(player, 'timeupdate', localOnTimeUpdate); + } } function PlayerStats(options) { - this.options = options, init(this), this.enabled(!0) + + this.options = options; + + init(this); + + this.enabled(true); } - return PlayerStats.prototype.enabled = function(enabled) { - if (null == enabled) return this._enabled; + + PlayerStats.prototype.enabled = function (enabled) { + + if (enabled == null) { + return this._enabled; + } + var options = this.options; - options && (this._enabled = enabled, enabled ? (this.element.classList.remove("hide"), bindEvents(this, options.player)) : (this.element.classList.add("hide"), unbindEvents(this, options.player))) - }, PlayerStats.prototype.toggle = function() { - this.enabled(!this.enabled()) - }, PlayerStats.prototype.destroy = function() { + + if (!options) { + return; + } + + this._enabled = enabled; + if (enabled) { + this.element.classList.remove('hide'); + bindEvents(this, options.player); + } else { + this.element.classList.add('hide'); + unbindEvents(this, options.player); + } + }; + + PlayerStats.prototype.toggle = function () { + this.enabled(!this.enabled()); + }; + + PlayerStats.prototype.destroy = function () { + var options = this.options; - options && (this.options = null, unbindEvents(this, options.player)); + + if (options) { + + this.options = null; + unbindEvents(this, options.player); + } + var elem = this.element; - elem && (elem.parentNode.removeChild(elem), this.element = null) - }, PlayerStats + if (elem) { + elem.parentNode.removeChild(elem); + this.element = null; + } + }; + + return PlayerStats; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/playlisteditor/playlisteditor.js b/src/bower_components/emby-webcomponents/playlisteditor/playlisteditor.js index 22a5c180d8..a2fd9a1a33 100644 --- a/src/bower_components/emby-webcomponents/playlisteditor/playlisteditor.js +++ b/src/bower_components/emby-webcomponents/playlisteditor/playlisteditor.js @@ -1,127 +1,298 @@ -define(["shell", "dialogHelper", "loading", "layoutManager", "playbackManager", "connectionManager", "userSettings", "appRouter", "globalize", "emby-input", "paper-icon-button-light", "emby-select", "material-icons", "css!./../formdialog", "emby-button"], function(shell, dialogHelper, loading, layoutManager, playbackManager, connectionManager, userSettings, appRouter, globalize) { - "use strict"; +define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager', 'connectionManager', 'userSettings', 'appRouter', 'globalize', 'emby-input', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button'], function (shell, dialogHelper, loading, layoutManager, playbackManager, connectionManager, userSettings, appRouter, globalize) { + 'use strict'; + + var currentServerId; function parentWithClass(elem, className) { - for (; !elem.classList || !elem.classList.contains(className);) - if (!(elem = elem.parentNode)) return null; - return elem + + while (!elem.classList || !elem.classList.contains(className)) { + elem = elem.parentNode; + + if (!elem) { + return null; + } + } + + return elem; } function onSubmit(e) { - var panel = parentWithClass(this, "dialog"), - playlistId = panel.querySelector("#selectPlaylistToAddTo").value, - apiClient = connectionManager.getApiClient(currentServerId); - return playlistId ? (userSettings.set("playlisteditor-lastplaylistid", playlistId), addToPlaylist(apiClient, panel, playlistId)) : createPlaylist(apiClient, panel), e.preventDefault(), !1 + + var panel = parentWithClass(this, 'dialog'); + + var playlistId = panel.querySelector('#selectPlaylistToAddTo').value; + var apiClient = connectionManager.getApiClient(currentServerId); + + if (playlistId) { + userSettings.set('playlisteditor-lastplaylistid', playlistId); + addToPlaylist(apiClient, panel, playlistId); + } else { + createPlaylist(apiClient, panel); + } + + e.preventDefault(); + return false; } function createPlaylist(apiClient, dlg) { + loading.show(); + var url = apiClient.getUrl("Playlists", { - Name: dlg.querySelector("#txtNewPlaylistName").value, - Ids: dlg.querySelector(".fldSelectedItemIds").value || "", + + Name: dlg.querySelector('#txtNewPlaylistName').value, + Ids: dlg.querySelector('.fldSelectedItemIds').value || '', userId: apiClient.getCurrentUserId() + }); + apiClient.ajax({ type: "POST", url: url, dataType: "json" - }).then(function(result) { + + }).then(function (result) { + loading.hide(); + var id = result.Id; - dlg.submitted = !0, dialogHelper.close(dlg), redirectToPlaylist(apiClient, id) - }) + dlg.submitted = true; + dialogHelper.close(dlg); + redirectToPlaylist(apiClient, id); + }); } function redirectToPlaylist(apiClient, id) { - appRouter.showItem(id, apiClient.serverId()) + + appRouter.showItem(id, apiClient.serverId()); } function addToPlaylist(apiClient, dlg, id) { - var itemIds = dlg.querySelector(".fldSelectedItemIds").value || ""; - if ("queue" === id) return playbackManager.queue({ - serverId: apiClient.serverId(), - ids: itemIds.split(",") - }), dlg.submitted = !0, void dialogHelper.close(dlg); + + var itemIds = dlg.querySelector('.fldSelectedItemIds').value || ''; + + if (id === 'queue') { + + playbackManager.queue({ + serverId: apiClient.serverId(), + ids: itemIds.split(',') + }); + dlg.submitted = true; + dialogHelper.close(dlg); + return; + } + loading.show(); + var url = apiClient.getUrl("Playlists/" + id + "/Items", { + Ids: itemIds, userId: apiClient.getCurrentUserId() }); + apiClient.ajax({ type: "POST", url: url - }).then(function() { - loading.hide(), dlg.submitted = !0, dialogHelper.close(dlg) - }) + + }).then(function () { + + loading.hide(); + + dlg.submitted = true; + dialogHelper.close(dlg); + }); } function triggerChange(select) { - select.dispatchEvent(new CustomEvent("change", {})) + select.dispatchEvent(new CustomEvent('change', {})); } function populatePlaylists(editorOptions, panel) { - var select = panel.querySelector("#selectPlaylistToAddTo"); - loading.hide(), panel.querySelector(".newPlaylistInfo").classList.add("hide"); + + var select = panel.querySelector('#selectPlaylistToAddTo'); + + loading.hide(); + + panel.querySelector('.newPlaylistInfo').classList.add('hide'); + var options = { - Recursive: !0, - IncludeItemTypes: "Playlist", - SortBy: "SortName", - EnableTotalRecordCount: !1 - }, - apiClient = connectionManager.getApiClient(currentServerId); - apiClient.getItems(apiClient.getCurrentUserId(), options).then(function(result) { - var html = ""; - !1 !== editorOptions.enableAddToPlayQueue && playbackManager.isPlaying() && (html += '"), html += '", html += result.Items.map(function(i) { - return '" - }), select.innerHTML = html; + + Recursive: true, + IncludeItemTypes: "Playlist", + SortBy: 'SortName', + EnableTotalRecordCount: false + }; + + var apiClient = connectionManager.getApiClient(currentServerId); + apiClient.getItems(apiClient.getCurrentUserId(), options).then(function (result) { + + var html = ''; + + if (editorOptions.enableAddToPlayQueue !== false && playbackManager.isPlaying()) { + html += ''; + } + + html += ''; + + html += result.Items.map(function (i) { + + return ''; + }); + + select.innerHTML = html; + var defaultValue = editorOptions.defaultValue; - defaultValue || (defaultValue = userSettings.get("playlisteditor-lastplaylistid") || ""), select.value = "new" === defaultValue ? "" : defaultValue, select.value || (select.value = ""), triggerChange(select), loading.hide() - }) + if (!defaultValue) { + defaultValue = userSettings.get('playlisteditor-lastplaylistid') || ''; + } + select.value = defaultValue === 'new' ? '' : defaultValue; + + // If the value is empty set it again, in case we tried to set a lastplaylistid that is no longer valid + if (!select.value) { + select.value = ''; + } + + triggerChange(select); + + loading.hide(); + }); } function getEditorHtml(items) { - var html = ""; - html += '
', html += '
', html += '
', html += '
'; - var autoFocus = items.length ? " autofocus" : ""; - return html += '", html += "
", html += '
', html += '
', autoFocus = items.length ? "" : " autofocus", html += '", html += "
", html += "
", html += '
', html += '", html += "
", html += '', html += "
", html += "
", html += "
" + + var html = ''; + + html += '
'; + html += '
'; + html += '
'; + + html += '
'; + var autoFocus = items.length ? ' autofocus' : ''; + html += ''; + html += '
'; + + html += '
'; + + html += '
'; + autoFocus = items.length ? '' : ' autofocus'; + html += ''; + html += '
'; + + // newPlaylistInfo + html += '
'; + + html += '
'; + html += ''; + html += '
'; + + html += ''; + + html += '
'; + html += '
'; + html += '
'; + + return html; } function initEditor(content, options, items) { - if (content.querySelector("#selectPlaylistToAddTo").addEventListener("change", function() { - this.value ? (content.querySelector(".newPlaylistInfo").classList.add("hide"), content.querySelector("#txtNewPlaylistName").removeAttribute("required")) : (content.querySelector(".newPlaylistInfo").classList.remove("hide"), content.querySelector("#txtNewPlaylistName").setAttribute("required", "required")) - }), content.querySelector("form").addEventListener("submit", onSubmit), content.querySelector(".fldSelectedItemIds", content).value = items.join(","), items.length) content.querySelector(".fldSelectPlaylist").classList.remove("hide"), populatePlaylists(options, content); - else { - content.querySelector(".fldSelectPlaylist").classList.add("hide"); - var selectPlaylistToAddTo = content.querySelector("#selectPlaylistToAddTo"); - selectPlaylistToAddTo.innerHTML = "", selectPlaylistToAddTo.value = "", triggerChange(selectPlaylistToAddTo) + + content.querySelector('#selectPlaylistToAddTo').addEventListener('change', function () { + if (this.value) { + content.querySelector('.newPlaylistInfo').classList.add('hide'); + content.querySelector('#txtNewPlaylistName').removeAttribute('required'); + } else { + content.querySelector('.newPlaylistInfo').classList.remove('hide'); + content.querySelector('#txtNewPlaylistName').setAttribute('required', 'required'); + } + }); + + content.querySelector('form').addEventListener('submit', onSubmit); + + content.querySelector('.fldSelectedItemIds', content).value = items.join(','); + + if (items.length) { + content.querySelector('.fldSelectPlaylist').classList.remove('hide'); + populatePlaylists(options, content); + } else { + content.querySelector('.fldSelectPlaylist').classList.add('hide'); + + var selectPlaylistToAddTo = content.querySelector('#selectPlaylistToAddTo'); + selectPlaylistToAddTo.innerHTML = ''; + selectPlaylistToAddTo.value = ''; + triggerChange(selectPlaylistToAddTo); } } function centerFocus(elem, horiz, on) { - require(["scrollHelper"], function(scrollHelper) { - var fn = on ? "on" : "off"; - scrollHelper.centerFocus[fn](elem, horiz) - }) + require(['scrollHelper'], function (scrollHelper) { + var fn = on ? 'on' : 'off'; + scrollHelper.centerFocus[fn](elem, horiz); + }); } - function PlaylistEditor() {} - var currentServerId; - return PlaylistEditor.prototype.show = function(options) { + function PlaylistEditor() { + + } + + PlaylistEditor.prototype.show = function (options) { + var items = options.items || {}; currentServerId = options.serverId; + var dialogOptions = { - removeOnClose: !0, - scrollY: !1 + removeOnClose: true, + scrollY: false }; - layoutManager.tv ? dialogOptions.size = "fullscreen" : dialogOptions.size = "small"; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'small'; + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"); - var html = "", - title = globalize.translate("sharedcomponents#HeaderAddToPlaylist"); - return html += '
', html += '', html += '

', html += title, html += "

", html += "
", html += getEditorHtml(items), dlg.innerHTML = html, initEditor(dlg, options, items), dlg.querySelector(".btnCancel").addEventListener("click", function() { - dialogHelper.close(dlg) - }), layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !0), dialogHelper.open(dlg).then(function() { - return layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !1), dlg.submitted ? Promise.resolve() : Promise.reject() - }) - }, PlaylistEditor + + dlg.classList.add('formDialog'); + + var html = ''; + var title = globalize.translate('sharedcomponents#HeaderAddToPlaylist'); + + html += '
'; + html += ''; + html += '

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

'; + + html += '
'; + + html += getEditorHtml(items); + + dlg.innerHTML = html; + + initEditor(dlg, options, items); + + dlg.querySelector('.btnCancel').addEventListener('click', function () { + + dialogHelper.close(dlg); + }); + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, true); + } + + return dialogHelper.open(dlg).then(function () { + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, false); + } + + if (dlg.submitted) { + return Promise.resolve(); + } + + return Promise.reject(); + }); + }; + + return PlaylistEditor; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/playmenu.js b/src/bower_components/emby-webcomponents/playmenu.js index acdf9e8f7e..097c17f71a 100644 --- a/src/bower_components/emby-webcomponents/playmenu.js +++ b/src/bower_components/emby-webcomponents/playmenu.js @@ -1,55 +1,75 @@ -define(["actionsheet", "datetime", "playbackManager", "globalize", "appSettings"], function(actionsheet, datetime, playbackManager, globalize, appSettings) { - "use strict"; +define(['actionsheet', 'datetime', 'playbackManager', 'globalize', 'appSettings'], function (actionsheet, datetime, playbackManager, globalize, appSettings) { + 'use strict'; function show(options) { - var item = options.item, - itemType = item.Type, - isFolder = item.IsFolder, - itemId = item.Id, - channelId = item.ChannelId, - serverId = item.ServerId, - resumePositionTicks = item.UserData ? item.UserData.PlaybackPositionTicks : null, - playableItemId = "Program" === itemType ? channelId : itemId; - if (!resumePositionTicks || isFolder) return void playbackManager.play({ - ids: [playableItemId], - serverId: serverId - }); + + var item = options.item; + + var itemType = item.Type; + var isFolder = item.IsFolder; + var itemId = item.Id; + var channelId = item.ChannelId; + var serverId = item.ServerId; + var resumePositionTicks = item.UserData ? item.UserData.PlaybackPositionTicks : null; + + var playableItemId = itemType === 'Program' ? channelId : itemId; + + if (!resumePositionTicks || isFolder) { + playbackManager.play({ + ids: [playableItemId], + serverId: serverId + }); + return; + } + var menuItems = []; + menuItems.push({ - name: globalize.translate("sharedcomponents#ResumeAt", datetime.getDisplayRunningTime(resumePositionTicks)), - id: "resume" - }), menuItems.push({ - name: globalize.translate("sharedcomponents#PlayFromBeginning"), - id: "play" - }), actionsheet.show({ + name: globalize.translate('sharedcomponents#ResumeAt', datetime.getDisplayRunningTime(resumePositionTicks)), + id: 'resume' + }); + + menuItems.push({ + name: globalize.translate('sharedcomponents#PlayFromBeginning'), + id: 'play' + }); + + actionsheet.show({ + items: menuItems, positionTo: options.positionTo - }).then(function(id) { + + }).then(function (id) { switch (id) { - case "play": + + case 'play': playbackManager.play({ ids: [playableItemId], serverId: serverId }); break; - case "resume": + case 'resume': playbackManager.play({ ids: [playableItemId], startPositionTicks: resumePositionTicks, serverId: serverId }); break; - case "queue": + case 'queue': playbackManager.queue({ items: [item] }); break; - case "shuffle": - playbackManager.shuffle(item) + case 'shuffle': + playbackManager.shuffle(item); + break; + default: + break; } - }) + }); } + return { show: show - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/pluginmanager.js b/src/bower_components/emby-webcomponents/pluginmanager.js index 57da495a42..ec65e63e65 100644 --- a/src/bower_components/emby-webcomponents/pluginmanager.js +++ b/src/bower_components/emby-webcomponents/pluginmanager.js @@ -1,63 +1,149 @@ -define(["events"], function(events) { - "use strict"; +define(['events'], function (events) { + 'use strict'; + + // TODO: replace with each plugin version + var cacheParam = new Date().getTime(); function loadStrings(plugin, globalize) { var strings = plugin.getTranslations ? plugin.getTranslations() : []; return globalize.loadStrings({ name: plugin.id || plugin.packageName, strings: strings - }) + }); } function definePluginRoute(pluginManager, route, plugin) { - route.contentPath = pluginManager.mapPath(plugin, route.path), route.path = pluginManager.mapRoute(plugin, route), Emby.App.defineRoute(route, plugin.id) + + route.contentPath = pluginManager.mapPath(plugin, route.path); + route.path = pluginManager.mapRoute(plugin, route); + + Emby.App.defineRoute(route, plugin.id); } function PluginManager() { - this.pluginsList = [] + + this.pluginsList = []; } - var cacheParam = (new Date).getTime(); - return PluginManager.prototype.loadPlugin = function(url) { - console.log("Loading plugin: " + url); + + PluginManager.prototype.loadPlugin = function (url) { + + console.log('Loading plugin: ' + url); var instance = this; - return new Promise(function(resolve, reject) { - require([url, "globalize", "appRouter"], function(pluginFactory, globalize, appRouter) { - var plugin = new pluginFactory; - if (instance.pluginsList.filter(function(p) { - return p.id === plugin.id - })[0]) return void resolve(url); + + return new Promise(function (resolve, reject) { + + require([url, 'globalize', 'appRouter'], function (pluginFactory, globalize, appRouter) { + + var plugin = new pluginFactory(); + + // See if it's already installed + var existing = instance.pluginsList.filter(function (p) { + return p.id === plugin.id; + })[0]; + + if (existing) { + resolve(url); + return; + } + plugin.installUrl = url; - var urlLower = url.toLowerCase(); - 1 === urlLower.indexOf("http:") && -1 === urlLower.indexOf("https:") && -1 === urlLower.indexOf("file:") && 0 !== url.indexOf(appRouter.baseUrl()) && (url = appRouter.baseUrl() + "/" + url); - var separatorIndex = Math.max(url.lastIndexOf("/"), url.lastIndexOf("\\")); + + var urlLower = url.toLowerCase(); + if (urlLower.indexOf('http:') === -1 && urlLower.indexOf('https:') === -1 && urlLower.indexOf('file:') === -1) { + if (url.indexOf(appRouter.baseUrl()) !== 0) { + + url = appRouter.baseUrl() + '/' + url; + } + } + + var separatorIndex = Math.max(url.lastIndexOf('/'), url.lastIndexOf('\\')); plugin.baseUrl = url.substring(0, separatorIndex); + var paths = {}; - paths[plugin.id] = plugin.baseUrl, requirejs.config({ + paths[plugin.id] = plugin.baseUrl; + + requirejs.config({ waitSeconds: 0, paths: paths - }), instance.register(plugin), plugin.getRoutes && plugin.getRoutes().forEach(function(route) { - definePluginRoute(instance, route, plugin) - }), "skin" === plugin.type ? resolve(plugin) : loadStrings(plugin, globalize).then(function() { - resolve(plugin) - }, reject) - }) - }) - }, PluginManager.prototype.register = function(obj) { - this.pluginsList.push(obj), events.trigger(this, "registered", [obj]) - }, PluginManager.prototype.ofType = function(type) { - return this.pluginsList.filter(function(o) { - return o.type === type - }) - }, PluginManager.prototype.plugins = function() { - return this.pluginsList - }, PluginManager.prototype.mapRoute = function(plugin, route) { - return "string" == typeof plugin && (plugin = this.pluginsList.filter(function(p) { - return (p.id || p.packageName) === plugin - })[0]), route = route.path || route, 0 === route.toLowerCase().indexOf("http") ? route : "/plugins/" + plugin.id + "/" + route - }, PluginManager.prototype.mapPath = function(plugin, path, addCacheParam) { - "string" == typeof plugin && (plugin = this.pluginsList.filter(function(p) { - return (p.id || p.packageName) === plugin - })[0]); - var url = plugin.baseUrl + "/" + path; - return addCacheParam && (url += -1 === url.indexOf("?") ? "?" : "&", url += "v=" + cacheParam), url - }, new PluginManager + }); + + instance.register(plugin); + + if (plugin.getRoutes) { + plugin.getRoutes().forEach(function (route) { + definePluginRoute(instance, route, plugin); + }); + } + + if (plugin.type === 'skin') { + + // translations won't be loaded for skins until needed + resolve(plugin); + } else { + + loadStrings(plugin, globalize).then(function () { + resolve(plugin); + }, reject); + } + }); + }); + }; + + // In lieu of automatic discovery, plugins will register dynamic objects + // Each object will have the following properties: + // name + // type (skin, screensaver, etc) + PluginManager.prototype.register = function (obj) { + + this.pluginsList.push(obj); + events.trigger(this, 'registered', [obj]); + }; + + PluginManager.prototype.ofType = function (type) { + + return this.pluginsList.filter(function (o) { + return o.type === type; + }); + }; + + PluginManager.prototype.plugins = function () { + return this.pluginsList; + }; + + PluginManager.prototype.mapRoute = function (plugin, route) { + + if (typeof plugin === 'string') { + plugin = this.pluginsList.filter(function (p) { + return (p.id || p.packageName) === plugin; + })[0]; + } + + route = route.path || route; + + if (route.toLowerCase().indexOf('http') === 0) { + return route; + } + + return '/plugins/' + plugin.id + '/' + route; + }; + + PluginManager.prototype.mapPath = function (plugin, path, addCacheParam) { + + if (typeof plugin === 'string') { + plugin = this.pluginsList.filter(function (p) { + return (p.id || p.packageName) === plugin; + })[0]; + } + + var url = plugin.baseUrl + '/' + path; + + if (addCacheParam) { + url += url.indexOf('?') === -1 ? '?' : '&'; + url += 'v=' + cacheParam; + } + + return url; + }; + + return new PluginManager(); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/polyfills/array.js b/src/bower_components/emby-webcomponents/polyfills/array.js index e32a7d9562..5caea62e3b 100644 --- a/src/bower_components/emby-webcomponents/polyfills/array.js +++ b/src/bower_components/emby-webcomponents/polyfills/array.js @@ -1,12 +1,25 @@ -Array.prototype.filter || (Array.prototype.filter = function(fun) { - "use strict"; - if (null == this) throw new TypeError; - var t = Object(this), - len = t.length >>> 0; - if ("function" != typeof fun) throw new TypeError; - for (var res = [], thisp = arguments[1], i = 0; i < len; i++) - if (i in t) { - var val = t[i]; - fun.call(thisp, val, i, t) && res.push(val) - } return res -}); \ No newline at end of file +if (!Array.prototype.filter) { + Array.prototype.filter = function (fun /*, thisp*/) { + "use strict"; + + if (this == null) + throw new TypeError(); + + var t = Object(this); + var len = t.length >>> 0; + if (typeof fun != "function") + throw new TypeError(); + + var res = []; + var thisp = arguments[1]; + for (var i = 0; i < len; i++) { + if (i in t) { + var val = t[i]; // in case fun mutates this + if (fun.call(thisp, val, i, t)) + res.push(val); + } + } + + return res; + }; +} \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/polyfills/bind.js b/src/bower_components/emby-webcomponents/polyfills/bind.js index 27b00f6eae..82495aa103 100644 --- a/src/bower_components/emby-webcomponents/polyfills/bind.js +++ b/src/bower_components/emby-webcomponents/polyfills/bind.js @@ -1,10 +1,27 @@ -Function.prototype.bind || (Function.prototype.bind = function(oThis) { - if ("function" != typeof this) throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); - var aArgs = Array.prototype.slice.call(arguments, 1), - fToBind = this, - fNOP = function() {}, - fBound = function() { - return fToBind.apply(this instanceof fNOP ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))) - }; - return this.prototype && (fNOP.prototype = this.prototype), fBound.prototype = new fNOP, fBound -}); \ No newline at end of file +if (!Function.prototype.bind) { + Function.prototype.bind = function (oThis) { + if (typeof this !== 'function') { + // closest thing possible to the ECMAScript 5 + // internal IsCallable function + throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); + } + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + fNOP = function () { }, + fBound = function () { + return fToBind.apply(this instanceof fNOP + ? this + : oThis, + aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + if (this.prototype) { + // Function.prototype doesn't have a prototype property + fNOP.prototype = this.prototype; + } + fBound.prototype = new fNOP(); + + return fBound; + }; +} \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/polyfills/objectassign.js b/src/bower_components/emby-webcomponents/polyfills/objectassign.js index 7883d26563..79568f8cba 100644 --- a/src/bower_components/emby-webcomponents/polyfills/objectassign.js +++ b/src/bower_components/emby-webcomponents/polyfills/objectassign.js @@ -1,12 +1,23 @@ -"function" != typeof Object.assign && function() { - Object.assign = function(target) { - "use strict"; - if (void 0 === target || null === target) throw new TypeError("Cannot convert undefined or null to object"); - for (var output = Object(target), index = 1; index < arguments.length; index++) { - var source = arguments[index]; - if (void 0 !== source && null !== source) - for (var nextKey in source) source.hasOwnProperty(nextKey) && (output[nextKey] = source[nextKey]) - } - return output - } -}(); \ No newline at end of file +if (typeof Object.assign != 'function') { + (function () { + Object.assign = function (target) { + 'use strict'; + if (target === undefined || target === null) { + throw new TypeError('Cannot convert undefined or null to object'); + } + + var output = Object(target); + for (var index = 1; index < arguments.length; index++) { + var source = arguments[index]; + if (source !== undefined && source !== null) { + for (var nextKey in source) { + if (source.hasOwnProperty(nextKey)) { + output[nextKey] = source[nextKey]; + } + } + } + } + return output; + }; + })(); +} \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/polyfills/raf.js b/src/bower_components/emby-webcomponents/polyfills/raf.js index 64b4a78435..a856f6a76a 100644 --- a/src/bower_components/emby-webcomponents/polyfills/raf.js +++ b/src/bower_components/emby-webcomponents/polyfills/raf.js @@ -1,13 +1,31 @@ -! function() { - for (var lastTime = 0, vendors = ["ms", "moz", "webkit", "o"], x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) window.requestAnimationFrame = window[vendors[x] + "RequestAnimationFrame"], window.cancelAnimationFrame = window[vendors[x] + "CancelAnimationFrame"] || window[vendors[x] + "CancelRequestAnimationFrame"]; - window.requestAnimationFrame || (window.requestAnimationFrame = function(callback, element) { - var currTime = (new Date).getTime(), - timeToCall = Math.max(0, 16 - (currTime - lastTime)), - id = window.setTimeout(function() { - callback(currTime + timeToCall) - }, timeToCall); - return lastTime = currTime + timeToCall, id - }), window.cancelAnimationFrame || (window.cancelAnimationFrame = function(id) { - clearTimeout(id) - }) -}(); \ No newline at end of file +// http://paulirish.com/2011/requestanimationframe-for-smart-animating/ +// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating + +// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel + +// MIT license + +(function () { + var lastTime = 0; + var vendors = ['ms', 'moz', 'webkit', 'o']; + for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { + window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; + window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] + || window[vendors[x] + 'CancelRequestAnimationFrame']; + } + + if (!window.requestAnimationFrame) + window.requestAnimationFrame = function (callback, element) { + var currTime = new Date().getTime(); + var timeToCall = Math.max(0, 16 - (currTime - lastTime)); + var id = window.setTimeout(function () { callback(currTime + timeToCall); }, + timeToCall); + lastTime = currTime + timeToCall; + return id; + }; + + if (!window.cancelAnimationFrame) + window.cancelAnimationFrame = function (id) { + clearTimeout(id); + }; +}()); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/prompt/nativeprompt.js b/src/bower_components/emby-webcomponents/prompt/nativeprompt.js index b38237c1f8..b0634bd428 100644 --- a/src/bower_components/emby-webcomponents/prompt/nativeprompt.js +++ b/src/bower_components/emby-webcomponents/prompt/nativeprompt.js @@ -1,16 +1,28 @@ -define([], function() { - "use strict"; +define([], function () { + 'use strict'; function replaceAll(str, find, replace) { - return str.split(find).join(replace) - } - return function(options) { - "string" == typeof options && (options = { - label: "", - text: options - }); - var label = replaceAll(options.label || "", "
", "\n"), - result = prompt(label, options.text || ""); - return result ? Promise.resolve(result) : Promise.reject(result) + + return str.split(find).join(replace); } + + return function (options) { + + if (typeof options === 'string') { + options = { + label: '', + text: options + }; + } + + var label = replaceAll(options.label || '', '
', '\n'); + + var result = prompt(label, options.text || ''); + + if (result) { + return Promise.resolve(result); + } else { + return Promise.reject(result); + } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/prompt/prompt.js b/src/bower_components/emby-webcomponents/prompt/prompt.js index d70ed5d6bc..6d3d6b246f 100644 --- a/src/bower_components/emby-webcomponents/prompt/prompt.js +++ b/src/bower_components/emby-webcomponents/prompt/prompt.js @@ -1,40 +1,104 @@ -define(["dialogHelper", "layoutManager", "scrollHelper", "globalize", "dom", "require", "material-icons", "emby-button", "paper-icon-button-light", "emby-input", "formDialogStyle"], function(dialogHelper, layoutManager, scrollHelper, globalize, dom, require) { - "use strict"; +define(['dialogHelper', 'layoutManager', 'scrollHelper', 'globalize', 'dom', 'require', 'material-icons', 'emby-button', 'paper-icon-button-light', 'emby-input', 'formDialogStyle'], function (dialogHelper, layoutManager, scrollHelper, globalize, dom, require) { + 'use strict'; function setInputProperties(dlg, options) { - var txtInput = dlg.querySelector("#txtInput"); - txtInput.label ? txtInput.label(options.label || "") : txtInput.setAttribute("label", options.label || ""), txtInput.value = options.value || "" + var txtInput = dlg.querySelector('#txtInput'); + + if (txtInput.label) { + txtInput.label(options.label || ''); + } else { + txtInput.setAttribute('label', options.label || ''); + } + txtInput.value = options.value || ''; } function showDialog(options, template) { + var dialogOptions = { - removeOnClose: !0, - scrollY: !1 + removeOnClose: true, + scrollY: false }; - layoutManager.tv && (dialogOptions.size = "fullscreen"); + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"), dlg.innerHTML = globalize.translateHtml(template, "sharedcomponents"), layoutManager.tv ? scrollHelper.centerFocus.on(dlg.querySelector(".formDialogContent"), !1) : (dlg.querySelector(".dialogContentInner").classList.add("dialogContentInner-mini"), dlg.classList.add("dialog-fullscreen-lowres")), dlg.querySelector(".btnCancel").addEventListener("click", function(e) { - dialogHelper.close(dlg) - }), dlg.querySelector(".formDialogHeaderTitle").innerHTML = options.title || "", options.description ? dlg.querySelector(".fieldDescription").innerHTML = options.description : dlg.querySelector(".fieldDescription").classList.add("hide"), setInputProperties(dlg, options); + + dlg.classList.add('formDialog'); + + dlg.innerHTML = globalize.translateHtml(template, 'sharedcomponents'); + + if (layoutManager.tv) { + scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false); + } else { + dlg.querySelector('.dialogContentInner').classList.add('dialogContentInner-mini'); + dlg.classList.add('dialog-fullscreen-lowres'); + } + + dlg.querySelector('.btnCancel').addEventListener('click', function (e) { + dialogHelper.close(dlg); + }); + + dlg.querySelector('.formDialogHeaderTitle').innerHTML = options.title || ''; + + if (options.description) { + dlg.querySelector('.fieldDescription').innerHTML = options.description; + } else { + dlg.querySelector('.fieldDescription').classList.add('hide'); + } + + setInputProperties(dlg, options); + var submitValue; - return dlg.querySelector("form").addEventListener("submit", function(e) { - return submitValue = dlg.querySelector("#txtInput").value, e.preventDefault(), e.stopPropagation(), setTimeout(function() { - dialogHelper.close(dlg) - }, 300), !1 - }), dlg.querySelector(".submitText").innerHTML = options.confirmText || globalize.translate("sharedcomponents#ButtonOk"), dlg.style.minWidth = Math.min(400, dom.getWindowSize().innerWidth - 50) + "px", dialogHelper.open(dlg).then(function() { - layoutManager.tv && scrollHelper.centerFocus.off(dlg.querySelector(".formDialogContent"), !1); + + dlg.querySelector('form').addEventListener('submit', function (e) { + + submitValue = dlg.querySelector('#txtInput').value; + e.preventDefault(); + e.stopPropagation(); + + // Important, don't close the dialog until after the form has completed submitting, or it will cause an error in Chrome + setTimeout(function () { + dialogHelper.close(dlg); + }, 300); + + return false; + }); + + dlg.querySelector('.submitText').innerHTML = options.confirmText || globalize.translate('sharedcomponents#ButtonOk'); + + dlg.style.minWidth = (Math.min(400, dom.getWindowSize().innerWidth - 50)) + 'px'; + + return dialogHelper.open(dlg).then(function () { + + if (layoutManager.tv) { + scrollHelper.centerFocus.off(dlg.querySelector('.formDialogContent'), false); + } + var value = submitValue; - return value || Promise.reject() - }) - } - return function(options) { - return new Promise(function(resolve, reject) { - require(["text!./prompt.template.html"], function(template) { - "string" == typeof options && (options = { - title: "", - text: options - }), showDialog(options, template).then(resolve, reject) - }) - }) + + if (value) { + return value; + } else { + return Promise.reject(); + } + }); } + + return function (options) { + + return new Promise(function (resolve, reject) { + require(['text!./prompt.template.html'], function (template) { + + if (typeof options === 'string') { + options = { + title: '', + text: options + }; + } + showDialog(options, template).then(resolve, reject); + }); + }); + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/qualityoptions.js b/src/bower_components/emby-webcomponents/qualityoptions.js index d0d7bae88c..df88f0fac8 100644 --- a/src/bower_components/emby-webcomponents/qualityoptions.js +++ b/src/bower_components/emby-webcomponents/qualityoptions.js @@ -1,214 +1,163 @@ -define(["globalize"], function(globalize) { - "use strict"; +define(['globalize'], function (globalize) { + 'use strict'; function getVideoQualityOptions(options) { - var maxStreamingBitrate = options.currentMaxBitrate, - videoWidth = options.videoWidth, - maxAllowedWidth = videoWidth || 4096, - qualityOptions = []; - maxAllowedWidth >= 3800 && (qualityOptions.push({ - name: "4K - 120 Mbps", - maxHeight: 2160, - bitrate: 12e7 - }), qualityOptions.push({ - name: "4K - 100 Mbps", - maxHeight: 2160, - bitrate: 1e8 - }), qualityOptions.push({ - name: "4K - 80 Mbps", - maxHeight: 2160, - bitrate: 8e7 - })), maxAllowedWidth >= 1900 ? (qualityOptions.push({ - name: "1080p - 60 Mbps", - maxHeight: 1080, - bitrate: 6e7 - }), qualityOptions.push({ - name: "1080p - 50 Mbps", - maxHeight: 1080, - bitrate: 5e7 - }), qualityOptions.push({ - name: "1080p - 40 Mbps", - maxHeight: 1080, - bitrate: 4e7 - }), qualityOptions.push({ - name: "1080p - 30 Mbps", - maxHeight: 1080, - bitrate: 3e7 - }), qualityOptions.push({ - name: "1080p - 25 Mbps", - maxHeight: 1080, - bitrate: 25e6 - }), qualityOptions.push({ - name: "1080p - 20 Mbps", - maxHeight: 1080, - bitrate: 2e7 - }), qualityOptions.push({ - name: "1080p - 15 Mbps", - maxHeight: 1080, - bitrate: 15e6 - }), qualityOptions.push({ - name: "1080p - 10 Mbps", - maxHeight: 1080, - bitrate: 10000001 - }), qualityOptions.push({ - name: "1080p - 8 Mbps", - maxHeight: 1080, - bitrate: 8000001 - }), qualityOptions.push({ - name: "1080p - 6 Mbps", - maxHeight: 1080, - bitrate: 6000001 - }), qualityOptions.push({ - name: "1080p - 5 Mbps", - maxHeight: 1080, - bitrate: 5000001 - }), qualityOptions.push({ - name: "1080p - 4 Mbps", - maxHeight: 1080, - bitrate: 4000002 - })) : maxAllowedWidth >= 1260 ? (qualityOptions.push({ - name: "720p - 10 Mbps", - maxHeight: 720, - bitrate: 1e7 - }), qualityOptions.push({ - name: "720p - 8 Mbps", - maxHeight: 720, - bitrate: 8e6 - }), qualityOptions.push({ - name: "720p - 6 Mbps", - maxHeight: 720, - bitrate: 6e6 - }), qualityOptions.push({ - name: "720p - 5 Mbps", - maxHeight: 720, - bitrate: 5e6 - })) : maxAllowedWidth >= 620 && (qualityOptions.push({ - name: "480p - 4 Mbps", - maxHeight: 480, - bitrate: 4000001 - }), qualityOptions.push({ - name: "480p - 3 Mbps", - maxHeight: 480, - bitrate: 3000001 - }), qualityOptions.push({ - name: "480p - 2.5 Mbps", - maxHeight: 480, - bitrate: 25e5 - }), qualityOptions.push({ - name: "480p - 2 Mbps", - maxHeight: 480, - bitrate: 2000001 - }), qualityOptions.push({ - name: "480p - 1.5 Mbps", - maxHeight: 480, - bitrate: 1500001 - })), maxAllowedWidth >= 1260 && (qualityOptions.push({ - name: "720p - 4 Mbps", - maxHeight: 720, - bitrate: 4e6 - }), qualityOptions.push({ - name: "720p - 3 Mbps", - maxHeight: 720, - bitrate: 3e6 - }), qualityOptions.push({ - name: "720p - 2 Mbps", - maxHeight: 720, - bitrate: 2e6 - }), qualityOptions.push({ - name: "720p - 1.5 Mbps", - maxHeight: 720, - bitrate: 15e5 - }), qualityOptions.push({ - name: "720p - 1 Mbps", - maxHeight: 720, - bitrate: 1000001 - })), qualityOptions.push({ - name: "480p - 1 Mbps", - maxHeight: 480, - bitrate: 1e6 - }), qualityOptions.push({ - name: "480p - 720 kbps", - maxHeight: 480, - bitrate: 72e4 - }), qualityOptions.push({ - name: "480p - 420 kbps", - maxHeight: 480, - bitrate: 42e4 - }), qualityOptions.push({ - name: "360p", - maxHeight: 360, - bitrate: 4e5 - }), qualityOptions.push({ - name: "240p", - maxHeight: 240, - bitrate: 32e4 - }), qualityOptions.push({ - name: "144p", - maxHeight: 144, - bitrate: 192e3 - }); + + var maxStreamingBitrate = options.currentMaxBitrate; + var videoWidth = options.videoWidth; + + var maxAllowedWidth = videoWidth || 4096; + //var maxAllowedHeight = videoHeight || 2304; + + var qualityOptions = []; + + if (maxAllowedWidth >= 3800) { + qualityOptions.push({ name: '4K - 120 Mbps', maxHeight: 2160, bitrate: 120000000 }); + qualityOptions.push({ name: '4K - 100 Mbps', maxHeight: 2160, bitrate: 100000000 }); + qualityOptions.push({ name: '4K - 80 Mbps', maxHeight: 2160, bitrate: 80000000 }); + } + + // Some 1080- videos are reported as 1912? + if (maxAllowedWidth >= 1900) { + + qualityOptions.push({ name: '1080p - 60 Mbps', maxHeight: 1080, bitrate: 60000000 }); + qualityOptions.push({ name: '1080p - 50 Mbps', maxHeight: 1080, bitrate: 50000000 }); + qualityOptions.push({ name: '1080p - 40 Mbps', maxHeight: 1080, bitrate: 40000000 }); + qualityOptions.push({ name: '1080p - 30 Mbps', maxHeight: 1080, bitrate: 30000000 }); + qualityOptions.push({ name: '1080p - 25 Mbps', maxHeight: 1080, bitrate: 25000000 }); + qualityOptions.push({ name: '1080p - 20 Mbps', maxHeight: 1080, bitrate: 20000000 }); + qualityOptions.push({ name: '1080p - 15 Mbps', maxHeight: 1080, bitrate: 15000000 }); + qualityOptions.push({ name: '1080p - 10 Mbps', maxHeight: 1080, bitrate: 10000001 }); + qualityOptions.push({ name: '1080p - 8 Mbps', maxHeight: 1080, bitrate: 8000001 }); + qualityOptions.push({ name: '1080p - 6 Mbps', maxHeight: 1080, bitrate: 6000001 }); + qualityOptions.push({ name: '1080p - 5 Mbps', maxHeight: 1080, bitrate: 5000001 }); + qualityOptions.push({ name: '1080p - 4 Mbps', maxHeight: 1080, bitrate: 4000002 }); + + } else if (maxAllowedWidth >= 1260) { + qualityOptions.push({ name: '720p - 10 Mbps', maxHeight: 720, bitrate: 10000000 }); + qualityOptions.push({ name: '720p - 8 Mbps', maxHeight: 720, bitrate: 8000000 }); + qualityOptions.push({ name: '720p - 6 Mbps', maxHeight: 720, bitrate: 6000000 }); + qualityOptions.push({ name: '720p - 5 Mbps', maxHeight: 720, bitrate: 5000000 }); + + } else if (maxAllowedWidth >= 620) { + qualityOptions.push({ name: '480p - 4 Mbps', maxHeight: 480, bitrate: 4000001 }); + qualityOptions.push({ name: '480p - 3 Mbps', maxHeight: 480, bitrate: 3000001 }); + qualityOptions.push({ name: '480p - 2.5 Mbps', maxHeight: 480, bitrate: 2500000 }); + qualityOptions.push({ name: '480p - 2 Mbps', maxHeight: 480, bitrate: 2000001 }); + qualityOptions.push({ name: '480p - 1.5 Mbps', maxHeight: 480, bitrate: 1500001 }); + } + + if (maxAllowedWidth >= 1260) { + qualityOptions.push({ name: '720p - 4 Mbps', maxHeight: 720, bitrate: 4000000 }); + qualityOptions.push({ name: '720p - 3 Mbps', maxHeight: 720, bitrate: 3000000 }); + qualityOptions.push({ name: '720p - 2 Mbps', maxHeight: 720, bitrate: 2000000 }); + + // The extra 1 is because they're keyed off the bitrate value + qualityOptions.push({ name: '720p - 1.5 Mbps', maxHeight: 720, bitrate: 1500000 }); + qualityOptions.push({ name: '720p - 1 Mbps', maxHeight: 720, bitrate: 1000001 }); + } + + qualityOptions.push({ name: '480p - 1 Mbps', maxHeight: 480, bitrate: 1000000 }); + qualityOptions.push({ name: '480p - 720 kbps', maxHeight: 480, bitrate: 720000 }); + qualityOptions.push({ name: '480p - 420 kbps', maxHeight: 480, bitrate: 420000 }); + qualityOptions.push({ name: '360p', maxHeight: 360, bitrate: 400000 }); + qualityOptions.push({ name: '240p', maxHeight: 240, bitrate: 320000 }); + qualityOptions.push({ name: '144p', maxHeight: 144, bitrate: 192000 }); + var autoQualityOption = { - name: globalize.translate("sharedcomponents#Auto"), + name: globalize.translate('sharedcomponents#Auto'), bitrate: 0, selected: options.isAutomaticBitrateEnabled }; - if (options.enableAuto && qualityOptions.push(autoQualityOption), maxStreamingBitrate) { - for (var selectedIndex = -1, i = 0, length = qualityOptions.length; i < length; i++) { - var option = qualityOptions[i]; - 1 === selectedIndex && option.bitrate <= maxStreamingBitrate && (selectedIndex = i) - } - 1 === selectedIndex && (selectedIndex = qualityOptions.length - 1); - var currentQualityOption = qualityOptions[selectedIndex]; - options.isAutomaticBitrateEnabled ? autoQualityOption.autoText = currentQualityOption.name : currentQualityOption.selected = !0 + + if (options.enableAuto) { + qualityOptions.push(autoQualityOption); } - return qualityOptions + + if (maxStreamingBitrate) { + var selectedIndex = -1; + for (var i = 0, length = qualityOptions.length; i < length; i++) { + + var option = qualityOptions[i]; + + if (selectedIndex === -1 && option.bitrate <= maxStreamingBitrate) { + selectedIndex = i; + } + } + + if (selectedIndex === -1) { + + selectedIndex = qualityOptions.length - 1; + } + + var currentQualityOption = qualityOptions[selectedIndex]; + + if (!options.isAutomaticBitrateEnabled) { + currentQualityOption.selected = true; + } else { + autoQualityOption.autoText = currentQualityOption.name; + } + } + + return qualityOptions; } function getAudioQualityOptions(options) { - var maxStreamingBitrate = options.currentMaxBitrate, - qualityOptions = []; - qualityOptions.push({ - name: "2 Mbps", - bitrate: 2e6 - }), qualityOptions.push({ - name: "1.5 Mbps", - bitrate: 15e5 - }), qualityOptions.push({ - name: "1 Mbps", - bitrate: 1e6 - }), qualityOptions.push({ - name: "320 kbps", - bitrate: 32e4 - }), qualityOptions.push({ - name: "256 kbps", - bitrate: 256e3 - }), qualityOptions.push({ - name: "192 kbps", - bitrate: 192e3 - }), qualityOptions.push({ - name: "128 kbps", - bitrate: 128e3 - }), qualityOptions.push({ - name: "96 kbps", - bitrate: 96e3 - }), qualityOptions.push({ - name: "64 kbps", - bitrate: 64e3 - }); + + var maxStreamingBitrate = options.currentMaxBitrate; + + var qualityOptions = []; + + qualityOptions.push({ name: '2 Mbps', bitrate: 2000000 }); + qualityOptions.push({ name: '1.5 Mbps', bitrate: 1500000 }); + qualityOptions.push({ name: '1 Mbps', bitrate: 1000000 }); + qualityOptions.push({ name: '320 kbps', bitrate: 320000 }); + qualityOptions.push({ name: '256 kbps', bitrate: 256000 }); + qualityOptions.push({ name: '192 kbps', bitrate: 192000 }); + qualityOptions.push({ name: '128 kbps', bitrate: 128000 }); + qualityOptions.push({ name: '96 kbps', bitrate: 96000 }); + qualityOptions.push({ name: '64 kbps', bitrate: 64000 }); + var autoQualityOption = { - name: globalize.translate("sharedcomponents#Auto"), + name: globalize.translate('sharedcomponents#Auto'), bitrate: 0, selected: options.isAutomaticBitrateEnabled }; - if (options.enableAuto && qualityOptions.push(autoQualityOption), maxStreamingBitrate) { - for (var selectedIndex = -1, i = 0, length = qualityOptions.length; i < length; i++) { - var option = qualityOptions[i]; - 1 === selectedIndex && option.bitrate <= maxStreamingBitrate && (selectedIndex = i) - } - 1 === selectedIndex && (selectedIndex = qualityOptions.length - 1); - var currentQualityOption = qualityOptions[selectedIndex]; - options.isAutomaticBitrateEnabled ? autoQualityOption.autoText = currentQualityOption.name : currentQualityOption.selected = !0 + + if (options.enableAuto) { + qualityOptions.push(autoQualityOption); } - return qualityOptions + + if (maxStreamingBitrate) { + var selectedIndex = -1; + for (var i = 0, length = qualityOptions.length; i < length; i++) { + + var option = qualityOptions[i]; + + if (selectedIndex === -1 && option.bitrate <= maxStreamingBitrate) { + selectedIndex = i; + } + } + + if (selectedIndex === -1) { + + selectedIndex = qualityOptions.length - 1; + } + + var currentQualityOption = qualityOptions[selectedIndex]; + + if (!options.isAutomaticBitrateEnabled) { + currentQualityOption.selected = true; + } else { + autoQualityOption.autoText = currentQualityOption.name; + } + } + + return qualityOptions; } + return { getVideoQualityOptions: getVideoQualityOptions, getAudioQualityOptions: getAudioQualityOptions - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/recordingcreator/recordingbutton.js b/src/bower_components/emby-webcomponents/recordingcreator/recordingbutton.js index a5b085284c..93c4c13abc 100644 --- a/src/bower_components/emby-webcomponents/recordingcreator/recordingbutton.js +++ b/src/bower_components/emby-webcomponents/recordingcreator/recordingbutton.js @@ -1,60 +1,116 @@ -define(["globalize", "connectionManager", "require", "loading", "apphost", "dom", "recordingHelper", "events", "registrationServices", "paper-icon-button-light", "emby-button", "css!./recordingfields"], function(globalize, connectionManager, require, loading, appHost, dom, recordingHelper, events, registrationServices) { - "use strict"; +define(['globalize', 'connectionManager', 'require', 'loading', 'apphost', 'dom', 'recordingHelper', 'events', 'registrationServices', 'paper-icon-button-light', 'emby-button', 'css!./recordingfields'], function (globalize, connectionManager, require, loading, appHost, dom, recordingHelper, events, registrationServices) { + 'use strict'; function onRecordingButtonClick(e) { + var item = this.item; + if (item) { - var serverId = item.ServerId, - programId = item.Id, - timerId = item.TimerId, - timerStatus = item.Status, - seriesTimerId = item.SeriesTimerId, - instance = this; - recordingHelper.toggleRecording(serverId, programId, timerId, timerStatus, seriesTimerId).then(function() { - instance.refresh(serverId, programId) - }) + + var serverId = item.ServerId; + var programId = item.Id; + var timerId = item.TimerId; + var timerStatus = item.Status; + var seriesTimerId = item.SeriesTimerId; + + var instance = this; + + recordingHelper.toggleRecording(serverId, programId, timerId, timerStatus, seriesTimerId).then(function () { + instance.refresh(serverId, programId); + }); } } function RecordingButton(options) { - this.options = options, options.item ? this.refreshItem(options.item) : options.itemId && options.serverId && this.refresh(options.itemId, options.serverId); + this.options = options; + + if (options.item) { + this.refreshItem(options.item); + } else if (options.itemId && options.serverId) { + this.refresh(options.itemId, options.serverId); + } var button = options.button; - button.querySelector("i").innerHTML = ""; + button.querySelector('i').innerHTML = ''; + var clickFn = onRecordingButtonClick.bind(this); - this.clickFn = clickFn, dom.addEventListener(button, "click", clickFn, { - passive: !0 - }) + this.clickFn = clickFn; + + dom.addEventListener(button, 'click', clickFn, { + passive: true + }); } function getIndicatorIcon(item) { + var status; - if ("SeriesTimer" === item.Type) return ""; - if (item.TimerId || item.SeriesTimerId) status = item.Status || "Cancelled"; + + if (item.Type === 'SeriesTimer') { + return ''; + } + else if (item.TimerId || item.SeriesTimerId) { + + status = item.Status || 'Cancelled'; + } + else if (item.Type === 'Timer') { + + status = item.Status; + } else { - if ("Timer" !== item.Type) return ""; - status = item.Status + return ''; } - return item.SeriesTimerId && "Cancelled" !== status ? "" : "" + + if (item.SeriesTimerId) { + + if (status !== 'Cancelled') { + return ''; + } + } + + return ''; } - return RecordingButton.prototype.refresh = function(serverId, itemId) { - var apiClient = connectionManager.getApiClient(serverId), - self = this; - apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function(item) { - self.refreshItem(item) - }) - }, RecordingButton.prototype.refreshItem = function(item) { - var options = this.options, - button = options.button; - this.item = item, button.querySelector("i").innerHTML = getIndicatorIcon(item), item.TimerId && "Cancelled" !== (item.Status || "Cancelled") ? button.classList.add("recordingIcon-active") : button.classList.remove("recordingIcon-active") - }, RecordingButton.prototype.destroy = function() { + + RecordingButton.prototype.refresh = function (serverId, itemId) { + + var apiClient = connectionManager.getApiClient(serverId); + var self = this; + apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) { + self.refreshItem(item); + }); + }; + + RecordingButton.prototype.refreshItem = function (item) { + var options = this.options; - if (options) { - var button = options.button, - clickFn = this.clickFn; - clickFn && dom.removeEventListener(button, "click", clickFn, { - passive: !0 - }) + var button = options.button; + this.item = item; + button.querySelector('i').innerHTML = getIndicatorIcon(item); + + if (item.TimerId && (item.Status || 'Cancelled') !== 'Cancelled') { + button.classList.add('recordingIcon-active'); + } else { + button.classList.remove('recordingIcon-active'); } - this.options = null, this.item = null - }, RecordingButton + }; + + RecordingButton.prototype.destroy = function () { + + var options = this.options; + + if (options) { + var button = options.button; + + var clickFn = this.clickFn; + + if (clickFn) { + dom.removeEventListener(button, 'click', clickFn, { + passive: true + }); + } + } + + this.options = null; + this.item = null; + }; + + return RecordingButton; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/recordingcreator/recordingcreator.css b/src/bower_components/emby-webcomponents/recordingcreator/recordingcreator.css index 91d68b6019..dc1ba9b31a 100644 --- a/src/bower_components/emby-webcomponents/recordingcreator/recordingcreator.css +++ b/src/bower_components/emby-webcomponents/recordingcreator/recordingcreator.css @@ -1,37 +1,27 @@ .recordingDialog-imageContainer { - -webkit-flex-shrink: 0; flex-shrink: 0; padding: 1em 1em 1em 0; - width: 25% + width: 25%; } .recordingDialog-img { - width: 100% + width: 100%; } .recordingDialog-itemName { - margin-top: .7em + margin-top: .7em; } .recordingDetailsContainer { - display: -webkit-box; - display: -webkit-flex; - display: flex + display: flex; } .recordingDetails { - -webkit-box-flex: 1; - -webkit-flex-grow: 1; - flex-grow: 1 + flex-grow: 1; } .recordingDetailText { - display: -webkit-box; - display: -webkit-flex; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - -webkit-flex-wrap: wrap; - flex-wrap: wrap -} \ No newline at end of file + flex-wrap: wrap; +} diff --git a/src/bower_components/emby-webcomponents/recordingcreator/recordingcreator.js b/src/bower_components/emby-webcomponents/recordingcreator/recordingcreator.js index 760cfdc3a9..2821946b6a 100644 --- a/src/bower_components/emby-webcomponents/recordingcreator/recordingcreator.js +++ b/src/bower_components/emby-webcomponents/recordingcreator/recordingcreator.js @@ -1,95 +1,206 @@ -define(["dialogHelper", "globalize", "layoutManager", "mediaInfo", "apphost", "connectionManager", "require", "loading", "scrollHelper", "datetime", "imageLoader", "recordingFields", "events", "emby-checkbox", "emby-button", "emby-collapse", "emby-input", "paper-icon-button-light", "css!./../formdialog", "css!./recordingcreator", "material-icons"], function(dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper, datetime, imageLoader, recordingFields, events) { - "use strict"; +define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'connectionManager', 'require', 'loading', 'scrollHelper', 'datetime', 'imageLoader', 'recordingFields', 'events', 'emby-checkbox', 'emby-button', 'emby-collapse', 'emby-input', 'paper-icon-button-light', 'css!./../formdialog', 'css!./recordingcreator', 'material-icons'], function (dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper, datetime, imageLoader, recordingFields, events) { + 'use strict'; + + var currentDialog; + var closeAction; + var currentRecordingFields; function closeDialog() { - dialogHelper.close(currentDialog) + + dialogHelper.close(currentDialog); } function init(context) { - context.querySelector(".btnPlay").addEventListener("click", function() { - closeAction = "play", closeDialog() - }), context.querySelector(".btnCancel").addEventListener("click", function() { - closeAction = null, closeDialog() - }) + + context.querySelector('.btnPlay').addEventListener('click', function () { + + closeAction = 'play'; + closeDialog(); + }); + + context.querySelector('.btnCancel').addEventListener('click', function () { + + closeAction = null; + closeDialog(); + }); } function getImageUrl(item, apiClient, imageHeight) { + var imageTags = item.ImageTags || {}; - return item.PrimaryImageTag && (imageTags.Primary = item.PrimaryImageTag), imageTags.Primary ? apiClient.getScaledImageUrl(item.Id, { - type: "Primary", - maxHeight: imageHeight, - tag: item.ImageTags.Primary - }) : imageTags.Thumb ? apiClient.getScaledImageUrl(item.Id, { - type: "Thumb", - maxHeight: imageHeight, - tag: item.ImageTags.Thumb - }) : null + + if (item.PrimaryImageTag) { + imageTags.Primary = item.PrimaryImageTag; + } + + if (imageTags.Primary) { + + return apiClient.getScaledImageUrl(item.Id, { + type: "Primary", + maxHeight: imageHeight, + tag: item.ImageTags.Primary + }); + } + else if (imageTags.Thumb) { + + return apiClient.getScaledImageUrl(item.Id, { + type: "Thumb", + maxHeight: imageHeight, + tag: item.ImageTags.Thumb + }); + } + + return null; } function renderRecording(context, defaultTimer, program, apiClient, refreshRecordingStateOnly) { + if (!refreshRecordingStateOnly) { - var imgUrl = getImageUrl(program, apiClient, 200), - imageContainer = context.querySelector(".recordingDialog-imageContainer"); - imgUrl ? (imageContainer.innerHTML = '', imageContainer.classList.remove("hide"), imageLoader.lazyChildren(imageContainer)) : (imageContainer.innerHTML = "", imageContainer.classList.add("hide")), context.querySelector(".recordingDialog-itemName").innerHTML = program.Name, context.querySelector(".formDialogHeaderTitle").innerHTML = program.Name, context.querySelector(".itemGenres").innerHTML = (program.Genres || []).join(" / "), context.querySelector(".itemOverview").innerHTML = program.Overview || ""; - var formDialogFooter = context.querySelector(".formDialogFooter"), - now = new Date; - now >= datetime.parseISO8601Date(program.StartDate, !0) && now < datetime.parseISO8601Date(program.EndDate, !0) ? formDialogFooter.classList.remove("hide") : formDialogFooter.classList.add("hide"), context.querySelector(".itemMiscInfoPrimary").innerHTML = mediaInfo.getPrimaryMediaInfoHtml(program) + var imgUrl = getImageUrl(program, apiClient, 200); + var imageContainer = context.querySelector('.recordingDialog-imageContainer'); + + if (imgUrl) { + imageContainer.innerHTML = ''; + imageContainer.classList.remove('hide'); + + imageLoader.lazyChildren(imageContainer); + } else { + imageContainer.innerHTML = ''; + imageContainer.classList.add('hide'); + } + + context.querySelector('.recordingDialog-itemName').innerHTML = program.Name; + context.querySelector('.formDialogHeaderTitle').innerHTML = program.Name; + context.querySelector('.itemGenres').innerHTML = (program.Genres || []).join(' / '); + context.querySelector('.itemOverview').innerHTML = program.Overview || ''; + + var formDialogFooter = context.querySelector('.formDialogFooter'); + var now = new Date(); + if (now >= datetime.parseISO8601Date(program.StartDate, true) && now < datetime.parseISO8601Date(program.EndDate, true)) { + formDialogFooter.classList.remove('hide'); + } else { + formDialogFooter.classList.add('hide'); + } + + context.querySelector('.itemMiscInfoPrimary').innerHTML = mediaInfo.getPrimaryMediaInfoHtml(program); } - context.querySelector(".itemMiscInfoSecondary").innerHTML = mediaInfo.getSecondaryMediaInfoHtml(program, {}), loading.hide() + + context.querySelector('.itemMiscInfoSecondary').innerHTML = mediaInfo.getSecondaryMediaInfoHtml(program, { + }); + + loading.hide(); } function reload(context, programId, serverId, refreshRecordingStateOnly) { + loading.show(); - var apiClient = connectionManager.getApiClient(serverId), - promise1 = apiClient.getNewLiveTvTimerDefaults({ - programId: programId - }), - promise2 = apiClient.getLiveTvProgram(programId, apiClient.getCurrentUserId()); - Promise.all([promise1, promise2]).then(function(responses) { - var defaults = responses[0], - program = responses[1]; - renderRecording(context, defaults, program, apiClient, refreshRecordingStateOnly) - }) + + var apiClient = connectionManager.getApiClient(serverId); + + var promise1 = apiClient.getNewLiveTvTimerDefaults({ programId: programId }); + var promise2 = apiClient.getLiveTvProgram(programId, apiClient.getCurrentUserId()); + + Promise.all([promise1, promise2]).then(function (responses) { + + var defaults = responses[0]; + var program = responses[1]; + + renderRecording(context, defaults, program, apiClient, refreshRecordingStateOnly); + }); } function executeCloseAction(action, programId, serverId) { - if ("play" === action) return void require(["playbackManager"], function(playbackManager) { - var apiClient = connectionManager.getApiClient(serverId); - apiClient.getLiveTvProgram(programId, apiClient.getCurrentUserId()).then(function(item) { - playbackManager.play({ - ids: [item.ChannelId], - serverId: serverId - }) - }) - }) + + if (action === 'play') { + + require(['playbackManager'], function (playbackManager) { + + var apiClient = connectionManager.getApiClient(serverId); + + apiClient.getLiveTvProgram(programId, apiClient.getCurrentUserId()).then(function (item) { + + playbackManager.play({ + ids: [item.ChannelId], + serverId: serverId + }); + }); + }); + return; + } } function showEditor(itemId, serverId) { - return new Promise(function(resolve, reject) { - closeAction = null, loading.show(), require(["text!./recordingcreator.template.html"], function(template) { - function onRecordingChanged() { - reload(dlg, itemId, serverId, !0) - } + + return new Promise(function (resolve, reject) { + + closeAction = null; + + loading.show(); + + require(['text!./recordingcreator.template.html'], function (template) { + var dialogOptions = { - removeOnClose: !0, - scrollY: !1 + removeOnClose: true, + scrollY: false }; - layoutManager.tv ? dialogOptions.size = "fullscreen" : dialogOptions.size = "small"; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'small'; + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"), dlg.classList.add("recordingDialog"); - var html = ""; - html += globalize.translateDocument(template, "sharedcomponents"), dlg.innerHTML = html, currentDialog = dlg, dlg.addEventListener("close", function() { - events.off(currentRecordingFields, "recordingchanged", onRecordingChanged), executeCloseAction(closeAction, itemId, serverId), currentRecordingFields && currentRecordingFields.hasChanged() ? resolve() : reject() - }), layoutManager.tv && scrollHelper.centerFocus.on(dlg.querySelector(".formDialogContent"), !1), init(dlg), reload(dlg, itemId, serverId), currentRecordingFields = new recordingFields({ - parent: dlg.querySelector(".recordingFields"), + + dlg.classList.add('formDialog'); + dlg.classList.add('recordingDialog'); + + var html = ''; + + html += globalize.translateDocument(template, 'sharedcomponents'); + + dlg.innerHTML = html; + + currentDialog = dlg; + + function onRecordingChanged() { + reload(dlg, itemId, serverId, true); + } + + dlg.addEventListener('close', function () { + + events.off(currentRecordingFields, 'recordingchanged', onRecordingChanged); + executeCloseAction(closeAction, itemId, serverId); + + if (currentRecordingFields && currentRecordingFields.hasChanged()) { + resolve(); + } else { + reject(); + } + }); + + if (layoutManager.tv) { + scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false); + } + + init(dlg); + + reload(dlg, itemId, serverId); + + currentRecordingFields = new recordingFields({ + parent: dlg.querySelector('.recordingFields'), programId: itemId, serverId: serverId - }), events.on(currentRecordingFields, "recordingchanged", onRecordingChanged), dialogHelper.open(dlg) - }) - }) + }); + + events.on(currentRecordingFields, 'recordingchanged', onRecordingChanged); + + dialogHelper.open(dlg); + }); + }); } - var currentDialog, closeAction, currentRecordingFields; + return { show: showEditor - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/recordingcreator/recordingeditor.js b/src/bower_components/emby-webcomponents/recordingcreator/recordingeditor.js index bcc900e918..4082e56d27 100644 --- a/src/bower_components/emby-webcomponents/recordingcreator/recordingeditor.js +++ b/src/bower_components/emby-webcomponents/recordingcreator/recordingeditor.js @@ -1,73 +1,164 @@ -define(["dialogHelper", "globalize", "layoutManager", "mediaInfo", "apphost", "connectionManager", "require", "loading", "scrollHelper", "imageLoader", "scrollStyles", "emby-button", "emby-collapse", "emby-input", "paper-icon-button-light", "css!./../formdialog", "css!./recordingcreator", "material-icons", "flexStyles"], function(dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper, imageLoader) { - "use strict"; +define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'connectionManager', 'require', 'loading', 'scrollHelper', 'imageLoader', 'scrollStyles', 'emby-button', 'emby-collapse', 'emby-input', 'paper-icon-button-light', 'css!./../formdialog', 'css!./recordingcreator', 'material-icons', 'flexStyles'], function (dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper, imageLoader) { + 'use strict'; + + var currentDialog; + var recordingDeleted = false; + var currentItemId; + var currentServerId; + var currentResolve; function deleteTimer(apiClient, timerId) { - return new Promise(function(resolve, reject) { - require(["recordingHelper"], function(recordingHelper) { - recordingHelper.cancelTimerWithConfirmation(timerId, apiClient.serverId()).then(resolve, reject) - }) - }) + + return new Promise(function (resolve, reject) { + + require(['recordingHelper'], function (recordingHelper) { + + recordingHelper.cancelTimerWithConfirmation(timerId, apiClient.serverId()).then(resolve, reject); + }); + }); } function renderTimer(context, item, apiClient) { - item.ProgramInfo; - context.querySelector("#txtPrePaddingMinutes").value = item.PrePaddingSeconds / 60, context.querySelector("#txtPostPaddingMinutes").value = item.PostPaddingSeconds / 60, loading.hide() + + var program = item.ProgramInfo || {}; + + context.querySelector('#txtPrePaddingMinutes').value = item.PrePaddingSeconds / 60; + context.querySelector('#txtPostPaddingMinutes').value = item.PostPaddingSeconds / 60; + + loading.hide(); } function closeDialog(isDeleted) { - recordingDeleted = isDeleted, dialogHelper.close(currentDialog) + + recordingDeleted = isDeleted; + + dialogHelper.close(currentDialog); } function onSubmit(e) { - var form = this, - apiClient = connectionManager.getApiClient(currentServerId); - return apiClient.getLiveTvTimer(currentItemId).then(function(item) { - item.PrePaddingSeconds = 60 * form.querySelector("#txtPrePaddingMinutes").value, item.PostPaddingSeconds = 60 * form.querySelector("#txtPostPaddingMinutes").value, apiClient.updateLiveTvTimer(item).then(currentResolve) - }), e.preventDefault(), !1 + + var form = this; + + var apiClient = connectionManager.getApiClient(currentServerId); + + apiClient.getLiveTvTimer(currentItemId).then(function (item) { + item.PrePaddingSeconds = form.querySelector('#txtPrePaddingMinutes').value * 60; + item.PostPaddingSeconds = form.querySelector('#txtPostPaddingMinutes').value * 60; + apiClient.updateLiveTvTimer(item).then(currentResolve); + }); + + e.preventDefault(); + + // Disable default form submission + return false; } function init(context) { - context.querySelector(".btnCancel").addEventListener("click", function() { - closeDialog(!1) - }), context.querySelector(".btnCancelRecording").addEventListener("click", function() { - deleteTimer(connectionManager.getApiClient(currentServerId), currentItemId).then(function() { - closeDialog(!0) - }) - }), context.querySelector("form").addEventListener("submit", onSubmit) + + context.querySelector('.btnCancel').addEventListener('click', function () { + + closeDialog(false); + }); + + context.querySelector('.btnCancelRecording').addEventListener('click', function () { + + var apiClient = connectionManager.getApiClient(currentServerId); + deleteTimer(apiClient, currentItemId).then(function () { + closeDialog(true); + }); + }); + + context.querySelector('form').addEventListener('submit', onSubmit); } function reload(context, id) { - loading.show(), currentItemId = id; + + loading.show(); + currentItemId = id; + var apiClient = connectionManager.getApiClient(currentServerId); - apiClient.getLiveTvTimer(id).then(function(result) { - renderTimer(context, result, apiClient), loading.hide() - }) + apiClient.getLiveTvTimer(id).then(function (result) { + + renderTimer(context, result, apiClient); + loading.hide(); + }); } function showEditor(itemId, serverId, options) { - return new Promise(function(resolve, reject) { - recordingDeleted = !1, currentServerId = serverId, loading.show(), options = options || {}, currentResolve = resolve, require(["text!./recordingeditor.template.html"], function(template) { + + return new Promise(function (resolve, reject) { + + recordingDeleted = false; + currentServerId = serverId; + loading.show(); + options = options || {}; + currentResolve = resolve; + + require(['text!./recordingeditor.template.html'], function (template) { + var dialogOptions = { - removeOnClose: !0, - scrollY: !1 + removeOnClose: true, + scrollY: false }; - layoutManager.tv && (dialogOptions.size = "fullscreen"); + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"), dlg.classList.add("recordingDialog"), layoutManager.tv || (dlg.style["min-width"] = "20%", dlg.classList.add("dialog-fullscreen-lowres")); - var html = ""; - html += globalize.translateDocument(template, "sharedcomponents"), dlg.innerHTML = html, !1 === options.enableCancel && dlg.querySelector(".formDialogFooter").classList.add("hide"), currentDialog = dlg, dlg.addEventListener("closing", function() { - recordingDeleted || dlg.querySelector(".btnSubmit").click() - }), dlg.addEventListener("close", function() { - recordingDeleted && resolve({ - updated: !0, - deleted: !0 - }) - }), layoutManager.tv && scrollHelper.centerFocus.on(dlg.querySelector(".formDialogContent"), !1), init(dlg), reload(dlg, itemId), dialogHelper.open(dlg) - }) - }) + + dlg.classList.add('formDialog'); + dlg.classList.add('recordingDialog'); + + if (!layoutManager.tv) { + dlg.style['min-width'] = '20%'; + dlg.classList.add('dialog-fullscreen-lowres'); + } + + var html = ''; + + html += globalize.translateDocument(template, 'sharedcomponents'); + + dlg.innerHTML = html; + + if (options.enableCancel === false) { + dlg.querySelector('.formDialogFooter').classList.add('hide'); + } + + currentDialog = dlg; + + dlg.addEventListener('closing', function () { + + if (!recordingDeleted) { + dlg.querySelector('.btnSubmit').click(); + } + }); + + dlg.addEventListener('close', function () { + + if (recordingDeleted) { + resolve({ + updated: true, + deleted: true + }); + } + }); + + if (layoutManager.tv) { + scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false); + } + + init(dlg); + + reload(dlg, itemId); + + dialogHelper.open(dlg); + }); + }); } - var currentDialog, currentItemId, currentServerId, currentResolve, recordingDeleted = !1; + return { show: showEditor - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/recordingcreator/recordingfields.css b/src/bower_components/emby-webcomponents/recordingcreator/recordingfields.css index e48272d4a4..c8492f5298 100644 --- a/src/bower_components/emby-webcomponents/recordingcreator/recordingfields.css +++ b/src/bower_components/emby-webcomponents/recordingcreator/recordingfields.css @@ -1,12 +1,12 @@ .recordingButton { margin-left: 0; - min-width: 10em + min-width: 10em; } .recordingIcon-active { - color: #c33 + color: #cc3333; } .recordSeriesContainer { - margin-bottom: .8em + margin-bottom: .8em; } \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/recordingcreator/recordingfields.js b/src/bower_components/emby-webcomponents/recordingcreator/recordingfields.js index 735951b300..fd4b2c288a 100644 --- a/src/bower_components/emby-webcomponents/recordingcreator/recordingfields.js +++ b/src/bower_components/emby-webcomponents/recordingcreator/recordingfields.js @@ -1,168 +1,362 @@ -define(["globalize", "connectionManager", "serverNotifications", "require", "loading", "apphost", "dom", "recordingHelper", "events", "registrationServices", "paper-icon-button-light", "emby-button", "css!./recordingfields", "flexStyles"], function(globalize, connectionManager, serverNotifications, require, loading, appHost, dom, recordingHelper, events, registrationServices) { - "use strict"; +define(['globalize', 'connectionManager', 'serverNotifications', 'require', 'loading', 'apphost', 'dom', 'recordingHelper', 'events', 'registrationServices', 'paper-icon-button-light', 'emby-button', 'css!./recordingfields', 'flexStyles'], function (globalize, connectionManager, serverNotifications, require, loading, appHost, dom, recordingHelper, events, registrationServices) { + 'use strict'; function getRegistration(apiClient, feature) { + return registrationServices.validateFeature(feature, { - showDialog: !1, - viewOnly: !0 - }) + showDialog: false, + viewOnly: true + }); } function showConvertRecordingsUnlockMessage(context, apiClient) { - getRegistration(apiClient, getDvrFeatureCode()).then(function() { - context.querySelector(".convertRecordingsContainer").classList.add("hide") - }, function() { - context.querySelector(".convertRecordingsContainer").classList.remove("hide") - }) + + getRegistration(apiClient, getDvrFeatureCode()).then(function () { + + context.querySelector('.convertRecordingsContainer').classList.add('hide'); + }, function () { + context.querySelector('.convertRecordingsContainer').classList.remove('hide'); + }); } function showSeriesRecordingFields(context, programId, apiClient) { - getRegistration(apiClient, getDvrFeatureCode()).then(function() { - context.querySelector(".supporterContainer").classList.add("hide"), context.querySelector(".convertRecordingsContainer").classList.add("hide"), context.querySelector(".recordSeriesContainer").classList.remove("hide") - }, function() { - context.querySelector(".supporterContainerText").innerHTML = globalize.translate("sharedcomponents#MessageActiveSubscriptionRequiredSeriesRecordings"), context.querySelector(".supporterContainer").classList.remove("hide"), context.querySelector(".recordSeriesContainer").classList.add("hide"), context.querySelector(".convertRecordingsContainer").classList.add("hide") - }) + + getRegistration(apiClient, getDvrFeatureCode()).then(function () { + + context.querySelector('.supporterContainer').classList.add('hide'); + context.querySelector('.convertRecordingsContainer').classList.add('hide'); + context.querySelector('.recordSeriesContainer').classList.remove('hide'); + + }, function () { + + context.querySelector('.supporterContainerText').innerHTML = globalize.translate('sharedcomponents#MessageActiveSubscriptionRequiredSeriesRecordings'); + context.querySelector('.supporterContainer').classList.remove('hide'); + context.querySelector('.recordSeriesContainer').classList.add('hide'); + context.querySelector('.convertRecordingsContainer').classList.add('hide'); + }); } function getDvrFeatureCode() { - return "dvr" + + return 'dvr'; } function showSingleRecordingFields(context, programId, apiClient) { - getRegistration(apiClient, getDvrFeatureCode()).then(function() { - context.querySelector(".supporterContainer").classList.add("hide"), showConvertRecordingsUnlockMessage(context, apiClient) - }, function() { - context.querySelector(".supporterContainerText").innerHTML = globalize.translate("sharedcomponents#DvrSubscriptionRequired"), context.querySelector(".supporterContainer").classList.remove("hide"), context.querySelector(".convertRecordingsContainer").classList.add("hide") - }) + + getRegistration(apiClient, getDvrFeatureCode()).then(function () { + + context.querySelector('.supporterContainer').classList.add('hide'); + showConvertRecordingsUnlockMessage(context, apiClient); + + }, function () { + + context.querySelector('.supporterContainerText').innerHTML = globalize.translate('sharedcomponents#DvrSubscriptionRequired'); + context.querySelector('.supporterContainer').classList.remove('hide'); + context.querySelector('.convertRecordingsContainer').classList.add('hide'); + }); } function showRecordingFieldsContainer(context, programId, apiClient) { - getRegistration(apiClient, getDvrFeatureCode()).then(function() { - context.querySelector(".recordingFields").classList.remove("hide") - }, function() { - context.querySelector(".recordingFields").classList.add("hide") - }) + + getRegistration(apiClient, getDvrFeatureCode()).then(function () { + + context.querySelector('.recordingFields').classList.remove('hide'); + + }, function () { + + context.querySelector('.recordingFields').classList.add('hide'); + }); } function loadData(parent, program, apiClient) { - program.IsSeries ? (parent.querySelector(".recordSeriesContainer").classList.remove("hide"), showSeriesRecordingFields(parent, program.Id, apiClient)) : (parent.querySelector(".recordSeriesContainer").classList.add("hide"), showSingleRecordingFields(parent, program.Id, apiClient)), program.SeriesTimerId ? (parent.querySelector(".btnManageSeriesRecording").classList.remove("hide"), parent.querySelector(".seriesRecordingButton .recordingIcon").classList.add("recordingIcon-active"), parent.querySelector(".seriesRecordingButton .buttonText").innerHTML = globalize.translate("sharedcomponents#CancelSeries")) : (parent.querySelector(".btnManageSeriesRecording").classList.add("hide"), parent.querySelector(".seriesRecordingButton .recordingIcon").classList.remove("recordingIcon-active"), parent.querySelector(".seriesRecordingButton .buttonText").innerHTML = globalize.translate("sharedcomponents#RecordSeries")), program.TimerId && "Cancelled" !== program.Status ? (parent.querySelector(".btnManageRecording").classList.remove("hide"), parent.querySelector(".singleRecordingButton .recordingIcon").classList.add("recordingIcon-active"), "InProgress" === program.Status ? parent.querySelector(".singleRecordingButton .buttonText").innerHTML = globalize.translate("sharedcomponents#StopRecording") : parent.querySelector(".singleRecordingButton .buttonText").innerHTML = globalize.translate("sharedcomponents#DoNotRecord")) : (parent.querySelector(".btnManageRecording").classList.add("hide"), parent.querySelector(".singleRecordingButton .recordingIcon").classList.remove("recordingIcon-active"), parent.querySelector(".singleRecordingButton .buttonText").innerHTML = globalize.translate("sharedcomponents#Record")) + + if (program.IsSeries) { + parent.querySelector('.recordSeriesContainer').classList.remove('hide'); + showSeriesRecordingFields(parent, program.Id, apiClient); + } else { + parent.querySelector('.recordSeriesContainer').classList.add('hide'); + showSingleRecordingFields(parent, program.Id, apiClient); + } + + if (program.SeriesTimerId) { + parent.querySelector('.btnManageSeriesRecording').classList.remove('hide'); + parent.querySelector('.seriesRecordingButton .recordingIcon').classList.add('recordingIcon-active'); + parent.querySelector('.seriesRecordingButton .buttonText').innerHTML = globalize.translate('sharedcomponents#CancelSeries'); + } else { + parent.querySelector('.btnManageSeriesRecording').classList.add('hide'); + parent.querySelector('.seriesRecordingButton .recordingIcon').classList.remove('recordingIcon-active'); + parent.querySelector('.seriesRecordingButton .buttonText').innerHTML = globalize.translate('sharedcomponents#RecordSeries'); + } + + if (program.TimerId && program.Status !== 'Cancelled') { + parent.querySelector('.btnManageRecording').classList.remove('hide'); + parent.querySelector('.singleRecordingButton .recordingIcon').classList.add('recordingIcon-active'); + + if (program.Status === 'InProgress') { + parent.querySelector('.singleRecordingButton .buttonText').innerHTML = globalize.translate('sharedcomponents#StopRecording'); + } else { + parent.querySelector('.singleRecordingButton .buttonText').innerHTML = globalize.translate('sharedcomponents#DoNotRecord'); + } + + } else { + parent.querySelector('.btnManageRecording').classList.add('hide'); + parent.querySelector('.singleRecordingButton .recordingIcon').classList.remove('recordingIcon-active'); + parent.querySelector('.singleRecordingButton .buttonText').innerHTML = globalize.translate('sharedcomponents#Record'); + } } function fetchData(instance) { - var options = instance.options, - apiClient = connectionManager.getApiClient(options.serverId); - return showRecordingFieldsContainer(options.parent, options.programId, apiClient), apiClient.getLiveTvProgram(options.programId, apiClient.getCurrentUserId()).then(function(program) { - instance.TimerId = program.TimerId, instance.Status = program.Status, instance.SeriesTimerId = program.SeriesTimerId, loadData(options.parent, program, apiClient) - }) + + var options = instance.options; + var apiClient = connectionManager.getApiClient(options.serverId); + + showRecordingFieldsContainer(options.parent, options.programId, apiClient); + + return apiClient.getLiveTvProgram(options.programId, apiClient.getCurrentUserId()).then(function (program) { + + instance.TimerId = program.TimerId; + instance.Status = program.Status; + instance.SeriesTimerId = program.SeriesTimerId; + + loadData(options.parent, program, apiClient); + }); } function onTimerChangedExternally(e, apiClient, data) { - var options = this.options, - refresh = !1; - data.Id && this.TimerId === data.Id && (refresh = !0), data.ProgramId && options && options.programId === data.ProgramId && (refresh = !0), refresh && this.refresh() + + var options = this.options; + var refresh = false; + + if (data.Id) { + if (this.TimerId === data.Id) { + refresh = true; + } + } + if (data.ProgramId && options) { + if (options.programId === data.ProgramId) { + refresh = true; + } + } + + if (refresh) { + this.refresh(); + } } function onSeriesTimerChangedExternally(e, apiClient, data) { - var options = this.options, - refresh = !1; - data.Id && this.SeriesTimerId === data.Id && (refresh = !0), data.ProgramId && options && options.programId === data.ProgramId && (refresh = !0), refresh && this.refresh() + + var options = this.options; + var refresh = false; + + if (data.Id) { + if (this.SeriesTimerId === data.Id) { + refresh = true; + } + } + if (data.ProgramId && options) { + if (options.programId === data.ProgramId) { + refresh = true; + } + } + + if (refresh) { + this.refresh(); + } } function RecordingEditor(options) { - this.options = options, this.embed(); + this.options = options; + this.embed(); + var timerChangedHandler = onTimerChangedExternally.bind(this); - this.timerChangedHandler = timerChangedHandler, events.on(serverNotifications, "TimerCreated", timerChangedHandler), events.on(serverNotifications, "TimerCancelled", timerChangedHandler); + this.timerChangedHandler = timerChangedHandler; + + events.on(serverNotifications, 'TimerCreated', timerChangedHandler); + events.on(serverNotifications, 'TimerCancelled', timerChangedHandler); + var seriesTimerChangedHandler = onSeriesTimerChangedExternally.bind(this); - this.seriesTimerChangedHandler = seriesTimerChangedHandler, events.on(serverNotifications, "SeriesTimerCreated", seriesTimerChangedHandler), events.on(serverNotifications, "SeriesTimerCancelled", seriesTimerChangedHandler) + this.seriesTimerChangedHandler = seriesTimerChangedHandler; + + events.on(serverNotifications, 'SeriesTimerCreated', seriesTimerChangedHandler); + events.on(serverNotifications, 'SeriesTimerCancelled', seriesTimerChangedHandler); } function onSupporterButtonClick() { - registrationServices.showPremiereInfo() + registrationServices.showPremiereInfo(); } function onManageRecordingClick(e) { + var options = this.options; - if (this.TimerId && "Cancelled" !== this.Status) { - var self = this; - require(["recordingEditor"], function(recordingEditor) { - recordingEditor.show(self.TimerId, options.serverId, { - enableCancel: !1 - }).then(function() { - self.changed = !0 - }) - }) + + if (!this.TimerId || this.Status === 'Cancelled') { + return; } + + var self = this; + + require(['recordingEditor'], function (recordingEditor) { + + recordingEditor.show(self.TimerId, options.serverId, { + + enableCancel: false + + }).then(function () { + self.changed = true; + }); + }); } function onManageSeriesRecordingClick(e) { + var options = this.options; - if (this.SeriesTimerId) { - var self = this; - require(["seriesRecordingEditor"], function(seriesRecordingEditor) { - seriesRecordingEditor.show(self.SeriesTimerId, options.serverId, { - enableCancel: !1 - }).then(function() { - self.changed = !0 - }) - }) + + if (!this.SeriesTimerId) { + return; } + + var self = this; + + require(['seriesRecordingEditor'], function (seriesRecordingEditor) { + + seriesRecordingEditor.show(self.SeriesTimerId, options.serverId, { + + enableCancel: false + + }).then(function () { + self.changed = true; + }); + }); } function onRecordChange(e) { - this.changed = !0; - var self = this, - options = this.options, - apiClient = connectionManager.getApiClient(options.serverId), - button = dom.parentWithTag(e.target, "BUTTON"), - isChecked = !button.querySelector("i").classList.contains("recordingIcon-active"), - hasEnabledTimer = this.TimerId && "Cancelled" !== this.Status; - isChecked ? hasEnabledTimer || (loading.show(), recordingHelper.createRecording(apiClient, options.programId, !1).then(function() { - events.trigger(self, "recordingchanged"), fetchData(self), loading.hide() - })) : hasEnabledTimer && (loading.show(), recordingHelper.cancelTimer(apiClient, this.TimerId, !0).then(function() { - events.trigger(self, "recordingchanged"), fetchData(self), loading.hide() - })) + + this.changed = true; + + var self = this; + var options = this.options; + var apiClient = connectionManager.getApiClient(options.serverId); + + var button = dom.parentWithTag(e.target, 'BUTTON'); + var isChecked = !button.querySelector('i').classList.contains('recordingIcon-active'); + + var hasEnabledTimer = this.TimerId && this.Status !== 'Cancelled'; + + if (isChecked) { + if (!hasEnabledTimer) { + loading.show(); + recordingHelper.createRecording(apiClient, options.programId, false).then(function () { + events.trigger(self, 'recordingchanged'); + fetchData(self); + loading.hide(); + }); + } + } else { + if (hasEnabledTimer) { + loading.show(); + recordingHelper.cancelTimer(apiClient, this.TimerId, true).then(function () { + events.trigger(self, 'recordingchanged'); + fetchData(self); + loading.hide(); + }); + } + } } function sendToast(msg) { - require(["toast"], function(toast) { - toast(msg) - }) + require(['toast'], function (toast) { + toast(msg); + }); } function onRecordSeriesChange(e) { - this.changed = !0; - var self = this, - options = this.options, - apiClient = connectionManager.getApiClient(options.serverId); - if (dom.parentWithTag(e.target, "BUTTON").querySelector("i").classList.contains("recordingIcon-active")) showSingleRecordingFields(options.parent, options.programId, apiClient), this.SeriesTimerId && apiClient.cancelLiveTvSeriesTimer(this.SeriesTimerId).then(function() { - sendToast(globalize.translate("sharedcomponents#RecordingCancelled")), fetchData(self) - }); - else if (showSeriesRecordingFields(options.parent, options.programId, apiClient), !this.SeriesTimerId) { - var promise = this.TimerId ? recordingHelper.changeRecordingToSeries(apiClient, this.TimerId, options.programId) : recordingHelper.createRecording(apiClient, options.programId, !0); - promise.then(function() { - fetchData(self) - }) + + this.changed = true; + + var self = this; + var options = this.options; + var apiClient = connectionManager.getApiClient(options.serverId); + + var button = dom.parentWithTag(e.target, 'BUTTON'); + var isChecked = !button.querySelector('i').classList.contains('recordingIcon-active'); + + if (isChecked) { + showSeriesRecordingFields(options.parent, options.programId, apiClient); + + if (!this.SeriesTimerId) { + + var promise = this.TimerId ? + recordingHelper.changeRecordingToSeries(apiClient, this.TimerId, options.programId) : + recordingHelper.createRecording(apiClient, options.programId, true); + + promise.then(function () { + fetchData(self); + }); + } + } else { + + showSingleRecordingFields(options.parent, options.programId, apiClient); + + if (this.SeriesTimerId) { + apiClient.cancelLiveTvSeriesTimer(this.SeriesTimerId).then(function () { + sendToast(globalize.translate('sharedcomponents#RecordingCancelled')); + fetchData(self); + }); + } } } - return RecordingEditor.prototype.embed = function() { + + RecordingEditor.prototype.embed = function () { + var self = this; - return new Promise(function(resolve, reject) { - require(["text!./recordingfields.template.html"], function(template) { - var options = self.options, - context = options.parent; - context.innerHTML = globalize.translateDocument(template, "sharedcomponents"); - for (var supporterButtons = context.querySelectorAll(".btnSupporter"), i = 0, length = supporterButtons.length; i < length; i++) supporterButtons[i].addEventListener("click", onSupporterButtonClick); - context.querySelector(".singleRecordingButton").addEventListener("click", onRecordChange.bind(self)), context.querySelector(".seriesRecordingButton").addEventListener("click", onRecordSeriesChange.bind(self)), context.querySelector(".btnManageRecording").addEventListener("click", onManageRecordingClick.bind(self)), context.querySelector(".btnManageSeriesRecording").addEventListener("click", onManageSeriesRecordingClick.bind(self)), fetchData(self).then(resolve) - }) - }) - }, RecordingEditor.prototype.hasChanged = function() { - return this.changed - }, RecordingEditor.prototype.refresh = function() { - fetchData(this) - }, RecordingEditor.prototype.destroy = function() { + + return new Promise(function (resolve, reject) { + + require(['text!./recordingfields.template.html'], function (template) { + + var options = self.options; + var context = options.parent; + context.innerHTML = globalize.translateDocument(template, 'sharedcomponents'); + + var supporterButtons = context.querySelectorAll('.btnSupporter'); + for (var i = 0, length = supporterButtons.length; i < length; i++) { + supporterButtons[i].addEventListener('click', onSupporterButtonClick); + } + + context.querySelector('.singleRecordingButton').addEventListener('click', onRecordChange.bind(self)); + context.querySelector('.seriesRecordingButton').addEventListener('click', onRecordSeriesChange.bind(self)); + context.querySelector('.btnManageRecording').addEventListener('click', onManageRecordingClick.bind(self)); + context.querySelector('.btnManageSeriesRecording').addEventListener('click', onManageSeriesRecordingClick.bind(self)); + + fetchData(self).then(resolve); + }); + }); + }; + + RecordingEditor.prototype.hasChanged = function () { + + return this.changed; + }; + + RecordingEditor.prototype.refresh = function () { + + fetchData(this); + }; + + RecordingEditor.prototype.destroy = function () { + var timerChangedHandler = this.timerChangedHandler; - this.timerChangedHandler = null, events.off(serverNotifications, "TimerCreated", timerChangedHandler), events.off(serverNotifications, "TimerCancelled", timerChangedHandler); + this.timerChangedHandler = null; + + events.off(serverNotifications, 'TimerCreated', timerChangedHandler); + events.off(serverNotifications, 'TimerCancelled', timerChangedHandler); + var seriesTimerChangedHandler = this.seriesTimerChangedHandler; - this.seriesTimerChangedHandler = null, events.off(serverNotifications, "SeriesTimerCreated", seriesTimerChangedHandler), events.off(serverNotifications, "SeriesTimerCancelled", seriesTimerChangedHandler) - }, RecordingEditor + this.seriesTimerChangedHandler = null; + + events.off(serverNotifications, 'SeriesTimerCreated', seriesTimerChangedHandler); + events.off(serverNotifications, 'SeriesTimerCancelled', seriesTimerChangedHandler); + }; + + return RecordingEditor; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/recordingcreator/recordinghelper.js b/src/bower_components/emby-webcomponents/recordingcreator/recordinghelper.js index 6839b3ea3c..363111a9a9 100644 --- a/src/bower_components/emby-webcomponents/recordingcreator/recordinghelper.js +++ b/src/bower_components/emby-webcomponents/recordingcreator/recordinghelper.js @@ -1,116 +1,221 @@ -define(["globalize", "loading", "connectionManager", "registrationServices"], function(globalize, loading, connectionManager, registrationServices) { - "use strict"; +define(['globalize', 'loading', 'connectionManager', 'registrationServices'], function (globalize, loading, connectionManager, registrationServices) { + 'use strict'; function changeRecordingToSeries(apiClient, timerId, programId, confirmTimerCancellation) { - return loading.show(), apiClient.getItem(apiClient.getCurrentUserId(), programId).then(function(item) { - return item.IsSeries ? apiClient.getNewLiveTvTimerDefaults({ - programId: programId - }).then(function(timerDefaults) { - return apiClient.createLiveTvSeriesTimer(timerDefaults).then(function() { - loading.hide(), sendToast(globalize.translate("sharedcomponents#SeriesRecordingScheduled")) - }) - }) : confirmTimerCancellation ? cancelTimerWithConfirmation(timerId, apiClient.serverId()) : cancelTimer(apiClient.serverId(), timerId, !0) - }) + + loading.show(); + + return apiClient.getItem(apiClient.getCurrentUserId(), programId).then(function (item) { + + if (item.IsSeries) { + // create series + return apiClient.getNewLiveTvTimerDefaults({ programId: programId }).then(function (timerDefaults) { + + return apiClient.createLiveTvSeriesTimer(timerDefaults).then(function () { + + loading.hide(); + sendToast(globalize.translate('sharedcomponents#SeriesRecordingScheduled')); + }); + }); + } else { + // cancel + if (confirmTimerCancellation) { + return cancelTimerWithConfirmation(timerId, apiClient.serverId()); + } + + return cancelTimer(apiClient.serverId(), timerId, true); + } + }); } function cancelTimerWithConfirmation(timerId, serverId) { - return new Promise(function(resolve, reject) { - require(["confirm"], function(confirm) { + + return new Promise(function (resolve, reject) { + + require(['confirm'], function (confirm) { + confirm({ - text: globalize.translate("sharedcomponents#MessageConfirmRecordingCancellation"), - primary: "cancel", - confirmText: globalize.translate("sharedcomponents#HeaderCancelRecording"), - cancelText: globalize.translate("sharedcomponents#HeaderKeepRecording") - }).then(function() { - loading.show(), cancelTimer(connectionManager.getApiClient(serverId), timerId, !0).then(resolve, reject) - }, reject) - }) - }) + + text: globalize.translate('sharedcomponents#MessageConfirmRecordingCancellation'), + primary: 'cancel', + confirmText: globalize.translate('sharedcomponents#HeaderCancelRecording'), + cancelText: globalize.translate('sharedcomponents#HeaderKeepRecording') + + }).then(function () { + + loading.show(); + + var apiClient = connectionManager.getApiClient(serverId); + cancelTimer(apiClient, timerId, true).then(resolve, reject); + + }, reject); + }); + }); } function cancelSeriesTimerWithConfirmation(timerId, serverId) { - return new Promise(function(resolve, reject) { - require(["confirm"], function(confirm) { + + return new Promise(function (resolve, reject) { + + require(['confirm'], function (confirm) { + confirm({ - text: globalize.translate("sharedcomponents#MessageConfirmRecordingCancellation"), - primary: "cancel", - confirmText: globalize.translate("sharedcomponents#HeaderCancelSeries"), - cancelText: globalize.translate("sharedcomponents#HeaderKeepSeries") - }).then(function() { - loading.show(), connectionManager.getApiClient(serverId).cancelLiveTvSeriesTimer(timerId).then(function() { - require(["toast"], function(toast) { - toast(globalize.translate("sharedcomponents#SeriesCancelled")) - }), loading.hide(), resolve() - }, reject) - }, reject) - }) - }) + + text: globalize.translate('sharedcomponents#MessageConfirmRecordingCancellation'), + primary: 'cancel', + confirmText: globalize.translate('sharedcomponents#HeaderCancelSeries'), + cancelText: globalize.translate('sharedcomponents#HeaderKeepSeries') + + }).then(function () { + + loading.show(); + + var apiClient = connectionManager.getApiClient(serverId); + apiClient.cancelLiveTvSeriesTimer(timerId).then(function () { + + require(['toast'], function (toast) { + toast(globalize.translate('sharedcomponents#SeriesCancelled')); + }); + + loading.hide(); + resolve(); + }, reject); + + }, reject); + }); + }); } function cancelTimer(apiClient, timerId, hideLoading) { - return loading.show(), apiClient.cancelLiveTvTimer(timerId).then(function() { - !1 !== hideLoading && (loading.hide(), sendToast(globalize.translate("sharedcomponents#RecordingCancelled"))) - }) + loading.show(); + return apiClient.cancelLiveTvTimer(timerId).then(function () { + + if (hideLoading !== false) { + loading.hide(); + sendToast(globalize.translate('sharedcomponents#RecordingCancelled')); + } + }); } function createRecording(apiClient, programId, isSeries) { - return loading.show(), apiClient.getNewLiveTvTimerDefaults({ - programId: programId - }).then(function(item) { - return (isSeries ? apiClient.createLiveTvSeriesTimer(item) : apiClient.createLiveTvTimer(item)).then(function() { - loading.hide(), sendToast(globalize.translate("sharedcomponents#RecordingScheduled")) - }) - }) + + loading.show(); + return apiClient.getNewLiveTvTimerDefaults({ programId: programId }).then(function (item) { + + var promise = isSeries ? + apiClient.createLiveTvSeriesTimer(item) : + apiClient.createLiveTvTimer(item); + + return promise.then(function () { + + loading.hide(); + sendToast(globalize.translate('sharedcomponents#RecordingScheduled')); + }); + }); } function sendToast(msg) { - require(["toast"], function(toast) { - toast(msg) - }) + require(['toast'], function (toast) { + toast(msg); + }); } function showMultiCancellationPrompt(serverId, programId, timerId, timerStatus, seriesTimerId) { - return new Promise(function(resolve, reject) { - require(["dialog"], function(dialog) { + return new Promise(function (resolve, reject) { + + require(['dialog'], function (dialog) { + var items = []; + items.push({ - name: globalize.translate("sharedcomponents#HeaderKeepRecording"), - id: "cancel", - type: "submit" - }), "InProgress" === timerStatus ? items.push({ - name: globalize.translate("sharedcomponents#HeaderStopRecording"), - id: "canceltimer", - type: "cancel" - }) : items.push({ - name: globalize.translate("sharedcomponents#HeaderCancelRecording"), - id: "canceltimer", - type: "cancel" - }), items.push({ - name: globalize.translate("sharedcomponents#HeaderCancelSeries"), - id: "cancelseriestimer", - type: "cancel" - }), dialog({ - text: globalize.translate("sharedcomponents#MessageConfirmRecordingCancellation"), + name: globalize.translate('sharedcomponents#HeaderKeepRecording'), + id: 'cancel', + type: 'submit' + }); + + if (timerStatus === 'InProgress') { + items.push({ + name: globalize.translate('sharedcomponents#HeaderStopRecording'), + id: 'canceltimer', + type: 'cancel' + }); + } else { + items.push({ + name: globalize.translate('sharedcomponents#HeaderCancelRecording'), + id: 'canceltimer', + type: 'cancel' + }); + } + + items.push({ + name: globalize.translate('sharedcomponents#HeaderCancelSeries'), + id: 'cancelseriestimer', + type: 'cancel' + }); + + dialog({ + + text: globalize.translate('sharedcomponents#MessageConfirmRecordingCancellation'), buttons: items - }).then(function(result) { + + }).then(function (result) { + var apiClient = connectionManager.getApiClient(serverId); - "canceltimer" === result ? (loading.show(), cancelTimer(apiClient, timerId, !0).then(resolve, reject)) : "cancelseriestimer" === result ? (loading.show(), apiClient.cancelLiveTvSeriesTimer(seriesTimerId).then(function() { - require(["toast"], function(toast) { - toast(globalize.translate("sharedcomponents#SeriesCancelled")) - }), loading.hide(), resolve() - }, reject)) : resolve() - }, reject) - }) - }) + + if (result === 'canceltimer') { + loading.show(); + + cancelTimer(apiClient, timerId, true).then(resolve, reject); + } + else if (result === 'cancelseriestimer') { + + loading.show(); + + apiClient.cancelLiveTvSeriesTimer(seriesTimerId).then(function () { + + require(['toast'], function (toast) { + toast(globalize.translate('sharedcomponents#SeriesCancelled')); + }); + + loading.hide(); + resolve(); + }, reject); + } else { + resolve(); + } + + }, reject); + }); + }); } function toggleRecording(serverId, programId, timerId, timerStatus, seriesTimerId) { - return registrationServices.validateFeature("dvr").then(function() { - var apiClient = connectionManager.getApiClient(serverId), - hasTimer = timerId && "Cancelled" !== timerStatus; - return seriesTimerId && hasTimer ? showMultiCancellationPrompt(serverId, programId, timerId, timerStatus, seriesTimerId) : hasTimer && programId ? changeRecordingToSeries(apiClient, timerId, programId, !0) : programId ? createRecording(apiClient, programId) : Promise.reject() - }) + + return registrationServices.validateFeature('dvr').then(function () { + var apiClient = connectionManager.getApiClient(serverId); + + var hasTimer = timerId && timerStatus !== 'Cancelled'; + + if (seriesTimerId && hasTimer) { + + // cancel + return showMultiCancellationPrompt(serverId, programId, timerId, timerStatus, seriesTimerId); + + } else if (hasTimer && programId) { + + // change to series recording, if possible + // otherwise cancel individual recording + return changeRecordingToSeries(apiClient, timerId, programId, true); + + } else if (programId) { + // schedule recording + return createRecording(apiClient, programId); + } else { + return Promise.reject(); + } + }); } + return { cancelTimer: cancelTimer, createRecording: createRecording, @@ -118,5 +223,5 @@ define(["globalize", "loading", "connectionManager", "registrationServices"], fu toggleRecording: toggleRecording, cancelTimerWithConfirmation: cancelTimerWithConfirmation, cancelSeriesTimerWithConfirmation: cancelSeriesTimerWithConfirmation - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/recordingcreator/seriesrecordingeditor.js b/src/bower_components/emby-webcomponents/recordingcreator/seriesrecordingeditor.js index 38b1a49652..bb92877fc4 100644 --- a/src/bower_components/emby-webcomponents/recordingcreator/seriesrecordingeditor.js +++ b/src/bower_components/emby-webcomponents/recordingcreator/seriesrecordingeditor.js @@ -1,98 +1,270 @@ -define(["dialogHelper", "globalize", "layoutManager", "mediaInfo", "apphost", "connectionManager", "require", "loading", "scrollHelper", "imageLoader", "datetime", "scrollStyles", "emby-button", "emby-checkbox", "emby-input", "emby-select", "paper-icon-button-light", "css!./../formdialog", "css!./recordingcreator", "material-icons", "flexStyles"], function(dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper, imageLoader, datetime) { - "use strict"; +define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'connectionManager', 'require', 'loading', 'scrollHelper', 'imageLoader', 'datetime', 'scrollStyles', 'emby-button', 'emby-checkbox', 'emby-input', 'emby-select', 'paper-icon-button-light', 'css!./../formdialog', 'css!./recordingcreator', 'material-icons', 'flexStyles'], function (dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper, imageLoader, datetime) { + 'use strict'; + + var currentDialog; + var recordingUpdated = false; + var recordingDeleted = false; + var currentItemId; + var currentServerId; function deleteTimer(apiClient, timerId) { - return new Promise(function(resolve, reject) { - require(["recordingHelper"], function(recordingHelper) { - recordingHelper.cancelSeriesTimerWithConfirmation(timerId, apiClient.serverId()).then(resolve, reject) - }) - }) + + return new Promise(function (resolve, reject) { + + require(['recordingHelper'], function (recordingHelper) { + + recordingHelper.cancelSeriesTimerWithConfirmation(timerId, apiClient.serverId()).then(resolve, reject); + }); + }); } function renderTimer(context, item, apiClient) { - item.ProgramInfo; - context.querySelector("#txtPrePaddingMinutes").value = item.PrePaddingSeconds / 60, context.querySelector("#txtPostPaddingMinutes").value = item.PostPaddingSeconds / 60, context.querySelector(".selectChannels").value = item.RecordAnyChannel ? "all" : "one", context.querySelector(".selectAirTime").value = item.RecordAnyTime ? "any" : "original", context.querySelector(".selectShowType").value = item.RecordNewOnly ? "new" : "all", context.querySelector(".chkSkipEpisodesInLibrary").checked = item.SkipEpisodesInLibrary, context.querySelector(".selectKeepUpTo").value = item.KeepUpTo || 0, item.ChannelName || item.ChannelNumber ? context.querySelector(".optionChannelOnly").innerHTML = globalize.translate("sharedcomponents#ChannelNameOnly", item.ChannelName || item.ChannelNumber) : context.querySelector(".optionChannelOnly").innerHTML = globalize.translate("sharedcomponents#OneChannel"), context.querySelector(".optionAroundTime").innerHTML = globalize.translate("sharedcomponents#AroundTime", datetime.getDisplayTime(datetime.parseISO8601Date(item.StartDate))), loading.hide() + + var program = item.ProgramInfo || {}; + + context.querySelector('#txtPrePaddingMinutes').value = item.PrePaddingSeconds / 60; + context.querySelector('#txtPostPaddingMinutes').value = item.PostPaddingSeconds / 60; + + context.querySelector('.selectChannels').value = item.RecordAnyChannel ? 'all' : 'one'; + context.querySelector('.selectAirTime').value = item.RecordAnyTime ? 'any' : 'original'; + + context.querySelector('.selectShowType').value = item.RecordNewOnly ? 'new' : 'all'; + context.querySelector('.chkSkipEpisodesInLibrary').checked = item.SkipEpisodesInLibrary; + context.querySelector('.selectKeepUpTo').value = item.KeepUpTo || 0; + + if (item.ChannelName || item.ChannelNumber) { + context.querySelector('.optionChannelOnly').innerHTML = globalize.translate('sharedcomponents#ChannelNameOnly', item.ChannelName || item.ChannelNumber); + } else { + context.querySelector('.optionChannelOnly').innerHTML = globalize.translate('sharedcomponents#OneChannel'); + } + + context.querySelector('.optionAroundTime').innerHTML = globalize.translate('sharedcomponents#AroundTime', datetime.getDisplayTime(datetime.parseISO8601Date(item.StartDate))); + + loading.hide(); } function closeDialog(isDeleted) { - recordingUpdated = !0, recordingDeleted = isDeleted, dialogHelper.close(currentDialog) + + recordingUpdated = true; + recordingDeleted = isDeleted; + + dialogHelper.close(currentDialog); } function onSubmit(e) { - var form = this, - apiClient = connectionManager.getApiClient(currentServerId); - return apiClient.getLiveTvSeriesTimer(currentItemId).then(function(item) { - item.PrePaddingSeconds = 60 * form.querySelector("#txtPrePaddingMinutes").value, item.PostPaddingSeconds = 60 * form.querySelector("#txtPostPaddingMinutes").value, item.RecordAnyChannel = "all" === form.querySelector(".selectChannels").value, item.RecordAnyTime = "any" === form.querySelector(".selectAirTime").value, item.RecordNewOnly = "new" === form.querySelector(".selectShowType").value, item.SkipEpisodesInLibrary = form.querySelector(".chkSkipEpisodesInLibrary").checked, item.KeepUpTo = form.querySelector(".selectKeepUpTo").value, apiClient.updateLiveTvSeriesTimer(item) - }), e.preventDefault(), !1 + + var form = this; + + var apiClient = connectionManager.getApiClient(currentServerId); + + apiClient.getLiveTvSeriesTimer(currentItemId).then(function (item) { + + item.PrePaddingSeconds = form.querySelector('#txtPrePaddingMinutes').value * 60; + item.PostPaddingSeconds = form.querySelector('#txtPostPaddingMinutes').value * 60; + item.RecordAnyChannel = form.querySelector('.selectChannels').value === 'all'; + item.RecordAnyTime = form.querySelector('.selectAirTime').value === 'any'; + item.RecordNewOnly = form.querySelector('.selectShowType').value === 'new'; + item.SkipEpisodesInLibrary = form.querySelector('.chkSkipEpisodesInLibrary').checked; + item.KeepUpTo = form.querySelector('.selectKeepUpTo').value; + + apiClient.updateLiveTvSeriesTimer(item); + }); + + e.preventDefault(); + + // Disable default form submission + return false; } function init(context) { - fillKeepUpTo(context), context.querySelector(".btnCancel").addEventListener("click", function() { - closeDialog(!1) - }), context.querySelector(".btnCancelRecording").addEventListener("click", function() { - deleteTimer(connectionManager.getApiClient(currentServerId), currentItemId).then(function() { - closeDialog(!0) - }) - }), context.querySelector("form").addEventListener("submit", onSubmit) + + fillKeepUpTo(context); + + context.querySelector('.btnCancel').addEventListener('click', function () { + + closeDialog(false); + }); + + context.querySelector('.btnCancelRecording').addEventListener('click', function () { + + var apiClient = connectionManager.getApiClient(currentServerId); + deleteTimer(apiClient, currentItemId).then(function () { + closeDialog(true); + }); + }); + + context.querySelector('form').addEventListener('submit', onSubmit); } function reload(context, id) { + var apiClient = connectionManager.getApiClient(currentServerId); - loading.show(), "string" == typeof id ? (currentItemId = id, apiClient.getLiveTvSeriesTimer(id).then(function(result) { - renderTimer(context, result, apiClient), loading.hide() - })) : id && (currentItemId = id.Id, renderTimer(context, id, apiClient), loading.hide()) + + loading.show(); + if (typeof id === 'string') { + currentItemId = id; + + apiClient.getLiveTvSeriesTimer(id).then(function (result) { + + renderTimer(context, result, apiClient); + loading.hide(); + }); + } else if (id) { + + currentItemId = id.Id; + + renderTimer(context, id, apiClient); + loading.hide(); + } } function fillKeepUpTo(context) { - for (var html = "", i = 0; i <= 50; i++) { - var text; - text = 0 === i ? globalize.translate("sharedcomponents#AsManyAsPossible") : 1 === i ? globalize.translate("sharedcomponents#ValueOneEpisode") : globalize.translate("sharedcomponents#ValueEpisodeCount", i), html += '" - } - context.querySelector(".selectKeepUpTo").innerHTML = html - } + var html = ''; + + for (var i = 0; i <= 50; i++) { + + var text; + + if (i === 0) { + text = globalize.translate('sharedcomponents#AsManyAsPossible'); + } else if (i === 1) { + text = globalize.translate('sharedcomponents#ValueOneEpisode'); + } else { + text = globalize.translate('sharedcomponents#ValueEpisodeCount', i); + } + + html += ''; + } + + context.querySelector('.selectKeepUpTo').innerHTML = html; + } + function onFieldChange(e) { - this.querySelector(".btnSubmit").click() + this.querySelector('.btnSubmit').click(); } function embed(itemId, serverId, options) { - recordingUpdated = !1, recordingDeleted = !1, currentServerId = serverId, loading.show(), options = options || {}, require(["text!./seriesrecordingeditor.template.html"], function(template) { + + recordingUpdated = false; + recordingDeleted = false; + currentServerId = serverId; + loading.show(); + options = options || {}; + + require(['text!./seriesrecordingeditor.template.html'], function (template) { + var dialogOptions = { - removeOnClose: !0, - scrollY: !1 + removeOnClose: true, + scrollY: false }; - layoutManager.tv ? dialogOptions.size = "fullscreen" : dialogOptions.size = "small"; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'small'; + } + var dlg = options.context; - dlg.classList.add("hide"), dlg.innerHTML = globalize.translateDocument(template, "sharedcomponents"), dlg.querySelector(".formDialogHeader").classList.add("hide"), dlg.querySelector(".formDialogFooter").classList.add("hide"), dlg.querySelector(".formDialogContent").className = "", dlg.querySelector(".dialogContentInner").className = "", dlg.classList.remove("hide"), dlg.removeEventListener("change", onFieldChange), dlg.addEventListener("change", onFieldChange), currentDialog = dlg, init(dlg), reload(dlg, itemId) - }) + + dlg.classList.add('hide'); + dlg.innerHTML = globalize.translateDocument(template, 'sharedcomponents'); + + dlg.querySelector('.formDialogHeader').classList.add('hide'); + dlg.querySelector('.formDialogFooter').classList.add('hide'); + dlg.querySelector('.formDialogContent').className = ''; + dlg.querySelector('.dialogContentInner').className = ''; + dlg.classList.remove('hide'); + + dlg.removeEventListener('change', onFieldChange); + dlg.addEventListener('change', onFieldChange); + + currentDialog = dlg; + + init(dlg); + + reload(dlg, itemId); + }); } function showEditor(itemId, serverId, options) { - return new Promise(function(resolve, reject) { - recordingUpdated = !1, recordingDeleted = !1, currentServerId = serverId, loading.show(), options = options || {}, require(["text!./seriesrecordingeditor.template.html"], function(template) { + + return new Promise(function (resolve, reject) { + + recordingUpdated = false; + recordingDeleted = false; + currentServerId = serverId; + loading.show(); + options = options || {}; + + require(['text!./seriesrecordingeditor.template.html'], function (template) { + var dialogOptions = { - removeOnClose: !0, - scrollY: !1 + removeOnClose: true, + scrollY: false }; - layoutManager.tv ? dialogOptions.size = "fullscreen" : dialogOptions.size = "small"; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'small'; + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"), dlg.classList.add("recordingDialog"), layoutManager.tv || (dlg.style["min-width"] = "20%"); - var html = ""; - html += globalize.translateDocument(template, "sharedcomponents"), dlg.innerHTML = html, !1 === options.enableCancel && dlg.querySelector(".formDialogFooter").classList.add("hide"), currentDialog = dlg, dlg.addEventListener("closing", function() { - recordingDeleted || this.querySelector(".btnSubmit").click() - }), dlg.addEventListener("close", function() { - recordingUpdated ? resolve({ - updated: !0, - deleted: recordingDeleted - }) : reject() - }), layoutManager.tv && scrollHelper.centerFocus.on(dlg.querySelector(".formDialogContent"), !1), init(dlg), reload(dlg, itemId), dialogHelper.open(dlg) - }) - }) + + dlg.classList.add('formDialog'); + dlg.classList.add('recordingDialog'); + + if (!layoutManager.tv) { + dlg.style['min-width'] = '20%'; + } + + var html = ''; + + html += globalize.translateDocument(template, 'sharedcomponents'); + + dlg.innerHTML = html; + + if (options.enableCancel === false) { + dlg.querySelector('.formDialogFooter').classList.add('hide'); + } + + currentDialog = dlg; + + dlg.addEventListener('closing', function () { + + if (!recordingDeleted) { + this.querySelector('.btnSubmit').click(); + } + }); + + dlg.addEventListener('close', function () { + + if (recordingUpdated) { + resolve({ + updated: true, + deleted: recordingDeleted + }); + } else { + reject(); + } + }); + + if (layoutManager.tv) { + scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false); + } + + init(dlg); + + reload(dlg, itemId); + + dialogHelper.open(dlg); + }); + }); } - var currentDialog, currentItemId, currentServerId, recordingUpdated = !1, - recordingDeleted = !1; + return { show: showEditor, embed: embed - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/refreshdialog/refreshdialog.js b/src/bower_components/emby-webcomponents/refreshdialog/refreshdialog.js index 1b35092781..2165960d78 100644 --- a/src/bower_components/emby-webcomponents/refreshdialog/refreshdialog.js +++ b/src/bower_components/emby-webcomponents/refreshdialog/refreshdialog.js @@ -1,65 +1,175 @@ -define(["shell", "dialogHelper", "loading", "layoutManager", "connectionManager", "appRouter", "globalize", "emby-input", "emby-checkbox", "paper-icon-button-light", "emby-select", "material-icons", "css!./../formdialog", "emby-button"], function(shell, dialogHelper, loading, layoutManager, connectionManager, appRouter, globalize) { - "use strict"; +define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'connectionManager', 'appRouter', 'globalize', 'emby-input', 'emby-checkbox', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button'], function (shell, dialogHelper, loading, layoutManager, connectionManager, appRouter, globalize) { + 'use strict'; function parentWithClass(elem, className) { - for (; !elem.classList || !elem.classList.contains(className);) - if (!(elem = elem.parentNode)) return null; - return elem + + while (!elem.classList || !elem.classList.contains(className)) { + elem = elem.parentNode; + + if (!elem) { + return null; + } + } + + return elem; } function getEditorHtml() { - var html = ""; - return html += '
', html += '
', html += '
', html += '
', html += '", html += "
", html += '", html += '
', html += globalize.translate("sharedcomponents#RefreshDialogHelp"), html += "
", html += '', html += "
", html += '
', html += '", html += "
", html += "
", html += "
", html += "
" + + var html = ''; + + html += '
'; + html += '
'; + html += '
'; + + html += '
'; + html += ''; + html += '
'; + + html += ''; + + html += '
'; + html += globalize.translate('sharedcomponents#RefreshDialogHelp'); + html += '
'; + + html += ''; + + html += '
'; + html += '
'; + html += ''; + html += '
'; + + html += '
'; + html += '
'; + html += '
'; + + return html; } function centerFocus(elem, horiz, on) { - require(["scrollHelper"], function(scrollHelper) { - var fn = on ? "on" : "off"; - scrollHelper.centerFocus[fn](elem, horiz) - }) + require(['scrollHelper'], function (scrollHelper) { + var fn = on ? 'on' : 'off'; + scrollHelper.centerFocus[fn](elem, horiz); + }); } function onSubmit(e) { + loading.show(); - var instance = this, - dlg = parentWithClass(e.target, "dialog"), - options = instance.options, - apiClient = connectionManager.getApiClient(options.serverId), - replaceAllMetadata = "all" === dlg.querySelector("#selectMetadataRefreshMode").value, - mode = "scan" === dlg.querySelector("#selectMetadataRefreshMode").value ? "Default" : "FullRefresh", - replaceAllImages = "FullRefresh" === mode && dlg.querySelector(".chkReplaceImages").checked; - return options.itemIds.forEach(function(itemId) { + + var instance = this; + var dlg = parentWithClass(e.target, 'dialog'); + var options = instance.options; + + var apiClient = connectionManager.getApiClient(options.serverId); + + var replaceAllMetadata = dlg.querySelector('#selectMetadataRefreshMode').value === 'all'; + + var mode = dlg.querySelector('#selectMetadataRefreshMode').value === 'scan' ? 'Default' : 'FullRefresh'; + var replaceAllImages = mode === 'FullRefresh' && dlg.querySelector('.chkReplaceImages').checked; + + options.itemIds.forEach(function (itemId) { apiClient.refreshItem(itemId, { - Recursive: !0, + + Recursive: true, ImageRefreshMode: mode, MetadataRefreshMode: mode, ReplaceAllImages: replaceAllImages, ReplaceAllMetadata: replaceAllMetadata - }) - }), dialogHelper.close(dlg), require(["toast"], function(toast) { - toast(globalize.translate("sharedcomponents#RefreshQueued")) - }), loading.hide(), e.preventDefault(), !1 + }); + }); + + dialogHelper.close(dlg); + + require(['toast'], function (toast) { + toast(globalize.translate('sharedcomponents#RefreshQueued')); + }); + + loading.hide(); + + e.preventDefault(); + return false; } function RefreshDialog(options) { - this.options = options + this.options = options; } - return RefreshDialog.prototype.show = function() { + + RefreshDialog.prototype.show = function () { + var dialogOptions = { - removeOnClose: !0, - scrollY: !1 + removeOnClose: true, + scrollY: false }; - layoutManager.tv ? dialogOptions.size = "fullscreen" : dialogOptions.size = "small"; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'small'; + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"); - var html = "", - title = globalize.translate("sharedcomponents#RefreshMetadata"); - return html += '
', html += '', html += '

', html += title, html += "

", html += "
", html += getEditorHtml(), dlg.innerHTML = html, dlg.querySelector("form").addEventListener("submit", onSubmit.bind(this)), dlg.querySelector("#selectMetadataRefreshMode").addEventListener("change", function() { - "scan" === this.value ? dlg.querySelector(".fldReplaceExistingImages").classList.add("hide") : dlg.querySelector(".fldReplaceExistingImages").classList.remove("hide") - }), this.options.mode && (dlg.querySelector("#selectMetadataRefreshMode").value = this.options.mode), dlg.querySelector("#selectMetadataRefreshMode").dispatchEvent(new CustomEvent("change")), dlg.querySelector(".btnCancel").addEventListener("click", function() { - dialogHelper.close(dlg) - }), layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !0), new Promise(function(resolve, reject) { - layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !1), dlg.addEventListener("close", resolve), dialogHelper.open(dlg) - }) - }, RefreshDialog + + dlg.classList.add('formDialog'); + + var html = ''; + var title = globalize.translate('sharedcomponents#RefreshMetadata'); + + html += '
'; + html += ''; + html += '

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

'; + + html += '
'; + + html += getEditorHtml(); + + dlg.innerHTML = html; + + dlg.querySelector('form').addEventListener('submit', onSubmit.bind(this)); + + dlg.querySelector('#selectMetadataRefreshMode').addEventListener('change', function () { + + if (this.value === 'scan') { + dlg.querySelector('.fldReplaceExistingImages').classList.add('hide'); + } else { + dlg.querySelector('.fldReplaceExistingImages').classList.remove('hide'); + } + }); + + if (this.options.mode) { + dlg.querySelector('#selectMetadataRefreshMode').value = this.options.mode; + } + + dlg.querySelector('#selectMetadataRefreshMode').dispatchEvent(new CustomEvent('change')); + + dlg.querySelector('.btnCancel').addEventListener('click', function () { + + dialogHelper.close(dlg); + }); + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, true); + } + + return new Promise(function (resolve, reject) { + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, false); + } + + dlg.addEventListener('close', resolve); + dialogHelper.open(dlg); + }); + }; + + return RefreshDialog; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/registrationservices/registrationservices.js b/src/bower_components/emby-webcomponents/registrationservices/registrationservices.js index f9e96460d4..e753054a16 100644 --- a/src/bower_components/emby-webcomponents/registrationservices/registrationservices.js +++ b/src/bower_components/emby-webcomponents/registrationservices/registrationservices.js @@ -1,301 +1,16 @@ -define(["appSettings", "loading", "apphost", "iapManager", "events", "shell", "globalize", "dialogHelper", "connectionManager", "layoutManager", "emby-button", "emby-linkbutton"], function(appSettings, loading, appHost, iapManager, events, shell, globalize, dialogHelper, connectionManager, layoutManager) { - "use strict"; - - function alertText(options) { - return new Promise(function(resolve, reject) { - require(["alert"], function(alert) { - alert(options).then(resolve, reject) - }) - }) - } - - function showInAppPurchaseInfo(subscriptionOptions, unlockableProductInfo, dialogOptions) { - return new Promise(function(resolve, reject) { - require(["listViewStyle", "formDialogStyle"], function() { - showInAppPurchaseElement(subscriptionOptions, unlockableProductInfo, dialogOptions, resolve, reject), currentDisplayingResolve = resolve - }) - }) - } - - function showPeriodicMessage(feature, settingsKey) { - return new Promise(function(resolve, reject) { - require(["listViewStyle", "emby-button", "formDialogStyle"], function() { - var dlg = dialogHelper.createDialog({ - size: layoutManager.tv ? "fullscreen" : "fullscreen-border", - removeOnClose: !0, - scrollY: !1 - }); - dlg.classList.add("formDialog"); - var html = ""; - var seconds = 11; - html += '
' + globalize.translate("sharedcomponents#ContinueInSecondsValue", seconds) + "
", html += '", html += "
", html += "
", html += "
", dlg.innerHTML = html; - var i, length, isRejected = !0, - timeTextInterval = setInterval(function() { - seconds -= 1, seconds <= 0 ? (dlg.querySelector(".continueTimeText").classList.add("hide"), dlg.querySelector(".btnContinue").classList.remove("hide")) : dlg.querySelector(".continueTimeText").innerHTML = globalize.translate("sharedcomponents#ContinueInSecondsValue", seconds) - }, 1e3), - btnPurchases = dlg.querySelectorAll(".buttonPremiereInfo"); - for (i = 0, length = btnPurchases.length; i < length; i++) btnPurchases[i].addEventListener("click", showExternalPremiereInfo); - layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !0), dlg.addEventListener("close", function(e) { - clearInterval(timeTextInterval), layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !1), isRejected ? reject() : (appSettings.set(settingsKey, (new Date).getTime()), resolve()) - }), dlg.querySelector(".btnContinue").addEventListener("click", function() { - isRejected = !1, dialogHelper.close(dlg) - }), dlg.querySelector(".btnGetPremiere").addEventListener("click", showPremiereInfo), dialogHelper.open(dlg); - var onCancelClick = function() { - dialogHelper.close(dlg) - }, - elems = dlg.querySelectorAll(".btnCancelSupporterInfo"); - for (i = 0, length = elems.length; i < length; i++) elems[i].addEventListener("click", onCancelClick) - }) - }) - } - - function showPeriodicMessageIfNeeded(feature) { - if ("playback" !== feature) return Promise.resolve(); - var intervalMs = iapManager.getPeriodicMessageIntervalMs(feature); - if (intervalMs <= 0) return Promise.resolve(); - var settingsKey = "periodicmessage11-" + feature, - lastMessage = parseInt(appSettings.get(settingsKey) || "0"); - if (!lastMessage) return appSettings.set(settingsKey, (new Date).getTime()), Promise.resolve(); - if ((new Date).getTime() - lastMessage > intervalMs) { - var apiClient = connectionManager.currentApiClient(); - if ("6da60dd6edfc4508bca2c434d4400816" === apiClient.serverId()) return Promise.resolve(); - var registrationOptions = { - viewOnly: !0 - }; - return connectionManager.getRegistrationInfo(iapManager.getAdminFeatureName(feature), apiClient, registrationOptions).catch(function(errorResult) { - return "overlimit" === errorResult ? (appSettings.set(settingsKey, (new Date).getTime()), Promise.resolve()) : showPeriodicMessage(feature, settingsKey) - }) - } - return Promise.resolve() - } +define(['appSettings', 'loading', 'apphost', 'events', 'shell', 'globalize', 'dialogHelper', 'connectionManager', 'layoutManager', 'emby-button', 'emby-linkbutton'], function (appSettings, loading, appHost, events, shell, globalize, dialogHelper, connectionManager, layoutManager) { + 'use strict'; function validateFeature(feature, options) { - return options = options || {}, console.log("validateFeature: " + feature), iapManager.isUnlockedByDefault(feature, options).then(function() { - return showPeriodicMessageIfNeeded(feature) - }, function() { - var unlockableFeatureCacheKey = "featurepurchased-" + feature; - if ("1" === appSettings.get(unlockableFeatureCacheKey)) return showPeriodicMessageIfNeeded(feature); - var unlockableProduct = iapManager.getProductInfo(feature); - if (unlockableProduct) { - var unlockableCacheKey = "productpurchased-" + unlockableProduct.id; - if (unlockableProduct.owned) return appSettings.set(unlockableFeatureCacheKey, "1"), appSettings.set(unlockableCacheKey, "1"), showPeriodicMessageIfNeeded(feature); - if ("1" === appSettings.get(unlockableCacheKey)) return showPeriodicMessageIfNeeded(feature) - } - var unlockableProductInfo = unlockableProduct ? { - enableAppUnlock: !0, - id: unlockableProduct.id, - price: unlockableProduct.price, - feature: feature - } : null; - return iapManager.getSubscriptionOptions().then(function(subscriptionOptions) { - if (subscriptionOptions.filter(function(p) { - return p.owned - }).length > 0) return Promise.resolve(); - var registrationOptions = { - viewOnly: options.viewOnly - }; - return connectionManager.getRegistrationInfo(iapManager.getAdminFeatureName(feature), connectionManager.currentApiClient(), registrationOptions).catch(function(errorResult) { - if (!1 === options.showDialog) return Promise.reject(); - var alertPromise; - return "overlimit" === errorResult && (alertPromise = showOverLimitAlert()), alertPromise || (alertPromise = Promise.resolve()), alertPromise.then(function() { - var dialogOptions = { - title: globalize.translate("sharedcomponents#HeaderUnlockFeature"), - feature: feature - }; - return currentValidatingFeature = feature, showInAppPurchaseInfo(subscriptionOptions, unlockableProductInfo, dialogOptions) - }) - }) - }) - }) - } - - function showOverLimitAlert() { - return alertText("Your Jellyfin Premiere device limit has been exceeded. Please check with the owner of your Jellyfin Server and have them contact Jellyfin support at apps@emby.media if necessary.").catch(function() { - return Promise.resolve() - }) - } - - function cancelInAppPurchase() { - var elem = document.querySelector(".inAppPurchaseOverlay"); - elem && dialogHelper.close(elem) - } - - function clearCurrentDisplayingInfo() { - currentDisplayingProductInfos = [], currentDisplayingResolve = null, currentValidatingFeature = null, isCurrentDialogRejected = null - } - - function showExternalPremiereInfo() { - shell.openUrl(iapManager.getPremiumInfoUrl()) - } - - function centerFocus(elem, horiz, on) { - require(["scrollHelper"], function(scrollHelper) { - var fn = on ? "on" : "off"; - scrollHelper.centerFocus[fn](elem, horiz) - }) - } - - function getPurchaseTermHtml(term) { - return "
  • " + term + "
  • " - } - - function getTermsOfPurchaseHtml() { - var html = "", - termsOfPurchase = iapManager.getTermsOfPurchase ? iapManager.getTermsOfPurchase() : []; - return termsOfPurchase.length ? (html += "

    " + globalize.translate("sharedcomponents#HeaderTermsOfPurchase") + "

    ", termsOfPurchase.push('' + globalize.translate("sharedcomponents#PrivacyPolicy") + ""), termsOfPurchase.push('' + globalize.translate("sharedcomponents#TermsOfUse") + ""), html += "") : html - } - - function showInAppPurchaseElement(subscriptionOptions, unlockableProductInfo, dialogOptions, resolve, reject) { - function onCloseButtonClick() { - dialogHelper.close(dlg) - } - cancelInAppPurchase(), currentDisplayingProductInfos = subscriptionOptions.slice(0), unlockableProductInfo && currentDisplayingProductInfos.push(unlockableProductInfo); - var dlg = dialogHelper.createDialog({ - size: layoutManager.tv ? "fullscreen" : "fullscreen-border", - removeOnClose: !0, - scrollY: !1 - }); - dlg.classList.add("formDialog"); - var html = ""; - html += '
    ', html += '', html += '

    ', html += dialogOptions.title || "", html += "

    ", html += "
    ", html += '
    ', html += '
    ', html += '
    ', html += '

    ', html += unlockableProductInfo ? globalize.translate("sharedcomponents#MessageUnlockAppWithPurchaseOrSupporter") : globalize.translate("sharedcomponents#MessageUnlockAppWithSupporter"), html += "

    ", html += '

    ', html += globalize.translate("sharedcomponents#MessageToValidateSupporter"), html += "

    "; - var i, length; - for (i = 0, length = subscriptionOptions.length; i < length; i++) !0, html += "

    ", html += '", html += "

    "; - if (unlockableProductInfo) { - !0; - var unlockText = globalize.translate("sharedcomponents#ButtonUnlockWithPurchase"); - unlockableProductInfo.price && (unlockText = globalize.translate("sharedcomponents#ButtonUnlockPrice", unlockableProductInfo.price)), html += "

    ", html += '", html += "

    " - } - html += "

    ", html += '", html += "

    ", subscriptionOptions.length && (html += '

    ' + globalize.translate("sharedcomponents#HeaderBenefitsJellyfinPremiere") + "

    ", html += '
    ', html += getSubscriptionBenefits().map(getSubscriptionBenefitHtml).join(""), html += "
    "), "playback" === dialogOptions.feature && (html += "

    ", html += '", html += "

    "), html += getTermsOfPurchaseHtml(), html += "
    ", html += "
    ", html += "
    ", dlg.innerHTML = html, document.body.appendChild(dlg); - var btnPurchases = dlg.querySelectorAll(".btnPurchase"); - for (i = 0, length = btnPurchases.length; i < length; i++) btnPurchases[i].addEventListener("click", onPurchaseButtonClick); - for (btnPurchases = dlg.querySelectorAll(".buttonPremiereInfo"), i = 0, length = btnPurchases.length; i < length; i++) btnPurchases[i].addEventListener("click", showExternalPremiereInfo); - isCurrentDialogRejected = !0; - var resolveWithTimeLimit = !1, - btnPlayMinute = dlg.querySelector(".btnPlayMinute"); - btnPlayMinute && btnPlayMinute.addEventListener("click", function() { - resolveWithTimeLimit = !0, isCurrentDialogRejected = !1, dialogHelper.close(dlg) - }), dlg.querySelector(".btnRestorePurchase").addEventListener("click", function() { - restorePurchase(unlockableProductInfo) - }), loading.hide(); - var btnCloseDialogs = dlg.querySelectorAll(".btnCloseDialog"); - for (i = 0, length = btnCloseDialogs.length; i < length; i++) btnCloseDialogs[i].addEventListener("click", onCloseButtonClick); - dlg.classList.add("inAppPurchaseOverlay"), layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !0), dialogHelper.open(dlg).then(function() { - layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !1); - var rejected = isCurrentDialogRejected; - clearCurrentDisplayingInfo(), rejected ? reject() : resolveWithTimeLimit && resolve({ - enableTimeLimit: !0 - }) - }) - } - - function getSubscriptionBenefits() { - var list = []; - return list.push({ - name: globalize.translate("sharedcomponents#HeaderFreeApps"), - icon: "", - text: globalize.translate("sharedcomponents#FreeAppsFeatureDescription") - }), appHost.supports("sync") && list.push({ - name: globalize.translate("sharedcomponents#HeaderOfflineDownloads"), - icon: "", - text: globalize.translate("sharedcomponents#HeaderOfflineDownloadsDescription") - }), list.push({ - name: globalize.translate("sharedcomponents#LiveTV"), - icon: "", - text: globalize.translate("sharedcomponents#LiveTvFeatureDescription") - }), list.push({ - name: "Jellyfin DVR", - icon: "", - text: globalize.translate("sharedcomponents#DvrFeatureDescription") - }), list.push({ - name: globalize.translate("sharedcomponents#HeaderCinemaMode"), - icon: "", - text: globalize.translate("sharedcomponents#CinemaModeFeatureDescription") - }), list.push({ - name: globalize.translate("sharedcomponents#HeaderCloudSync"), - icon: "", - text: globalize.translate("sharedcomponents#CloudSyncFeatureDescription") - }), list - } - - function getSubscriptionBenefitHtml(item) { - var enableLink = appHost.supports("externalpremium"), - html = "", - cssClass = "listItem"; - return layoutManager.tv && (cssClass += " listItem-focusscale"), enableLink ? (cssClass += " listItem-button", html += '" : "
    " - } - - function onPurchaseButtonClick() { - var featureId = this.getAttribute("data-featureid"); - "true" === this.getAttribute("data-email") ? getUserEmail().then(function(email) { - iapManager.beginPurchase(featureId, email) - }) : iapManager.beginPurchase(featureId) - } - - function restorePurchase(unlockableProductInfo) { - var dlg = dialogHelper.createDialog({ - size: layoutManager.tv ? "fullscreen" : "fullscreen-border", - removeOnClose: !0, - scrollY: !1 - }); - dlg.classList.add("formDialog"); - var html = ""; - html += '
    ', html += '', html += '

    ', html += iapManager.getRestoreButtonText(), html += "

    ", html += "
    ", html += '
    ', html += '
    ', html += '

    ', html += globalize.translate("sharedcomponents#HowDidYouPay"), html += "

    ", html += "

    ", html += '", html += "

    ", unlockableProductInfo && (html += "

    ", html += '", html += "

    "), html += "
    ", html += "
    ", dlg.innerHTML = html, document.body.appendChild(dlg), loading.hide(), layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !0), dlg.querySelector(".btnCloseDialog").addEventListener("click", function() { - dialogHelper.close(dlg) - }), dlg.querySelector(".btnRestoreSub").addEventListener("click", function() { - dialogHelper.close(dlg), alertText({ - text: globalize.translate("sharedcomponents#MessageToValidateSupporter"), - title: "Jellyfin Premiere" - }) - }); - var btnRestoreUnlock = dlg.querySelector(".btnRestoreUnlock"); - btnRestoreUnlock && btnRestoreUnlock.addEventListener("click", function() { - dialogHelper.close(dlg), iapManager.restorePurchase() - }), dialogHelper.open(dlg).then(function() { - layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !1) - }) - } - - function getUserEmail() { - if (connectionManager.isLoggedIntoConnect()) { - var connectUser = connectionManager.connectUser(); - if (connectUser && connectUser.Email) return Promise.resolve(connectUser.Email) - } - return new Promise(function(resolve, reject) { - require(["prompt"], function(prompt) { - prompt({ - label: globalize.translate("sharedcomponents#LabelEmailAddress") - }).then(resolve, reject) - }) - }) - } - - function onProductUpdated(e, product) { - if (product.owned) { - var resolve = currentDisplayingResolve; - if (resolve && currentDisplayingProductInfos.filter(function(p) { - return product.id === p.id - }).length) return isCurrentDialogRejected = !1, cancelInAppPurchase(), void resolve() - } - var feature = currentValidatingFeature; - feature && iapManager.isUnlockedByDefault(feature).then(function() { - isCurrentDialogRejected = !1, cancelInAppPurchase(), resolve() - }) + return Promise.resolve(); } function showPremiereInfo() { - return appHost.supports("externalpremium") ? (showExternalPremiereInfo(), Promise.resolve()) : iapManager.getSubscriptionOptions().then(function(subscriptionOptions) { - return showInAppPurchaseInfo(subscriptionOptions, null, { - title: "Jellyfin Premiere", - feature: "sync" - }) - }) + return Promise.resolve(); } - var currentDisplayingProductInfos = [], - currentDisplayingResolve = null, - currentValidatingFeature = null, - isCurrentDialogRejected = null; - return events.on(iapManager, "productupdated", onProductUpdated), { + + return { validateFeature: validateFeature, showPremiereInfo: showPremiereInfo - } -}); + }; +}); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/require/requirecss.js b/src/bower_components/emby-webcomponents/require/requirecss.js index 5e158496cf..f65ed1fd04 100644 --- a/src/bower_components/emby-webcomponents/require/requirecss.js +++ b/src/bower_components/emby-webcomponents/require/requirecss.js @@ -1,35 +1,75 @@ -define(function() { - "use strict"; +define(function () { + 'use strict'; + + var requireCss = {}; + + requireCss.normalize = function (name, normalize) { + if (name.substr(name.length - 4, 4) === '.css') { + name = name.substr(0, name.length - 4); + } + + return normalize(name); + }; + + var importedCss = []; function isLoaded(url) { - return -1 !== importedCss.indexOf(url) + return importedCss.indexOf(url) !== -1; } function removeFromLoadHistory(url) { - url = url.toLowerCase(), importedCss = importedCss.filter(function(c) { - return -1 === url.indexOf(c.toLowerCase()) - }) + + url = url.toLowerCase(); + + importedCss = importedCss.filter(function (c) { + return url.indexOf(c.toLowerCase()) === -1; + }); } - var requireCss = {}; - requireCss.normalize = function(name, normalize) { - return ".css" === name.substr(name.length - 4, 4) && (name = name.substr(0, name.length - 4)), normalize(name) - }; - var importedCss = []; - return requireCss.load = function(cssId, req, load, config) { - var srch = "/emby-webcomponents/require/requirecss", - index = cssId.indexOf(srch); - 1 !== index && (cssId = "css" + cssId.substring(index + srch.length)); - var url = cssId + ".css"; - if (-1 === url.indexOf("://") && (url = config.baseUrl + url), isLoaded(url)) load(); - else { + + requireCss.load = function (cssId, req, load, config) { + + // Somehow if the url starts with /css, require will get all screwed up since this extension is also called css + var srch = '/emby-webcomponents/require/requirecss'; + var index = cssId.indexOf(srch); + + if (index !== -1) { + cssId = 'css' + cssId.substring(index + srch.length); + } + + var url = cssId + '.css'; + + if (url.indexOf('://') === -1) { + url = config.baseUrl + url; + } + + if (!isLoaded(url)) { importedCss.push(url); - var link = document.createElement("link"); - link.setAttribute("rel", "stylesheet"), link.setAttribute("type", "text/css"), link.onload = load; + + var link = document.createElement('link'); + + link.setAttribute('rel', 'stylesheet'); + link.setAttribute('type', 'text/css'); + link.onload = load; + var linkUrl = url; - config.urlArgs && (linkUrl += config.urlArgs(cssId, url)), link.setAttribute("href", linkUrl), document.head.appendChild(link) + + if (config.urlArgs) { + linkUrl += config.urlArgs(cssId, url); + } + link.setAttribute('href', linkUrl); + document.head.appendChild(link); + } else { + load(); } - }, window.requireCss = { - removeStylesheet: function(stylesheet) { - stylesheet.parentNode.removeChild(stylesheet), removeFromLoadHistory(stylesheet.href) + }; + + window.requireCss = { + removeStylesheet: function (stylesheet) { + + stylesheet.parentNode.removeChild(stylesheet); + removeFromLoadHistory(stylesheet.href); } - }, requireCss -}); \ No newline at end of file + }; + + return requireCss; +}); diff --git a/src/bower_components/emby-webcomponents/require/requiretext.js b/src/bower_components/emby-webcomponents/require/requiretext.js index c31826f81c..ac508f95be 100644 --- a/src/bower_components/emby-webcomponents/require/requiretext.js +++ b/src/bower_components/emby-webcomponents/require/requiretext.js @@ -1,16 +1,43 @@ -define(function() { - "use strict"; - var addRedirectPrevention = null != self.dashboardVersion && self.Dashboard && !self.AppInfo.isNativeApp; +define(function () { + 'use strict'; + + // hack to work around the server's auto-redirection feature + var addRedirectPrevention = self.dashboardVersion != null && self.Dashboard && !self.AppInfo.isNativeApp; + return { - load: function(url, req, load, config) { - -1 === url.indexOf("://") && (url = config.baseUrl + url), config.urlArgs && (url += config.urlArgs(url, url)), addRedirectPrevention && (-1 === url.indexOf("?") ? url += "?" : url += "&", url += "r=0"); - var xhr = new XMLHttpRequest; - xhr.open("GET", url, !0), xhr.onload = function(e) { - load(this.response) - }, xhr.send() + + load: function (url, req, load, config) { + + if (url.indexOf('://') === -1) { + url = config.baseUrl + url; + } + + if (config.urlArgs) { + url += config.urlArgs(url, url); + } + + if (addRedirectPrevention) { + if (url.indexOf('?') === -1) { + url += '?'; + } else { + url += '&'; + } + + url += 'r=0'; + } + + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + + xhr.onload = function (e) { + load(this.response); + }; + + xhr.send(); }, - normalize: function(name, normalize) { - return normalize(name) + + normalize: function (name, normalize) { + return normalize(name); } - } -}); \ No newline at end of file + }; +}); diff --git a/src/bower_components/emby-webcomponents/resize-observer-polyfill/ResizeObserver.js b/src/bower_components/emby-webcomponents/resize-observer-polyfill/ResizeObserver.js index 4b7687b2f4..d9f152c827 100644 --- a/src/bower_components/emby-webcomponents/resize-observer-polyfill/ResizeObserver.js +++ b/src/bower_components/emby-webcomponents/resize-observer-polyfill/ResizeObserver.js @@ -1,244 +1,939 @@ -! function(global, factory) { - "object" == typeof exports && "undefined" != typeof module ? module.exports = factory() : "function" == typeof define && define.amd ? define(factory) : global.ResizeObserver = factory() -}(this, function() { - "use strict"; +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.ResizeObserver = factory()); +}(this, (function () { 'use strict'; - function getHTMLElementContentRect(target) { - var rect = target.getBoundingClientRect(); - return createRectInit(rect.left, rect.top, rect.width, rect.height) +/** + * A collection of shims that provide minimal functionality of the ES6 collections. + * + * These implementations are not meant to be used outside of the ResizeObserver + * modules as they cover only a limited range of use cases. + */ +/* eslint-disable require-jsdoc, valid-jsdoc */ +var MapShim = (function () { + if (typeof Map !== 'undefined') { + return Map; } - function getContentRect(target) { - return isBrowser ? getHTMLElementContentRect(target) : emptyRect - } + /** + * Returns index in provided array that matches the specified key. + * + * @param {Array} arr + * @param {*} key + * @returns {number} + */ + function getIndex(arr, key) { + var result = -1; - function createReadOnlyRect(ref) { - var x = ref.x, - y = ref.y, - width = ref.width, - height = ref.height, - Constr = "undefined" != typeof DOMRectReadOnly ? DOMRectReadOnly : Object, - rect = Object.create(Constr.prototype); - return defineConfigurable(rect, { - x: x, - y: y, - width: width, - height: height, - top: y, - right: x + width, - bottom: height + y, - left: x - }), rect - } + arr.some(function (entry, index) { + if (entry[0] === key) { + result = index; - function createRectInit(x, y, width, height) { - return { - x: x, - y: y, - width: width, - height: height - } - } - var MapShim = function() { - function getIndex(arr, key) { - var result = -1; - return arr.some(function(entry, index) { - return entry[0] === key && (result = index, !0) - }), result - } - return "undefined" != typeof Map ? Map : function() { - function anonymous() { - this.__entries__ = [] - } - var prototypeAccessors = { - size: { - configurable: !0 - } - }; - return prototypeAccessors.size.get = function() { - return this.__entries__.length - }, anonymous.prototype.get = function(key) { - var index = getIndex(this.__entries__, key), - entry = this.__entries__[index]; - return entry && entry[1] - }, anonymous.prototype.set = function(key, value) { - var index = getIndex(this.__entries__, key); - ~index ? this.__entries__[index][1] = value : this.__entries__.push([key, value]) - }, anonymous.prototype.delete = function(key) { - var entries = this.__entries__, - index = getIndex(entries, key); - ~index && entries.splice(index, 1) - }, anonymous.prototype.has = function(key) { - return !!~getIndex(this.__entries__, key) - }, anonymous.prototype.clear = function() { - this.__entries__.splice(0) - }, anonymous.prototype.forEach = function(callback, ctx) { - var this$1 = this; - void 0 === ctx && (ctx = null); - for (var i = 0, list = this$1.__entries__; i < list.length; i += 1) { - var entry = list[i]; - callback.call(ctx, entry[1], entry[0]) - } - }, Object.defineProperties(anonymous.prototype, prototypeAccessors), anonymous - }() - }(), - isBrowser = "undefined" != typeof window && "undefined" != typeof document && window.document === document, - global$1 = function() { - return "undefined" != typeof global && global.Math === Math ? global : "undefined" != typeof self && self.Math === Math ? self : "undefined" != typeof window && window.Math === Math ? window : Function("return this")() - }(), - requestAnimationFrame$1 = function() { - return "function" == typeof requestAnimationFrame ? requestAnimationFrame.bind(global$1) : function(callback) { - return setTimeout(function() { - return callback(Date.now()) - }, 1e3 / 60) - } - }(), - trailingTimeout = 2, - throttle = function(callback, delay) { - function resolvePending() { - leadingCall && (leadingCall = !1, callback()), trailingCall && proxy() + return true; } - function timeoutCallback() { - requestAnimationFrame$1(resolvePending) - } - - function proxy() { - var timeStamp = Date.now(); - if (leadingCall) { - if (timeStamp - lastCallTime < trailingTimeout) return; - trailingCall = !0 - } else leadingCall = !0, trailingCall = !1, setTimeout(timeoutCallback, delay); - lastCallTime = timeStamp - } - var leadingCall = !1, - trailingCall = !1, - lastCallTime = 0; - return proxy - }, - transitionKeys = ["top", "right", "bottom", "left", "width", "height", "size", "weight"], - mutationObserverSupported = "undefined" != typeof MutationObserver, - ResizeObserverController = function() { - this.connected_ = !1, this.mutationEventsAdded_ = !1, this.mutationsObserver_ = null, this.observers_ = [], this.onTransitionEnd_ = this.onTransitionEnd_.bind(this), this.refresh = throttle(this.refresh.bind(this), 20) - }; - ResizeObserverController.prototype.addObserver = function(observer) { - ~this.observers_.indexOf(observer) || this.observers_.push(observer), this.connected_ || this.connect_() - }, ResizeObserverController.prototype.removeObserver = function(observer) { - var observers = this.observers_, - index = observers.indexOf(observer); - ~index && observers.splice(index, 1), !observers.length && this.connected_ && this.disconnect_() - }, ResizeObserverController.prototype.refresh = function() { - this.updateObservers_() && this.refresh() - }, ResizeObserverController.prototype.updateObservers_ = function() { - var activeObservers = this.observers_.filter(function(observer) { - return observer.gatherActive(), observer.hasActive() + return false; }); - return activeObservers.forEach(function(observer) { - return observer.broadcastActive() - }), activeObservers.length > 0 - }, ResizeObserverController.prototype.connect_ = function() { - isBrowser && !this.connected_ && (document.addEventListener("transitionend", this.onTransitionEnd_), window.addEventListener("resize", this.refresh), window.addEventListener("orientationchange", this.refresh), mutationObserverSupported ? (this.mutationsObserver_ = new MutationObserver(this.refresh), this.mutationsObserver_.observe(document, { - attributes: !0, - childList: !0, - characterData: !0, - subtree: !0 - })) : (document.addEventListener("DOMSubtreeModified", this.refresh), this.mutationEventsAdded_ = !0), this.connected_ = !0) - }, ResizeObserverController.prototype.disconnect_ = function() { - isBrowser && this.connected_ && (document.removeEventListener("transitionend", this.onTransitionEnd_), window.removeEventListener("resize", this.refresh), window.removeEventListener("orientationchange", this.refresh), this.mutationsObserver_ && this.mutationsObserver_.disconnect(), this.mutationEventsAdded_ && document.removeEventListener("DOMSubtreeModified", this.refresh), this.mutationsObserver_ = null, this.mutationEventsAdded_ = !1, this.connected_ = !1) - }, ResizeObserverController.prototype.onTransitionEnd_ = function(ref) { - var propertyName = ref.propertyName; - void 0 === propertyName && (propertyName = ""), transitionKeys.some(function(key) { - return !!~propertyName.indexOf(key) - }) && this.refresh() - }, ResizeObserverController.getInstance = function() { - return this.instance_ || (this.instance_ = new ResizeObserverController), this.instance_ - }, ResizeObserverController.instance_ = null; - var defineConfigurable = function(target, props) { - for (var i = 0, list = Object.keys(props); i < list.length; i += 1) { - var key = list[i]; - Object.defineProperty(target, key, { - value: props[key], - enumerable: !1, - writable: !1, - configurable: !0 - }) + + return result; + } + + return (function () { + function anonymous() { + this.__entries__ = []; + } + + var prototypeAccessors = { size: { configurable: true } }; + + /** + * @returns {boolean} + */ + prototypeAccessors.size.get = function () { + return this.__entries__.length; + }; + + /** + * @param {*} key + * @returns {*} + */ + anonymous.prototype.get = function (key) { + var index = getIndex(this.__entries__, key); + var entry = this.__entries__[index]; + + return entry && entry[1]; + }; + + /** + * @param {*} key + * @param {*} value + * @returns {void} + */ + anonymous.prototype.set = function (key, value) { + var index = getIndex(this.__entries__, key); + + if (~index) { + this.__entries__[index][1] = value; + } else { + this.__entries__.push([key, value]); } - return target - }, - getWindowOf = function(target) { - return target && target.ownerDocument && target.ownerDocument.defaultView || global$1 - }, - emptyRect = createRectInit(0, 0, 0, 0), - ResizeObservation = function(target) { - this.broadcastWidth = 0, this.broadcastHeight = 0, this.contentRect_ = createRectInit(0, 0, 0, 0), this.target = target }; - ResizeObservation.prototype.isActive = function() { - var rect = getContentRect(this.target); - return this.contentRect_ = rect, rect.width !== this.broadcastWidth || rect.height !== this.broadcastHeight - }, ResizeObservation.prototype.broadcastRect = function() { - var rect = this.contentRect_; - return this.broadcastWidth = rect.width, this.broadcastHeight = rect.height, rect - }; - var ResizeObserverEntry = function(target, rectInit) { - var contentRect = createReadOnlyRect(rectInit); - defineConfigurable(this, { - target: target, - contentRect: contentRect - }) - }, - ResizeObserverSPI = function(callback, controller, callbackCtx) { - if (this.activeObservations_ = [], this.observations_ = new MapShim, "function" != typeof callback) throw new TypeError("The callback provided as parameter 1 is not a function."); - this.callback_ = callback, this.controller_ = controller, this.callbackCtx_ = callbackCtx + + /** + * @param {*} key + * @returns {void} + */ + anonymous.prototype.delete = function (key) { + var entries = this.__entries__; + var index = getIndex(entries, key); + + if (~index) { + entries.splice(index, 1); + } }; - ResizeObserverSPI.prototype.observe = function(target) { - if (!arguments.length) throw new TypeError("1 argument required, but only 0 present."); - if ("undefined" != typeof Element && Element instanceof Object) { - if (!(target instanceof getWindowOf(target).Element)) throw new TypeError('parameter 1 is not of type "Element".'); - var observations = this.observations_; - observations.has(target) || (observations.set(target, new ResizeObservation(target)), this.controller_.addObserver(this), this.controller_.refresh()) + + /** + * @param {*} key + * @returns {void} + */ + anonymous.prototype.has = function (key) { + return !!~getIndex(this.__entries__, key); + }; + + /** + * @returns {void} + */ + anonymous.prototype.clear = function () { + this.__entries__.splice(0); + }; + + /** + * @param {Function} callback + * @param {*} [ctx=null] + * @returns {void} + */ + anonymous.prototype.forEach = function (callback, ctx) { + var this$1 = this; + if ( ctx === void 0 ) ctx = null; + + for (var i = 0, list = this$1.__entries__; i < list.length; i += 1) { + var entry = list[i]; + + callback.call(ctx, entry[1], entry[0]); + } + }; + + Object.defineProperties( anonymous.prototype, prototypeAccessors ); + + return anonymous; + }()); +})(); + +/** + * Detects whether window and document objects are available in current environment. + */ +var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined' && window.document === document; + +// Returns global object of a current environment. +var global$1 = (function () { + if (typeof global !== 'undefined' && global.Math === Math) { + return global; + } + + if (typeof self !== 'undefined' && self.Math === Math) { + return self; + } + + if (typeof window !== 'undefined' && window.Math === Math) { + return window; + } + + // eslint-disable-next-line no-new-func + return Function('return this')(); +})(); + +/** + * A shim for the requestAnimationFrame which falls back to the setTimeout if + * first one is not supported. + * + * @returns {number} Requests' identifier. + */ +var requestAnimationFrame$1 = (function () { + if (typeof requestAnimationFrame === 'function') { + // It's required to use a bounded function because IE sometimes throws + // an "Invalid calling object" error if rAF is invoked without the global + // object on the left hand side. + return requestAnimationFrame.bind(global$1); + } + + return function (callback) { return setTimeout(function () { return callback(Date.now()); }, 1000 / 60); }; +})(); + +// Defines minimum timeout before adding a trailing call. +var trailingTimeout = 2; + +/** + * Creates a wrapper function which ensures that provided callback will be + * invoked only once during the specified delay period. + * + * @param {Function} callback - Function to be invoked after the delay period. + * @param {number} delay - Delay after which to invoke callback. + * @returns {Function} + */ +var throttle = function (callback, delay) { + var leadingCall = false, + trailingCall = false, + lastCallTime = 0; + + /** + * Invokes the original callback function and schedules new invocation if + * the "proxy" was called during current request. + * + * @returns {void} + */ + function resolvePending() { + if (leadingCall) { + leadingCall = false; + + callback(); } - }, ResizeObserverSPI.prototype.unobserve = function(target) { - if (!arguments.length) throw new TypeError("1 argument required, but only 0 present."); - if ("undefined" != typeof Element && Element instanceof Object) { - if (!(target instanceof getWindowOf(target).Element)) throw new TypeError('parameter 1 is not of type "Element".'); - var observations = this.observations_; - observations.has(target) && (observations.delete(target), observations.size || this.controller_.removeObserver(this)) + + if (trailingCall) { + proxy(); } - }, ResizeObserverSPI.prototype.disconnect = function() { - this.clearActive(), this.observations_.clear(), this.controller_.removeObserver(this) - }, ResizeObserverSPI.prototype.gatherActive = function() { + } + + /** + * Callback invoked after the specified delay. It will further postpone + * invocation of the original function delegating it to the + * requestAnimationFrame. + * + * @returns {void} + */ + function timeoutCallback() { + requestAnimationFrame$1(resolvePending); + } + + /** + * Schedules invocation of the original function. + * + * @returns {void} + */ + function proxy() { + var timeStamp = Date.now(); + + if (leadingCall) { + // Reject immediately following calls. + if (timeStamp - lastCallTime < trailingTimeout) { + return; + } + + // Schedule new call to be in invoked when the pending one is resolved. + // This is important for "transitions" which never actually start + // immediately so there is a chance that we might miss one if change + // happens amids the pending invocation. + trailingCall = true; + } else { + leadingCall = true; + trailingCall = false; + + setTimeout(timeoutCallback, delay); + } + + lastCallTime = timeStamp; + } + + return proxy; +}; + +// Minimum delay before invoking the update of observers. +var REFRESH_DELAY = 20; + +// A list of substrings of CSS properties used to find transition events that +// might affect dimensions of observed elements. +var transitionKeys = ['top', 'right', 'bottom', 'left', 'width', 'height', 'size', 'weight']; + +// Check if MutationObserver is available. +var mutationObserverSupported = typeof MutationObserver !== 'undefined'; + +/** + * Singleton controller class which handles updates of ResizeObserver instances. + */ +var ResizeObserverController = function() { + this.connected_ = false; + this.mutationEventsAdded_ = false; + this.mutationsObserver_ = null; + this.observers_ = []; + + this.onTransitionEnd_ = this.onTransitionEnd_.bind(this); + this.refresh = throttle(this.refresh.bind(this), REFRESH_DELAY); +}; + +/** + * Adds observer to observers list. + * + * @param {ResizeObserverSPI} observer - Observer to be added. + * @returns {void} + */ + + +/** + * Holds reference to the controller's instance. + * + * @private {ResizeObserverController} + */ + + +/** + * Keeps reference to the instance of MutationObserver. + * + * @private {MutationObserver} + */ + +/** + * Indicates whether DOM listeners have been added. + * + * @private {boolean} + */ +ResizeObserverController.prototype.addObserver = function (observer) { + if (!~this.observers_.indexOf(observer)) { + this.observers_.push(observer); + } + + // Add listeners if they haven't been added yet. + if (!this.connected_) { + this.connect_(); + } +}; + +/** + * Removes observer from observers list. + * + * @param {ResizeObserverSPI} observer - Observer to be removed. + * @returns {void} + */ +ResizeObserverController.prototype.removeObserver = function (observer) { + var observers = this.observers_; + var index = observers.indexOf(observer); + + // Remove observer if it's present in registry. + if (~index) { + observers.splice(index, 1); + } + + // Remove listeners if controller has no connected observers. + if (!observers.length && this.connected_) { + this.disconnect_(); + } +}; + +/** + * Invokes the update of observers. It will continue running updates insofar + * it detects changes. + * + * @returns {void} + */ +ResizeObserverController.prototype.refresh = function () { + var changesDetected = this.updateObservers_(); + + // Continue running updates if changes have been detected as there might + // be future ones caused by CSS transitions. + if (changesDetected) { + this.refresh(); + } +}; + +/** + * Updates every observer from observers list and notifies them of queued + * entries. + * + * @private + * @returns {boolean} Returns "true" if any observer has detected changes in + * dimensions of it's elements. + */ +ResizeObserverController.prototype.updateObservers_ = function () { + // Collect observers that have active observations. + var activeObservers = this.observers_.filter(function (observer) { + return observer.gatherActive(), observer.hasActive(); + }); + + // Deliver notifications in a separate cycle in order to avoid any + // collisions between observers, e.g. when multiple instances of + // ResizeObserver are tracking the same element and the callback of one + // of them changes content dimensions of the observed target. Sometimes + // this may result in notifications being blocked for the rest of observers. + activeObservers.forEach(function (observer) { return observer.broadcastActive(); }); + + return activeObservers.length > 0; +}; + +/** + * Initializes DOM listeners. + * + * @private + * @returns {void} + */ +ResizeObserverController.prototype.connect_ = function () { + // Do nothing if running in a non-browser environment or if listeners + // have been already added. + if (!isBrowser || this.connected_) { + return; + } + + // Subscription to the "Transitionend" event is used as a workaround for + // delayed transitions. This way it's possible to capture at least the + // final state of an element. + document.addEventListener('transitionend', this.onTransitionEnd_); + + window.addEventListener('resize', this.refresh); + window.addEventListener('orientationchange', this.refresh); + + if (mutationObserverSupported) { + this.mutationsObserver_ = new MutationObserver(this.refresh); + + this.mutationsObserver_.observe(document, { + attributes: true, + childList: true, + characterData: true, + subtree: true + }); + } else { + document.addEventListener('DOMSubtreeModified', this.refresh); + + this.mutationEventsAdded_ = true; + } + + this.connected_ = true; +}; + +/** + * Removes DOM listeners. + * + * @private + * @returns {void} + */ +ResizeObserverController.prototype.disconnect_ = function () { + // Do nothing if running in a non-browser environment or if listeners + // have been already removed. + if (!isBrowser || !this.connected_) { + return; + } + + document.removeEventListener('transitionend', this.onTransitionEnd_); + window.removeEventListener('resize', this.refresh); + window.removeEventListener('orientationchange', this.refresh); + + if (this.mutationsObserver_) { + this.mutationsObserver_.disconnect(); + } + + if (this.mutationEventsAdded_) { + document.removeEventListener('DOMSubtreeModified', this.refresh); + } + + this.mutationsObserver_ = null; + this.mutationEventsAdded_ = false; + this.connected_ = false; +}; + +/** + * "Transitionend" event handler. + * + * @private + * @param {TransitionEvent} event + * @returns {void} + */ +ResizeObserverController.prototype.onTransitionEnd_ = function (ref) { + var propertyName = ref.propertyName; if ( propertyName === void 0 ) propertyName = ''; + + // Detect whether transition may affect dimensions of an element. + var isReflowProperty = transitionKeys.some(function (key) { + return !!~propertyName.indexOf(key); + }); + + if (isReflowProperty) { + this.refresh(); + } +}; + +/** + * Returns instance of the ResizeObserverController. + * + * @returns {ResizeObserverController} + */ +ResizeObserverController.getInstance = function () { + if (!this.instance_) { + this.instance_ = new ResizeObserverController(); + } + + return this.instance_; +}; + +ResizeObserverController.instance_ = null; + +/** + * Defines non-writable/enumerable properties of the provided target object. + * + * @param {Object} target - Object for which to define properties. + * @param {Object} props - Properties to be defined. + * @returns {Object} Target object. + */ +var defineConfigurable = (function (target, props) { + for (var i = 0, list = Object.keys(props); i < list.length; i += 1) { + var key = list[i]; + + Object.defineProperty(target, key, { + value: props[key], + enumerable: false, + writable: false, + configurable: true + }); + } + + return target; +}); + +/** + * Returns the global object associated with provided element. + * + * @param {Object} target + * @returns {Object} + */ +var getWindowOf = (function (target) { + // Assume that the element is an instance of Node, which means that it + // has the "ownerDocument" property from which we can retrieve a + // corresponding global object. + var ownerGlobal = target && target.ownerDocument && target.ownerDocument.defaultView; + + // Return the local global object if it's not possible extract one from + // provided element. + return ownerGlobal || global$1; +}); + +// Placeholder of an empty content rectangle. +var emptyRect = createRectInit(0, 0, 0, 0); + +/** + * Converts provided string to a number. + * + * @param {number|string} value + * @returns {number} + */ +function toFloat(value) { + return parseFloat(value) || 0; +} + +/** + * Extracts borders size from provided styles. + * + * @param {CSSStyleDeclaration} styles + * @param {...string} positions - Borders positions (top, right, ...) + * @returns {number} + */ +function getBordersSize(styles) { + var positions = [], len = arguments.length - 1; + while ( len-- > 0 ) positions[ len ] = arguments[ len + 1 ]; + + return positions.reduce(function (size, position) { + var value = styles['border-' + position + '-width']; + + return size + toFloat(value); + }, 0); +} + +/** + * Extracts paddings sizes from provided styles. + * + * @param {CSSStyleDeclaration} styles + * @returns {Object} Paddings box. + */ +function getPaddings(styles) { + var positions = ['top', 'right', 'bottom', 'left']; + var paddings = {}; + + for (var i = 0, list = positions; i < list.length; i += 1) { + var position = list[i]; + + var value = styles['padding-' + position]; + + paddings[position] = toFloat(value); + } + + return paddings; +} + +/** + * Calculates content rectangle of provided SVG element. + * + * @param {SVGGraphicsElement} target - Element content rectangle of which needs + * to be calculated. + * @returns {DOMRectInit} + */ +function getSVGContentRect(target) { + var bbox = target.getBBox(); + + return createRectInit(0, 0, bbox.width, bbox.height); +} + +/** + * Calculates content rectangle of provided HTMLElement. + * + * @param {HTMLElement} target - Element for which to calculate the content rectangle. + * @returns {DOMRectInit} + */ +function getHTMLElementContentRect(target) { + + var rect = target.getBoundingClientRect(); + return createRectInit(rect.left, rect.top, rect.width, rect.height); +} + +/** + * Checks whether provided element is a document element (). + * + * @param {Element} target - Element to be checked. + * @returns {boolean} + */ +function isDocumentElement(target) { + return target === getWindowOf(target).document.documentElement; +} + +/** + * Calculates an appropriate content rectangle for provided html or svg element. + * + * @param {Element} target - Element content rectangle of which needs to be calculated. + * @returns {DOMRectInit} + */ +function getContentRect(target) { + if (!isBrowser) { + return emptyRect; + } + + return getHTMLElementContentRect(target); +} + +/** + * Creates rectangle with an interface of the DOMRectReadOnly. + * Spec: https://drafts.fxtf.org/geometry/#domrectreadonly + * + * @param {DOMRectInit} rectInit - Object with rectangle's x/y coordinates and dimensions. + * @returns {DOMRectReadOnly} + */ +function createReadOnlyRect(ref) { + var x = ref.x; + var y = ref.y; + var width = ref.width; + var height = ref.height; + + // If DOMRectReadOnly is available use it as a prototype for the rectangle. + var Constr = typeof DOMRectReadOnly !== 'undefined' ? DOMRectReadOnly : Object; + var rect = Object.create(Constr.prototype); + + // Rectangle's properties are not writable and non-enumerable. + defineConfigurable(rect, { + x: x, y: y, width: width, height: height, + top: y, + right: x + width, + bottom: height + y, + left: x + }); + + return rect; +} + +/** + * Creates DOMRectInit object based on the provided dimensions and the x/y coordinates. + * Spec: https://drafts.fxtf.org/geometry/#dictdef-domrectinit + * + * @param {number} x - X coordinate. + * @param {number} y - Y coordinate. + * @param {number} width - Rectangle's width. + * @param {number} height - Rectangle's height. + * @returns {DOMRectInit} + */ +function createRectInit(x, y, width, height) { + return { x: x, y: y, width: width, height: height }; +} + +/** + * Class that is responsible for computations of the content rectangle of + * provided DOM element and for keeping track of it's changes. + */ +var ResizeObservation = function(target) { + this.broadcastWidth = 0; + this.broadcastHeight = 0; + this.contentRect_ = createRectInit(0, 0, 0, 0); + + this.target = target; +}; + +/** + * Updates content rectangle and tells whether it's width or height properties + * have changed since the last broadcast. + * + * @returns {boolean} + */ + + +/** + * Reference to the last observed content rectangle. + * + * @private {DOMRectInit} + */ + + +/** + * Broadcasted width of content rectangle. + * + * @type {number} + */ +ResizeObservation.prototype.isActive = function () { + var rect = getContentRect(this.target); + + this.contentRect_ = rect; + return rect.width !== this.broadcastWidth || rect.height !== this.broadcastHeight; +}; + +/** + * Updates 'broadcastWidth' and 'broadcastHeight' properties with a data + * from the corresponding properties of the last observed content rectangle. + * + * @returns {DOMRectInit} Last observed content rectangle. + */ +ResizeObservation.prototype.broadcastRect = function () { + var rect = this.contentRect_; + + this.broadcastWidth = rect.width; + this.broadcastHeight = rect.height; + + return rect; +}; + +var ResizeObserverEntry = function(target, rectInit) { + var contentRect = createReadOnlyRect(rectInit); + + // According to the specification following properties are not writable + // and are also not enumerable in the native implementation. + // + // Property accessors are not being used as they'd require to define a + // private WeakMap storage which may cause memory leaks in browsers that + // don't support this type of collections. + defineConfigurable(this, { target: target, contentRect: contentRect }); +}; + +var ResizeObserverSPI = function(callback, controller, callbackCtx) { + this.activeObservations_ = []; + this.observations_ = new MapShim(); + + if (typeof callback !== 'function') { + throw new TypeError('The callback provided as parameter 1 is not a function.'); + } + + this.callback_ = callback; + this.controller_ = controller; + this.callbackCtx_ = callbackCtx; +}; + +/** + * Starts observing provided element. + * + * @param {Element} target - Element to be observed. + * @returns {void} + */ + + +/** + * Registry of the ResizeObservation instances. + * + * @private {Map} + */ + + +/** + * Public ResizeObserver instance which will be passed to the callback + * function and used as a value of it's "this" binding. + * + * @private {ResizeObserver} + */ + +/** + * Collection of resize observations that have detected changes in dimensions + * of elements. + * + * @private {Array} + */ +ResizeObserverSPI.prototype.observe = function (target) { + if (!arguments.length) { + throw new TypeError('1 argument required, but only 0 present.'); + } + + // Do nothing if current environment doesn't have the Element interface. + if (typeof Element === 'undefined' || !(Element instanceof Object)) { + return; + } + + if (!(target instanceof getWindowOf(target).Element)) { + throw new TypeError('parameter 1 is not of type "Element".'); + } + + var observations = this.observations_; + + // Do nothing if element is already being observed. + if (observations.has(target)) { + return; + } + + observations.set(target, new ResizeObservation(target)); + + this.controller_.addObserver(this); + + // Force the update of observations. + this.controller_.refresh(); +}; + +/** + * Stops observing provided element. + * + * @param {Element} target - Element to stop observing. + * @returns {void} + */ +ResizeObserverSPI.prototype.unobserve = function (target) { + if (!arguments.length) { + throw new TypeError('1 argument required, but only 0 present.'); + } + + // Do nothing if current environment doesn't have the Element interface. + if (typeof Element === 'undefined' || !(Element instanceof Object)) { + return; + } + + if (!(target instanceof getWindowOf(target).Element)) { + throw new TypeError('parameter 1 is not of type "Element".'); + } + + var observations = this.observations_; + + // Do nothing if element is not being observed. + if (!observations.has(target)) { + return; + } + + observations.delete(target); + + if (!observations.size) { + this.controller_.removeObserver(this); + } +}; + +/** + * Stops observing all elements. + * + * @returns {void} + */ +ResizeObserverSPI.prototype.disconnect = function () { + this.clearActive(); + this.observations_.clear(); + this.controller_.removeObserver(this); +}; + +/** + * Collects observation instances the associated element of which has changed + * it's content rectangle. + * + * @returns {void} + */ +ResizeObserverSPI.prototype.gatherActive = function () { var this$1 = this; - this.clearActive(), this.observations_.forEach(function(observation) { - observation.isActive() && this$1.activeObservations_.push(observation) - }) - }, ResizeObserverSPI.prototype.broadcastActive = function() { - if (this.hasActive()) { - var ctx = this.callbackCtx_, - entries = this.activeObservations_.map(function(observation) { - return new ResizeObserverEntry(observation.target, observation.broadcastRect()) - }); - this.callback_.call(ctx, entries, ctx), this.clearActive() + + this.clearActive(); + + this.observations_.forEach(function (observation) { + if (observation.isActive()) { + this$1.activeObservations_.push(observation); } - }, ResizeObserverSPI.prototype.clearActive = function() { - this.activeObservations_.splice(0) - }, ResizeObserverSPI.prototype.hasActive = function() { - return this.activeObservations_.length > 0 + }); +}; + +/** + * Invokes initial callback function with a list of ResizeObserverEntry + * instances collected from active resize observations. + * + * @returns {void} + */ +ResizeObserverSPI.prototype.broadcastActive = function () { + // Do nothing if observer doesn't have active observations. + if (!this.hasActive()) { + return; + } + + var ctx = this.callbackCtx_; + + // Create ResizeObserverEntry instance for every active observation. + var entries = this.activeObservations_.map(function (observation) { + return new ResizeObserverEntry(observation.target, observation.broadcastRect()); + }); + + this.callback_.call(ctx, entries, ctx); + this.clearActive(); +}; + +/** + * Clears the collection of active observations. + * + * @returns {void} + */ +ResizeObserverSPI.prototype.clearActive = function () { + this.activeObservations_.splice(0); +}; + +/** + * Tells whether observer has active observations. + * + * @returns {boolean} + */ +ResizeObserverSPI.prototype.hasActive = function () { + return this.activeObservations_.length > 0; +}; + +// Registry of internal observers. If WeakMap is not available use current shim +// for the Map collection as it has all required methods and because WeakMap +// can't be fully polyfilled anyway. +var observers = typeof WeakMap !== 'undefined' ? new WeakMap() : new MapShim(); + +/** + * ResizeObserver API. Encapsulates the ResizeObserver SPI implementation + * exposing only those methods and properties that are defined in the spec. + */ +var ResizeObserver = function(callback) { + if (!(this instanceof ResizeObserver)) { + throw new TypeError('Cannot call a class as a function.'); + } + if (!arguments.length) { + throw new TypeError('1 argument required, but only 0 present.'); + } + + var controller = ResizeObserverController.getInstance(); + var observer = new ResizeObserverSPI(callback, controller, this); + + observers.set(this, observer); +}; + +// Expose public methods of ResizeObserver. +['observe', 'unobserve', 'disconnect'].forEach(function (method) { + ResizeObserver.prototype[method] = function () { + return (ref = observers.get(this))[method].apply(ref, arguments); + var ref; }; - var observers = "undefined" != typeof WeakMap ? new WeakMap : new MapShim, - ResizeObserver = function(callback) { - if (!(this instanceof ResizeObserver)) throw new TypeError("Cannot call a class as a function."); - if (!arguments.length) throw new TypeError("1 argument required, but only 0 present."); - var controller = ResizeObserverController.getInstance(), - observer = new ResizeObserverSPI(callback, controller, this); - observers.set(this, observer) - }; - return ["observe", "unobserve", "disconnect"].forEach(function(method) { - ResizeObserver.prototype[method] = function() { - return (ref = observers.get(this))[method].apply(ref, arguments); - var ref - } - }), - function() { - return void 0 !== global$1.ResizeObserver ? global$1.ResizeObserver : ResizeObserver - }() -}); \ No newline at end of file +}); + +var index = (function () { + // Export existing implementation if available. + if (typeof global$1.ResizeObserver !== 'undefined') { + return global$1.ResizeObserver; + } + + return ResizeObserver; +})(); + +return index; + +}))); diff --git a/src/bower_components/emby-webcomponents/router.js b/src/bower_components/emby-webcomponents/router.js index 00d38e1cdc..133a326589 100644 --- a/src/bower_components/emby-webcomponents/router.js +++ b/src/bower_components/emby-webcomponents/router.js @@ -1,416 +1,855 @@ -define(["loading", "globalize", "events", "viewManager", "layoutManager", "skinManager", "pluginManager", "backdrop", "browser", "pageJs", "appSettings", "apphost", "connectionManager"], function(loading, globalize, events, viewManager, layoutManager, skinManager, pluginManager, backdrop, browser, page, appSettings, appHost, connectionManager) { - "use strict"; +define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinManager', 'pluginManager', 'backdrop', 'browser', 'pageJs', 'appSettings', 'apphost', 'connectionManager'], function (loading, globalize, events, viewManager, layoutManager, skinManager, pluginManager, backdrop, browser, page, appSettings, appHost, connectionManager) { + 'use strict'; + + var appRouter = { + showLocalLogin: function (serverId, manualLogin) { + var pageName = manualLogin ? 'manuallogin' : 'login'; + show('/startup/' + pageName + '.html?serverid=' + serverId); + }, + showSelectServer: function () { + show('/startup/selectserver.html'); + }, + showWelcome: function () { + show('/startup/welcome.html'); + }, + showSettings: function () { + show('/settings/settings.html'); + }, + showSearch: function () { + skinManager.getCurrentSkin().search(); + }, + showGenre: function (options) { + skinManager.getCurrentSkin().showGenre(options); + }, + showGuide: function () { + skinManager.getCurrentSkin().showGuide({ + serverId: connectionManager.currentApiClient().serverId() + }); + }, + showLiveTV: function () { + skinManager.getCurrentSkin().showLiveTV({ + serverId: connectionManager.currentApiClient().serverId() + }); + }, + showRecordedTV: function () { + skinManager.getCurrentSkin().showRecordedTV(); + }, + showFavorites: function () { + skinManager.getCurrentSkin().showFavorites(); + }, + showNowPlaying: function () { + skinManager.getCurrentSkin().showNowPlaying(); + } + }; function beginConnectionWizard() { - backdrop.clear(), loading.show(), connectionManager.connect({ + backdrop.clear(); + loading.show(); + connectionManager.connect({ enableAutoLogin: appSettings.enableAutoLogin() - }).then(function(result) { - handleConnectionResult(result, loading) - }) + }).then(function (result) { + handleConnectionResult(result, loading); + }); } function handleConnectionResult(result, loading) { switch (result.State) { - case "SignedIn": - loading.hide(), skinManager.loadUserSkin(); + case 'SignedIn': + loading.hide(); + skinManager.loadUserSkin(); break; - case "ServerSignIn": - result.ApiClient.getPublicUsers().then(function(users) { - users.length ? appRouter.showLocalLogin(result.Servers[0].Id) : appRouter.showLocalLogin(result.Servers[0].Id, !0) + case 'ServerSignIn': + result.ApiClient.getPublicUsers().then(function (users) { + if (users.length) { + appRouter.showLocalLogin(result.Servers[0].Id); + } else { + appRouter.showLocalLogin(result.Servers[0].Id, true); + } }); break; - case "ServerSelection": + case 'ServerSelection': appRouter.showSelectServer(); break; - case "ConnectSignIn": + case 'ConnectSignIn': appRouter.showWelcome(); break; - case "ServerUpdateNeeded": - require(["alert"], function(alert) { + case 'ServerUpdateNeeded': + require(['alert'], function (alert) { alert({ - text: globalize.translate("sharedcomponents#ServerUpdateNeeded", "https://github.com/jellyfin/jellyfin"), - html: globalize.translate("sharedcomponents#ServerUpdateNeeded", 'https://github.com/jellyfin/jellyfin') - }).then(function() { - appRouter.showSelectServer() - }) - }) + text: globalize.translate('sharedcomponents#ServerUpdateNeeded', 'https://github.com/jellyfin/jellyfin'), + html: globalize.translate('sharedcomponents#ServerUpdateNeeded', 'https://github.com/jellyfin/jellyfin') + }).then(function () { + appRouter.showSelectServer(); + }); + }); + break; + default: + break; } } function loadContentUrl(ctx, next, route, request) { var url; - url = route.contentPath && "function" == typeof route.contentPath ? route.contentPath(ctx.querystring) : route.contentPath || route.path, -1 === url.indexOf("://") && (0 !== url.indexOf("/") && (url = "/" + url), url = baseUrl() + url), ctx.querystring && route.enableContentQueryString && (url += "?" + ctx.querystring), require(["text!" + url], function(html) { - loadContent(ctx, route, html, request) - }) + if (route.contentPath && typeof (route.contentPath) === 'function') { + url = route.contentPath(ctx.querystring); + } else { + url = route.contentPath || route.path; + } + + if (url.indexOf('://') === -1) { + // Put a slash at the beginning but make sure to avoid a double slash + if (url.indexOf('/') !== 0) { + url = '/' + url; + } + + url = baseUrl() + url; + } + + if (ctx.querystring && route.enableContentQueryString) { + url += '?' + ctx.querystring; + } + + require(['text!' + url], function (html) { + loadContent(ctx, route, html, request); + }); } function handleRoute(ctx, next, route) { - authenticate(ctx, route, function() { - initRoute(ctx, next, route) - }) + authenticate(ctx, route, function () { + initRoute(ctx, next, route); + }); } function initRoute(ctx, next, route) { - var onInitComplete = function(controllerFactory) { - sendRouteToViewManager(ctx, next, route, controllerFactory) + var onInitComplete = function (controllerFactory) { + sendRouteToViewManager(ctx, next, route, controllerFactory); }; - require(route.dependencies || [], function() { - route.controller ? require([route.controller], onInitComplete) : onInitComplete() - }) + + require(route.dependencies || [], function () { + if (route.controller) { + require([route.controller], onInitComplete); + } else { + onInitComplete(); + } + }); } function cancelCurrentLoadRequest() { var currentRequest = currentViewLoadRequest; - currentRequest && (currentRequest.cancel = !0) + if (currentRequest) { + currentRequest.cancel = true; + } } + var currentViewLoadRequest; function sendRouteToViewManager(ctx, next, route, controllerFactory) { - if (isDummyBackToHome && "home" === route.type) return void(isDummyBackToHome = !1); + if (isDummyBackToHome && route.type === 'home') { + isDummyBackToHome = false; + return; + } + cancelCurrentLoadRequest(); - var isBackNav = ctx.isBack, - currentRequest = { - url: baseUrl() + ctx.path, - transition: route.transition, - isBack: isBackNav, - state: ctx.state, - type: route.type, - fullscreen: route.fullscreen, - controllerFactory: controllerFactory, - options: { - supportsThemeMedia: route.supportsThemeMedia || !1, - enableMediaControl: !1 !== route.enableMediaControl - }, - autoFocus: route.autoFocus - }; - currentViewLoadRequest = currentRequest; - var onNewViewNeeded = function() { - "string" == typeof route.path ? loadContentUrl(ctx, next, route, currentRequest) : next() + var isBackNav = ctx.isBack; + + var currentRequest = { + url: baseUrl() + ctx.path, + transition: route.transition, + isBack: isBackNav, + state: ctx.state, + type: route.type, + fullscreen: route.fullscreen, + controllerFactory: controllerFactory, + options: { + supportsThemeMedia: route.supportsThemeMedia || false, + enableMediaControl: route.enableMediaControl !== false + }, + autoFocus: route.autoFocus }; - if (!isBackNav) return void onNewViewNeeded(); - viewManager.tryRestoreView(currentRequest, function() { + currentViewLoadRequest = currentRequest; + + var onNewViewNeeded = function () { + if (typeof route.path === 'string') { + loadContentUrl(ctx, next, route, currentRequest); + } else { + // ? TODO + next(); + } + }; + + if (!isBackNav) { + // Don't force a new view for home due to the back menu + //if (route.type !== 'home') { + onNewViewNeeded(); + return; + //} + } + viewManager.tryRestoreView(currentRequest, function () { + + // done currentRouteInfo = { route: route, path: ctx.path + }; + + }).catch(function (result) { + if (!result || !result.cancelled) { + onNewViewNeeded(); } - }).catch(function(result) { - result && result.cancelled || onNewViewNeeded() - }) + }); } + var msgTimeout; + var forcedLogoutMsg; function onForcedLogoutMessageTimeout() { var msg = forcedLogoutMsg; - forcedLogoutMsg = null, msg && require(["alert"], function(alert) { - alert(msg) - }) + forcedLogoutMsg = null; + + if (msg) { + require(['alert'], function (alert) { + alert(msg); + }); + } } function showForcedLogoutMessage(msg) { - forcedLogoutMsg = msg, msgTimeout && clearTimeout(msgTimeout), msgTimeout = setTimeout(onForcedLogoutMessageTimeout, 100) + forcedLogoutMsg = msg; + if (msgTimeout) { + clearTimeout(msgTimeout); + } + + msgTimeout = setTimeout(onForcedLogoutMessageTimeout, 100); } function onRequestFail(e, data) { + var apiClient = this; - if (401 === data.status && "ParentalControl" === data.errorCode) { - !currentRouteInfo || (currentRouteInfo.route.anonymous || currentRouteInfo.route.startup) || (showForcedLogoutMessage(globalize.translate("sharedcomponents#AccessRestrictedTryAgainLater")), connectionManager.isLoggedIntoConnect() ? appRouter.showConnectLogin() : appRouter.showLocalLogin(apiClient.serverId())) + + if (data.status === 401) { + if (data.errorCode === "ParentalControl") { + + var isCurrentAllowed = currentRouteInfo ? (currentRouteInfo.route.anonymous || currentRouteInfo.route.startup) : true; + + // Bounce to the login screen, but not if a password entry fails, obviously + if (!isCurrentAllowed) { + showForcedLogoutMessage(globalize.translate('sharedcomponents#AccessRestrictedTryAgainLater')); + appRouter.showLocalLogin(apiClient.serverId()); + } + + } } } function onBeforeExit(e) { - browser.web0s && page.restorePreviousState() + if (browser.web0s) { + page.restorePreviousState(); + } } function normalizeImageOptions(options) { - var setQuality, scaleFactor = browser.tv ? .8 : 1; - if (options.maxWidth && (options.maxWidth = Math.round(options.maxWidth * scaleFactor), setQuality = !0), options.width && (options.width = Math.round(options.width * scaleFactor), setQuality = !0), options.maxHeight && (options.maxHeight = Math.round(options.maxHeight * scaleFactor), setQuality = !0), options.height && (options.height = Math.round(options.height * scaleFactor), setQuality = !0), setQuality) { - var quality = 100, - type = options.type || "Primary"; - quality = browser.tv || browser.slow ? browser.chrome ? "Primary" === type ? 40 : 50 : "Backdrop" === type ? 60 : 50 : "Backdrop" === type ? 70 : 90, options.quality = quality + var scaleFactor = browser.tv ? 0.8 : 1; + + var setQuality; + if (options.maxWidth) { + options.maxWidth = Math.round(options.maxWidth * scaleFactor); + setQuality = true; + } + + if (options.width) { + options.width = Math.round(options.width * scaleFactor); + setQuality = true; + } + + if (options.maxHeight) { + options.maxHeight = Math.round(options.maxHeight * scaleFactor); + setQuality = true; + } + + if (options.height) { + options.height = Math.round(options.height * scaleFactor); + setQuality = true; + } + + if (setQuality) { + + var quality = 100; + + var type = options.type || 'Primary'; + + if (browser.tv || browser.slow) { + + if (browser.chrome) { + // webp support + quality = type === 'Primary' ? 40 : 50; + } else { + quality = type === 'Backdrop' ? 60 : 50; + } + } else { + quality = type === 'Backdrop' ? 70 : 90; + } + + options.quality = quality; } } function getMaxBandwidth() { if (navigator.connection) { var max = navigator.connection.downlinkMax; - if (max && max > 0 && max < Number.POSITIVE_INFINITY) return max /= 8, max *= 1e6, max *= .7, max = parseInt(max) + if (max && max > 0 && max < Number.POSITIVE_INFINITY) { + + max /= 8; + max *= 1000000; + max *= 0.7; + max = parseInt(max); + return max; + } } - return null + + return null; } function getMaxBandwidthIOS() { - return 8e5 + return 800000; } function onApiClientCreated(e, newApiClient) { - newApiClient.normalizeImageOptions = normalizeImageOptions, browser.iOS ? newApiClient.getMaxBandwidth = getMaxBandwidthIOS : newApiClient.getMaxBandwidth = getMaxBandwidth, events.off(newApiClient, "requestfail", onRequestFail), events.on(newApiClient, "requestfail", onRequestFail) + + newApiClient.normalizeImageOptions = normalizeImageOptions; + + if (browser.iOS) { + newApiClient.getMaxBandwidth = getMaxBandwidthIOS; + } else { + newApiClient.getMaxBandwidth = getMaxBandwidth; + } + + events.off(newApiClient, 'requestfail', onRequestFail); + events.on(newApiClient, 'requestfail', onRequestFail); } function initApiClient(apiClient) { - onApiClientCreated({}, apiClient) + + onApiClientCreated({}, apiClient); } function initApiClients() { - connectionManager.getApiClients().forEach(initApiClient), events.on(connectionManager, "apiclientcreated", onApiClientCreated) + + connectionManager.getApiClients().forEach(initApiClient); + + events.on(connectionManager, 'apiclientcreated', onApiClientCreated); } function onAppResume() { var apiClient = connectionManager.currentApiClient(); - apiClient && apiClient.ensureWebSocket() + + if (apiClient) { + apiClient.ensureWebSocket(); + } } + var firstConnectionResult; function start(options) { - loading.show(), initApiClients(), events.on(appHost, "beforeexit", onBeforeExit), events.on(appHost, "resume", onAppResume), connectionManager.connect({ + + loading.show(); + + initApiClients(); + + events.on(appHost, 'beforeexit', onBeforeExit); + events.on(appHost, 'resume', onAppResume); + + connectionManager.connect({ + enableAutoLogin: appSettings.enableAutoLogin() - }).then(function(result) { - firstConnectionResult = result, loading.hide(), options = options || {}, page({ - click: !1 !== options.click, - hashbang: !1 !== options.hashbang, + + }).then(function (result) { + + firstConnectionResult = result; + + loading.hide(); + + options = options || {}; + + page({ + click: options.click !== false, + hashbang: options.hashbang !== false, enableHistory: enableHistory() - }) - }) + }); + }); } function enableHistory() { - return !browser.xboxOne && !browser.orsay + + //if (browser.edgeUwp) { + // return false; + //} + + // shows status bar on navigation + if (browser.xboxOne) { + return false; + } + + // Does not support history + if (browser.orsay) { + return false; + } + + return true; } function enableNativeHistory() { - return page.enableNativeHistory() + return page.enableNativeHistory(); } function authenticate(ctx, route, callback) { + var firstResult = firstConnectionResult; - if (firstResult && (firstConnectionResult = null, "SignedIn" !== firstResult.State && !route.anonymous)) return void handleConnectionResult(firstResult, loading); - var apiClient = connectionManager.currentApiClient(), - pathname = ctx.pathname.toLowerCase(); - console.log("appRouter - processing path request " + pathname); - var isCurrentRouteStartup = !currentRouteInfo || currentRouteInfo.route.startup, - shouldExitApp = ctx.isBack && route.isDefaultRoute && isCurrentRouteStartup; - if (!(shouldExitApp || apiClient && apiClient.isLoggedIn() || route.anonymous)) return console.log("appRouter - route does not allow anonymous access, redirecting to login"), void beginConnectionWizard(); - if (shouldExitApp) { - if (appHost.supports("exit")) return void appHost.exit() - } else { - if (apiClient && apiClient.isLoggedIn()) { - if (console.log("appRouter - user is authenticated"), ctx.isBack && (route.isDefaultRoute || route.startup) && !isCurrentRouteStartup) return void handleBackToDefault(); - if (route.isDefaultRoute) return console.log("appRouter - loading skin home page"), void loadUserSkinWithOptions(ctx); - if (route.roles) return void validateRoles(apiClient, route.roles).then(function() { - callback() - }, beginConnectionWizard) + if (firstResult) { + + firstConnectionResult = null; + + if (firstResult.State !== 'SignedIn' && !route.anonymous) { + + handleConnectionResult(firstResult, loading); + return; } - console.log("appRouter - proceeding to " + pathname), callback() } + + var apiClient = connectionManager.currentApiClient(); + var pathname = ctx.pathname.toLowerCase(); + + console.log('appRouter - processing path request ' + pathname); + + var isCurrentRouteStartup = currentRouteInfo ? currentRouteInfo.route.startup : true; + var shouldExitApp = ctx.isBack && route.isDefaultRoute && isCurrentRouteStartup; + + if (!shouldExitApp && (!apiClient || !apiClient.isLoggedIn()) && !route.anonymous) { + console.log('appRouter - route does not allow anonymous access, redirecting to login'); + beginConnectionWizard(); + return; + } + + if (shouldExitApp) { + if (appHost.supports('exit')) { + appHost.exit(); + return; + } + return; + } + + if (apiClient && apiClient.isLoggedIn()) { + + console.log('appRouter - user is authenticated'); + + if (ctx.isBack && (route.isDefaultRoute || route.startup) && !isCurrentRouteStartup) { + handleBackToDefault(); + return; + } + else if (route.isDefaultRoute) { + console.log('appRouter - loading skin home page'); + loadUserSkinWithOptions(ctx); + return; + } else if (route.roles) { + + validateRoles(apiClient, route.roles).then(function () { + + callback(); + + }, beginConnectionWizard); + return; + } + } + + console.log('appRouter - proceeding to ' + pathname); + callback(); } function loadUserSkinWithOptions(ctx) { - require(["queryString"], function(queryString) { + + require(['queryString'], function (queryString) { + + //var url = options.url; + //var index = url.indexOf('?'); var params = queryString.parse(ctx.querystring); + skinManager.loadUserSkin({ start: params.start - }) - }) + }); + }); } function validateRoles(apiClient, roles) { - return Promise.all(roles.split(",").map(function(role) { - return validateRole(apiClient, role) - })) + + return Promise.all(roles.split(',').map(function (role) { + return validateRole(apiClient, role); + })); } function validateRole(apiClient, role) { - return "admin" === role ? apiClient.getCurrentUser().then(function(user) { - return user.Policy.IsAdministrator ? Promise.resolve() : Promise.reject() - }) : Promise.resolve() + + if (role === 'admin') { + + return apiClient.getCurrentUser().then(function (user) { + if (user.Policy.IsAdministrator) { + return Promise.resolve(); + } + return Promise.reject(); + }); + } + + // Unknown role + return Promise.resolve(); } + var isHandlingBackToDefault; + var isDummyBackToHome; + function handleBackToDefault() { - if (!appHost.supports("exitmenu") && appHost.supports("exit")) return void appHost.exit(); - isDummyBackToHome = !0, skinManager.loadUserSkin(), isHandlingBackToDefault || skinManager.getCurrentSkin().showBackMenu().then(function() { - isHandlingBackToDefault = !1 - }) + + if (!appHost.supports('exitmenu') && appHost.supports('exit')) { + appHost.exit(); + return; + } + + isDummyBackToHome = true; + skinManager.loadUserSkin(); + + if (isHandlingBackToDefault) { + return; + } + + // This must result in a call to either + // skinManager.loadUserSkin(); + // Logout + // Or exit app + skinManager.getCurrentSkin().showBackMenu().then(function () { + + isHandlingBackToDefault = false; + }); } function loadContent(ctx, route, html, request) { - html = globalize.translateDocument(html, route.dictionary), request.view = html, viewManager.loadView(request), currentRouteInfo = { + + html = globalize.translateDocument(html, route.dictionary); + request.view = html; + + viewManager.loadView(request); + + currentRouteInfo = { route: route, path: ctx.path - }, ctx.handled = !0 + }; + //next(); + + ctx.handled = true; } function getRequestFile() { - var path = self.location.pathname || "", - index = path.lastIndexOf("/"); - return path = -1 !== index ? path.substring(index) : "/" + path, path && "/" !== path || (path = "/index.html"), path + var path = self.location.pathname || ''; + + var index = path.lastIndexOf('/'); + if (index !== -1) { + path = path.substring(index); + } else { + path = '/' + path; + } + + if (!path || path === '/') { + path = '/index.html'; + } + + return path; } function endsWith(str, srch) { - return str.lastIndexOf(srch) === srch.length - 1 + + return str.lastIndexOf(srch) === srch.length - 1; } + var baseRoute = self.location.href.split('?')[0].replace(getRequestFile(), ''); + // support hashbang + baseRoute = baseRoute.split('#')[0]; + if (endsWith(baseRoute, '/') && !endsWith(baseRoute, '://')) { + baseRoute = baseRoute.substring(0, baseRoute.length - 1); + } function baseUrl() { - return baseRoute + return baseRoute; } function getHandler(route) { - return function(ctx, next) { - handleRoute(ctx, next, route) - } + return function (ctx, next) { + handleRoute(ctx, next, route); + }; } function getWindowLocationSearch(win) { - var currentPath = currentRouteInfo ? currentRouteInfo.path || "" : "", - index = currentPath.indexOf("?"), - search = ""; - return -1 !== index && (search = currentPath.substring(index)), search || "" + + var currentPath = currentRouteInfo ? (currentRouteInfo.path || '') : ''; + + var index = currentPath.indexOf('?'); + var search = ''; + + if (index !== -1) { + search = currentPath.substring(index); + } + + return search || ''; } function param(name, url) { - name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); - var regexS = "[\\?&]" + name + "=([^&#]*)", - regex = new RegExp(regexS, "i"), - results = regex.exec(url || getWindowLocationSearch()); - return null == results ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")) + name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]"); + var regexS = "[\\?&]" + name + "=([^&#]*)"; + var regex = new RegExp(regexS, "i"); + + var results = regex.exec(url || getWindowLocationSearch()); + if (results == null) { + return ""; + } else { + return decodeURIComponent(results[1].replace(/\+/g, " ")); + } } function back() { - page.back() + + page.back(); } function canGoBack() { + var curr = current(); - return !!curr && ("home" !== curr.type && page.canGoBack()) + + if (!curr) { + return false; + } + + if (curr.type === 'home') { + return false; + } + return page.canGoBack(); } - function show(path, options) { - 0 !== path.indexOf("/") && -1 === path.indexOf("://") && (path = "/" + path); - var baseRoute = baseUrl(); - return path = path.replace(baseRoute, ""), currentRouteInfo && currentRouteInfo.path === path && "home" !== currentRouteInfo.route.type ? (loading.hide(), Promise.resolve()) : new Promise(function(resolve, reject) { - resolveOnNextShow = resolve, page.show(path, options) + function showDirect(path) { + return new Promise(function(resolve, reject) { + resolveOnNextShow = resolve, page.show(baseUrl()+path) }) } + function show(path, options) { + + if (path.indexOf('/') !== 0 && path.indexOf('://') === -1) { + path = '/' + path; + } + + var baseRoute = baseUrl(); + path = path.replace(baseRoute, ''); + + if (currentRouteInfo && currentRouteInfo.path === path) { + + // can't use this with home right now due to the back menu + if (currentRouteInfo.route.type !== 'home') { + loading.hide(); + return Promise.resolve(); + } + } + + return new Promise(function (resolve, reject) { + + resolveOnNextShow = resolve; + page.show(path, options); + }); + } + + var resolveOnNextShow; + document.addEventListener('viewshow', function () { + var resolve = resolveOnNextShow; + if (resolve) { + resolveOnNextShow = null; + resolve(); + } + }); + + var currentRouteInfo; function current() { - return currentRouteInfo ? currentRouteInfo.route : null + return currentRouteInfo ? currentRouteInfo.route : null; } function goHome() { + var skin = skinManager.getCurrentSkin(); + if (skin.getHomeRoute) { var homePath = skin.getHomeRoute(); - return show(pluginManager.mapRoute(skin, homePath)) + return show(pluginManager.mapRoute(skin, homePath)); + } else { + var homeRoute = skin.getRoutes().filter(function (r) { + return r.type === 'home'; + })[0]; + + return show(pluginManager.mapRoute(skin, homeRoute)); } - var homeRoute = skin.getRoutes().filter(function(r) { - return "home" === r.type - })[0]; - return show(pluginManager.mapRoute(skin, homeRoute)) } function getRouteUrl(item, options) { - return "downloads" === item ? "offline/offline.html" : "managedownloads" === item ? "offline/managedownloads.html" : "settings" === item ? "settings/settings.html" : skinManager.getCurrentSkin().getRouteUrl(item, options) + if (item === 'settings') { + return 'settings/settings.html'; + } + + return skinManager.getCurrentSkin().getRouteUrl(item, options); } function showItem(item, serverId, options) { - if ("string" == typeof item) { + + if (typeof (item) === 'string') { var apiClient = serverId ? connectionManager.getApiClient(serverId) : connectionManager.currentApiClient(); - apiClient.getItem(apiClient.getCurrentUserId(), item).then(function(item) { - appRouter.showItem(item, options) - }) + apiClient.getItem(apiClient.getCurrentUserId(), item).then(function (item) { + appRouter.showItem(item, options); + }); } else { - 2 === arguments.length && (options = arguments[1]); + + if (arguments.length === 2) { + options = arguments[1]; + } + var url = appRouter.getRouteUrl(item, options); appRouter.show(url, { item: item - }) + }); } } function setTitle(title) { - skinManager.getCurrentSkin().setTitle(title) + skinManager.getCurrentSkin().setTitle(title); } function showVideoOsd() { - var skin = skinManager.getCurrentSkin(), - homeRoute = skin.getRoutes().filter(function(r) { - return "video-osd" === r.type - })[0]; - return show(pluginManager.mapRoute(skin, homeRoute)) + var skin = skinManager.getCurrentSkin(); + + var homeRoute = skin.getRoutes().filter(function (r) { + return r.type === 'video-osd'; + })[0]; + + return show(pluginManager.mapRoute(skin, homeRoute)); } + var allRoutes = []; + function addRoute(path, newRoute) { - page(path, getHandler(newRoute)), allRoutes.push(newRoute) + + page(path, getHandler(newRoute)); + allRoutes.push(newRoute); } function getRoutes() { - return allRoutes + return allRoutes; } + var backdropContainer; + var backgroundContainer; function setTransparency(level) { - backdropContainer || (backdropContainer = document.querySelector(".backdropContainer")), backgroundContainer || (backgroundContainer = document.querySelector(".backgroundContainer")), "full" === level || 2 === level ? (backdrop.clear(!0), document.documentElement.classList.add("transparentDocument"), backgroundContainer.classList.add("backgroundContainer-transparent"), backdropContainer.classList.add("hide")) : "backdrop" === level || 1 === level ? (backdrop.externalBackdrop(!0), document.documentElement.classList.add("transparentDocument"), backgroundContainer.classList.add("backgroundContainer-transparent"), backdropContainer.classList.add("hide")) : (backdrop.externalBackdrop(!1), document.documentElement.classList.remove("transparentDocument"), backgroundContainer.classList.remove("backgroundContainer-transparent"), backdropContainer.classList.remove("hide")) + + if (!backdropContainer) { + backdropContainer = document.querySelector('.backdropContainer'); + } + if (!backgroundContainer) { + backgroundContainer = document.querySelector('.backgroundContainer'); + } + + if (level === 'full' || level === 2) { + backdrop.clear(true); + document.documentElement.classList.add('transparentDocument'); + backgroundContainer.classList.add('backgroundContainer-transparent'); + backdropContainer.classList.add('hide'); + } + else if (level === 'backdrop' || level === 1) { + backdrop.externalBackdrop(true); + document.documentElement.classList.add('transparentDocument'); + backgroundContainer.classList.add('backgroundContainer-transparent'); + backdropContainer.classList.add('hide'); + } else { + backdrop.externalBackdrop(false); + document.documentElement.classList.remove('transparentDocument'); + backgroundContainer.classList.remove('backgroundContainer-transparent'); + backdropContainer.classList.remove('hide'); + } } function pushState(state, title, url) { - state.navigate = !1, page.pushState(state, title, url) + + state.navigate = false; + + page.pushState(state, title, url); + } + + function setBaseRoute() { + var baseRoute = self.location.pathname.replace(getRequestFile(), ''); + if (baseRoute.lastIndexOf('/') === baseRoute.length - 1) { + baseRoute = baseRoute.substring(0, baseRoute.length - 1); + } + + console.log('Setting page base to ' + baseRoute); + + page.base(baseRoute); + } + + setBaseRoute(); + + function syncNow() { + require(['localsync'], function (localSync) { + localSync.sync(); + }); } function invokeShortcut(id) { - 0 === id.indexOf("library-") ? (id = id.replace("library-", ""), id = id.split("_"), appRouter.showItem(id[0], id[1])) : 0 === id.indexOf("item-") ? (id = id.replace("item-", ""), id = id.split("_"), appRouter.showItem(id[0], id[1])) : (id = id.split("_"), appRouter.show(appRouter.getRouteUrl(id[0], { - serverId: id[1] - }))) + + if (id.indexOf('library-') === 0) { + + id = id.replace('library-', ''); + + id = id.split('_'); + + appRouter.showItem(id[0], id[1]); + + } else if (id.indexOf('item-') === 0) { + + id = id.replace('item-', ''); + + id = id.split('_'); + + appRouter.showItem(id[0], id[1]); + + } else { + + id = id.split('_'); + + appRouter.show(appRouter.getRouteUrl(id[0], { + serverId: id[1] + })); + } } - var currentViewLoadRequest, msgTimeout, forcedLogoutMsg, firstConnectionResult, isHandlingBackToDefault, isDummyBackToHome, appRouter = { - showLocalLogin: function(serverId, manualLogin) { - show("/startup/" + (manualLogin ? "manuallogin" : "login") + ".html?serverid=" + serverId) - }, - showSelectServer: function() { - show("/startup/selectserver.html") - }, - showWelcome: function() { - show("/startup/welcome.html") - }, - showConnectLogin: function() { - show("/startup/connectlogin.html") - }, - showSettings: function() { - show("/settings/settings.html") - }, - showSearch: function() { - skinManager.getCurrentSkin().search() - }, - showGenre: function(options) { - skinManager.getCurrentSkin().showGenre(options) - }, - showGuide: function() { - skinManager.getCurrentSkin().showGuide({ - serverId: connectionManager.currentApiClient().serverId() - }) - }, - showLiveTV: function() { - skinManager.getCurrentSkin().showLiveTV({ - serverId: connectionManager.currentApiClient().serverId() - }) - }, - showRecordedTV: function() { - skinManager.getCurrentSkin().showRecordedTV() - }, - showFavorites: function() { - skinManager.getCurrentSkin().showFavorites() - }, - showNowPlaying: function() { - skinManager.getCurrentSkin().showNowPlaying() - } - }, - baseRoute = self.location.href.split("?")[0].replace(getRequestFile(), ""); - baseRoute = baseRoute.split("#")[0], endsWith(baseRoute, "/") && !endsWith(baseRoute, "://") && (baseRoute = baseRoute.substring(0, baseRoute.length - 1)); - var resolveOnNextShow; - document.addEventListener("viewshow", function() { - var resolve = resolveOnNextShow; - resolve && (resolveOnNextShow = null, resolve()) - }); - var currentRouteInfo, backdropContainer, backgroundContainer, allRoutes = []; - return function() { - var baseRoute = self.location.pathname.replace(getRequestFile(), ""); - baseRoute.lastIndexOf("/") === baseRoute.length - 1 && (baseRoute = baseRoute.substring(0, baseRoute.length - 1)), console.log("Setting page base to " + baseRoute), page.base(baseRoute) - }(), appRouter.addRoute = addRoute, appRouter.param = param, appRouter.back = back, appRouter.show = show, appRouter.start = start, appRouter.baseUrl = baseUrl, appRouter.canGoBack = canGoBack, appRouter.current = current, appRouter.beginConnectionWizard = beginConnectionWizard, appRouter.goHome = goHome, appRouter.showItem = showItem, appRouter.setTitle = setTitle, appRouter.setTransparency = setTransparency, appRouter.getRoutes = getRoutes, appRouter.getRouteUrl = getRouteUrl, appRouter.pushState = pushState, appRouter.enableNativeHistory = enableNativeHistory, appRouter.showVideoOsd = showVideoOsd, appRouter.handleAnchorClick = page.handleAnchorClick, appRouter.TransparencyLevel = { + + appRouter.addRoute = addRoute; + appRouter.param = param; + appRouter.back = back; + appRouter.show = show; + appRouter.showDirect = showDirect; + appRouter.start = start; + appRouter.baseUrl = baseUrl; + appRouter.canGoBack = canGoBack; + appRouter.current = current; + appRouter.beginConnectionWizard = beginConnectionWizard; + appRouter.goHome = goHome; + appRouter.showItem = showItem; + appRouter.setTitle = setTitle; + appRouter.setTransparency = setTransparency; + appRouter.getRoutes = getRoutes; + appRouter.getRouteUrl = getRouteUrl; + appRouter.pushState = pushState; + appRouter.enableNativeHistory = enableNativeHistory; + appRouter.showVideoOsd = showVideoOsd; + appRouter.handleAnchorClick = page.handleAnchorClick; + appRouter.TransparencyLevel = { None: 0, Backdrop: 1, Full: 2 - }, appRouter.invokeShortcut = invokeShortcut, appRouter + }; + appRouter.invokeShortcut = invokeShortcut; + + return appRouter; }); diff --git a/src/bower_components/emby-webcomponents/sanitizefilename.js b/src/bower_components/emby-webcomponents/sanitizefilename.js index fc8e3a8a64..fbc387836b 100644 --- a/src/bower_components/emby-webcomponents/sanitizefilename.js +++ b/src/bower_components/emby-webcomponents/sanitizefilename.js @@ -1,36 +1,100 @@ -define([], function() { - "use strict"; +// From https://github.com/parshap/node-sanitize-filename + +define([], function () { + 'use strict'; + + var illegalRe = /[\/\?<>\\:\*\|":]/g; + var controlRe = /[\x00-\x1f\x80-\x9f]/g; + var reservedRe = /^\.+$/; + var windowsReservedRe = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i; + var windowsTrailingRe = /[\. ]+$/; function isHighSurrogate(codePoint) { - return codePoint >= 55296 && codePoint <= 56319 + return codePoint >= 0xd800 && codePoint <= 0xdbff; } function isLowSurrogate(codePoint) { - return codePoint >= 56320 && codePoint <= 57343 + return codePoint >= 0xdc00 && codePoint <= 0xdfff; } function getByteLength(string) { - if ("string" != typeof string) throw new Error("Input must be string"); - for (var charLength = string.length, byteLength = 0, codePoint = null, prevCodePoint = null, i = 0; i < charLength; i++) codePoint = string.charCodeAt(i), isLowSurrogate(codePoint) ? null != prevCodePoint && isHighSurrogate(prevCodePoint) ? byteLength += 1 : byteLength += 3 : codePoint <= 127 ? byteLength += 1 : codePoint >= 128 && codePoint <= 2047 ? byteLength += 2 : codePoint >= 2048 && codePoint <= 65535 && (byteLength += 3), prevCodePoint = codePoint; - return byteLength + if (typeof string !== "string") { + throw new Error("Input must be string"); + } + + var charLength = string.length; + var byteLength = 0; + var codePoint = null; + var prevCodePoint = null; + for (var i = 0; i < charLength; i++) { + codePoint = string.charCodeAt(i); + // handle 4-byte non-BMP chars + // low surrogate + if (isLowSurrogate(codePoint)) { + // when parsing previous hi-surrogate, 3 is added to byteLength + if (prevCodePoint != null && isHighSurrogate(prevCodePoint)) { + byteLength += 1; + } + else { + byteLength += 3; + } + } + else if (codePoint <= 0x7f) { + byteLength += 1; + } + else if (codePoint >= 0x80 && codePoint <= 0x7ff) { + byteLength += 2; + } + else if (codePoint >= 0x800 && codePoint <= 0xffff) { + byteLength += 3; + } + prevCodePoint = codePoint; + } + + return byteLength; } function truncate(string, byteLength) { - if ("string" != typeof string) throw new Error("Input must be string"); - for (var codePoint, segment, charLength = string.length, curByteLength = 0, i = 0; i < charLength; i += 1) { - if (codePoint = string.charCodeAt(i), segment = string[i], isHighSurrogate(codePoint) && isLowSurrogate(string.charCodeAt(i + 1)) && (i += 1, segment += string[i]), (curByteLength += getByteLength(segment)) === byteLength) return string.slice(0, i + 1); - if (curByteLength > byteLength) return string.slice(0, i - segment.length + 1) + if (typeof string !== "string") { + throw new Error("Input must be string"); } - return string + + var charLength = string.length; + var curByteLength = 0; + var codePoint; + var segment; + + for (var i = 0; i < charLength; i += 1) { + codePoint = string.charCodeAt(i); + segment = string[i]; + + if (isHighSurrogate(codePoint) && isLowSurrogate(string.charCodeAt(i + 1))) { + i += 1; + segment += string[i]; + } + + curByteLength += getByteLength(segment); + + if (curByteLength === byteLength) { + return string.slice(0, i + 1); + } + else if (curByteLength > byteLength) { + return string.slice(0, i - segment.length + 1); + } + } + + return string; } - var illegalRe = /[\/\?<>\\:\*\|":]/g, - controlRe = /[\x00-\x1f\x80-\x9f]/g, - reservedRe = /^\.+$/, - windowsReservedRe = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i, - windowsTrailingRe = /[\. ]+$/; + return { - sanitize: function(input, replacement) { - return truncate(input.replace(illegalRe, replacement).replace(controlRe, replacement).replace(reservedRe, replacement).replace(windowsReservedRe, replacement).replace(windowsTrailingRe, replacement), 255) + sanitize: function (input, replacement) { + var sanitized = input + .replace(illegalRe, replacement) + .replace(controlRe, replacement) + .replace(reservedRe, replacement) + .replace(windowsReservedRe, replacement) + .replace(windowsTrailingRe, replacement); + return truncate(sanitized, 255); } - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/scroller/smoothscroller.js b/src/bower_components/emby-webcomponents/scroller/smoothscroller.js index dabd776911..cfa1e07a00 100644 --- a/src/bower_components/emby-webcomponents/scroller/smoothscroller.js +++ b/src/bower_components/emby-webcomponents/scroller/smoothscroller.js @@ -1,292 +1,937 @@ -define(["browser", "layoutManager", "dom", "focusManager", "ResizeObserver", "scrollStyles"], function(browser, layoutManager, dom, focusManager, ResizeObserver) { - "use strict"; +define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'scrollStyles'], function (browser, layoutManager, dom, focusManager, ResizeObserver) { + 'use strict'; + /** +* Return type of the value. +* +* @param {Mixed} value +* +* @return {String} +*/ function type(value) { - return null == value ? String(value) : "object" == typeof value || "function" == typeof value ? Object.prototype.toString.call(value).match(/\s([a-z]+)/i)[1].toLowerCase() || "object" : typeof value - } - - function disableOneEvent(event) { - event.preventDefault(), event.stopPropagation(), this.removeEventListener(event.type, disableOneEvent) - } - - function within(number, min, max) { - return number < min ? min : number > max ? max : number - } - var dragMouseEvents = ["mousemove", "mouseup"], - dragTouchEvents = ["touchmove", "touchend"], - wheelEvent = document.implementation.hasFeature("Event.wheel", "3.0") ? "wheel" : "mousewheel", - interactiveElements = ["INPUT", "SELECT", "TEXTAREA"], - abs = Math.abs, - sqrt = Math.sqrt, - pow = Math.pow, - round = Math.round, - max = Math.max, - scrollerFactory = (Math.min, function(frame, options) { - function ensureSizeInfo() { - requiresReflow && (requiresReflow = !1, frameSize = o.horizontal ? frame.offsetWidth : frame.offsetHeight, slideeSize = o.scrollWidth || Math.max(slideeElement[o.horizontal ? "offsetWidth" : "offsetHeight"], slideeElement[o.horizontal ? "scrollWidth" : "scrollHeight"]), self._pos.end = max(slideeSize - frameSize, 0)) - } - - function load(isInit) { - if (requiresReflow = !0, !isInit) { - ensureSizeInfo(); - var pos = self._pos; - self.slideTo(within(pos.dest, pos.start, pos.end)) - } - } - - function initFrameResizeObserver() { - var observerOptions = {}; - self.frameResizeObserver = new ResizeObserver(onResize, observerOptions), self.frameResizeObserver.observe(frame) - } - - function nativeScrollTo(container, pos, immediate) { - container.scroll ? o.horizontal ? container.scroll({ - left: pos, - behavior: immediate ? "instant" : "smooth" - }) : container.scroll({ - top: pos, - behavior: immediate ? "instant" : "smooth" - }) : !immediate && container.scrollTo ? o.horizontal ? container.scrollTo(Math.round(pos), 0) : container.scrollTo(0, Math.round(pos)) : o.horizontal ? container.scrollLeft = Math.round(pos) : container.scrollTop = Math.round(pos) - } - - function setStyleProperty(elem, name, value, speed, resetTransition) { - var style = elem.style; - (resetTransition || browser.edge) && (style.transition = "none", elem.offsetWidth), style.transition = "transform " + speed + "ms ease-out", style[name] = value - } - - function dispatchScrollEventIfNeeded() { - o.dispatchScrollEvent && frame.dispatchEvent(new CustomEvent(self.getScrollEventName(), { - bubbles: !0, - cancelable: !1 - })) - } - - function renderAnimateWithTransform(fromPosition, toPosition, immediate) { - var speed = o.speed; - immediate && (speed = o.immediateSpeed || 50), o.horizontal ? setStyleProperty(slideeElement, "transform", "translateX(" + -round(toPosition) + "px)", speed) : setStyleProperty(slideeElement, "transform", "translateY(" + -round(toPosition) + "px)", speed), self._pos.cur = toPosition, dispatchScrollEventIfNeeded() - } - - function getBoundingClientRect(elem) { - return elem.getBoundingClientRect ? elem.getBoundingClientRect() : { - top: 0, - left: 0 - } - } - - function dragInitSlidee(event) { - var isTouch = "touchstart" === event.type; - if (!(dragging.init || !isTouch && isInteractive(event.target)) && (isTouch ? o.touchDragging : o.mouseDragging && event.which < 2)) { - isTouch || event.preventDefault(), dragging.released = 0, dragging.init = 0, dragging.source = event.target, dragging.touch = isTouch; - var pointer = isTouch ? event.touches[0] : event; - dragging.initX = pointer.pageX, dragging.initY = pointer.pageY, dragging.initPos = self._pos.cur, dragging.start = +new Date, dragging.time = 0, dragging.path = 0, dragging.delta = 0, dragging.locked = 0, dragging.pathToLock = isTouch ? 30 : 10, transform && (isTouch ? dragTouchEvents.forEach(function(eventName) { - dom.addEventListener(document, eventName, dragHandler, { - passive: !0 - }) - }) : dragMouseEvents.forEach(function(eventName) { - dom.addEventListener(document, eventName, dragHandler, { - passive: !0 - }) - })) - } - } - - function dragHandler(event) { - dragging.released = "mouseup" === event.type || "touchend" === event.type; - var pointer = dragging.touch ? event[dragging.released ? "changedTouches" : "touches"][0] : event; - if (dragging.pathX = pointer.pageX - dragging.initX, dragging.pathY = pointer.pageY - dragging.initY, dragging.path = sqrt(pow(dragging.pathX, 2) + pow(dragging.pathY, 2)), dragging.delta = o.horizontal ? dragging.pathX : dragging.pathY, dragging.released || !(dragging.path < 1)) { - if (!dragging.init) { - if (dragging.path < o.dragThreshold) return dragging.released ? dragEnd() : void 0; - if (!(o.horizontal ? abs(dragging.pathX) > abs(dragging.pathY) : abs(dragging.pathX) < abs(dragging.pathY))) return dragEnd(); - dragging.init = 1 - }!dragging.locked && dragging.path > dragging.pathToLock && (dragging.locked = 1, dragging.source.addEventListener("click", disableOneEvent)), dragging.released && dragEnd(), self.slideTo(round(dragging.initPos - dragging.delta)) - } - } - - function dragEnd() { - dragging.released = !0, dragTouchEvents.forEach(function(eventName) { - dom.removeEventListener(document, eventName, dragHandler, { - passive: !0 - }) - }), dragMouseEvents.forEach(function(eventName) { - dom.removeEventListener(document, eventName, dragHandler, { - passive: !0 - }) - }), setTimeout(function() { - dragging.source.removeEventListener("click", disableOneEvent) - }), dragging.init = 0 - } - - function isInteractive(element) { - for (; element;) { - if (-1 !== interactiveElements.indexOf(element.tagName)) return !0; - element = element.parentNode - } - return !1 - } - - function normalizeWheelDelta(event) { - return scrolling.curDelta = (o.horizontal ? event.deltaY || event.deltaX : event.deltaY) || -event.wheelDelta, transform && (scrolling.curDelta /= 1 === event.deltaMode ? 3 : 100), scrolling.curDelta - } - - function scrollHandler(event) { - ensureSizeInfo(); - var pos = self._pos; - if (o.scrollBy && pos.start !== pos.end) { - var delta = normalizeWheelDelta(event); - transform ? (delta > 0 && pos.dest < pos.end || delta < 0 && (pos.dest, pos.start), self.slideBy(o.scrollBy * delta)) : (isSmoothScrollSupported && (delta *= 12), o.horizontal ? nativeScrollElement.scrollLeft += delta : nativeScrollElement.scrollTop += delta) - } - } - - function onResize(entries) { - var entry = entries[0]; - if (entry) { - var newRect = entry.contentRect; - if (0 === newRect.width || 0 === newRect.height) return; - newRect.width === contentRect.width && newRect.height === contentRect.height || (contentRect = newRect, load(!1)) - } - } - - function resetScroll() { - o.horizontal ? this.scrollLeft = 0 : this.scrollTop = 0 - } - - function onFrameClick(e) { - if (1 === e.which) { - var focusableParent = focusManager.focusableParent(e.target); - focusableParent && focusableParent !== document.activeElement && focusableParent.focus() - } - } - var o = Object.assign({}, { - slidee: null, - horizontal: !1, - mouseWheel: !0, - scrollBy: 0, - dragSource: null, - mouseDragging: 1, - touchDragging: 1, - dragThreshold: 3, - intervactive: null, - speed: 0 - }, options), - isSmoothScrollSupported = "scrollBehavior" in document.documentElement.style; - !1 === options.allowNativeScroll ? options.enableNativeScroll = !1 : isSmoothScrollSupported && (browser.firefox && !layoutManager.tv || options.allowNativeSmoothScroll) ? options.enableNativeScroll = !0 : options.requireAnimation && (browser.animate || browser.supportsCssAnimation()) ? options.enableNativeScroll = !1 : layoutManager.tv && browser.animate || (options.enableNativeScroll = !0), browser.web0s && (options.enableNativeScroll = !0); - var self = this; - self.options = o; - var slideeElement = o.slidee ? o.slidee : function(n, elem) { - for (var matched = []; n; n = n.nextSibling) 1 === n.nodeType && n !== elem && matched.push(n); - return matched - }(frame.firstChild)[0]; - self._pos = { - start: 0, - center: 0, - end: 0, - cur: 0, - dest: 0 - }; - var transform = !options.enableNativeScroll, - scrollSource = frame, - dragSourceElement = o.dragSource ? o.dragSource : frame, - dragging = { - released: 1 - }, - scrolling = { - last: 0, - delta: 0, - resetTime: 200 - }; - self.initialized = 0, self.slidee = slideeElement, self.options = o, self.dragging = dragging; - var nativeScrollElement = frame, - requiresReflow = !0, - frameSize = 0, - slideeSize = 0; - self.reload = function() { - load() - }, self.getScrollEventName = function() { - return transform ? "scrollanimate" : "scroll" - }, self.getScrollSlider = function() { - return slideeElement - }, self.getScrollFrame = function() { - return frame - }; - var lastAnimate; - self.slideTo = function(newPos, immediate, fullItemPos) { - ensureSizeInfo(); - var pos = self._pos; - if (newPos = within(newPos, pos.start, pos.end), !transform) return void nativeScrollTo(nativeScrollElement, newPos, immediate); - var from = pos.cur; - immediate = immediate || dragging.init || !o.speed; - var now = (new Date).getTime(); - o.autoImmediate && !immediate && now - (lastAnimate || 0) <= 50 && (immediate = !0), !immediate && o.skipSlideToWhenVisible && fullItemPos && fullItemPos.isVisible || newPos !== pos.dest && (pos.dest = newPos, renderAnimateWithTransform(from, newPos, immediate), lastAnimate = now) - }, self.getPos = function(item) { - var scrollElement = transform ? slideeElement : nativeScrollElement, - slideeOffset = getBoundingClientRect(scrollElement), - itemOffset = getBoundingClientRect(item), - offset = (o.horizontal ? slideeOffset.left : slideeOffset.top, o.horizontal ? slideeOffset.right : slideeOffset.bottom, o.horizontal ? itemOffset.left - slideeOffset.left : itemOffset.top - slideeOffset.top), - size = o.horizontal ? itemOffset.width : itemOffset.height; - size || 0 === size || (size = item[o.horizontal ? "offsetWidth" : "offsetHeight"]); - var centerOffset = o.centerOffset || 0; - transform || (centerOffset = 0, o.horizontal ? offset += nativeScrollElement.scrollLeft : offset += nativeScrollElement.scrollTop), ensureSizeInfo(); - var currentStart = self._pos.cur, - currentEnd = currentStart + frameSize; - return { - start: offset, - center: offset + centerOffset - frameSize / 2 + size / 2, - end: offset - frameSize + size, - size: size, - isVisible: offset >= currentStart && offset + size <= currentEnd - } - }, self.getCenterPosition = function(item) { - ensureSizeInfo(); - var pos = self.getPos(item); - return within(pos.center, pos.start, pos.end) - }, self.destroy = function() { - return self.frameResizeObserver && (self.frameResizeObserver.disconnect(), self.frameResizeObserver = null), dom.removeEventListener(frame, "scroll", resetScroll, { - passive: !0 - }), dom.removeEventListener(scrollSource, wheelEvent, scrollHandler, { - passive: !0 - }), dom.removeEventListener(dragSourceElement, "touchstart", dragInitSlidee, { - passive: !0 - }), dom.removeEventListener(frame, "click", onFrameClick, { - passive: !0, - capture: !0 - }), dom.removeEventListener(dragSourceElement, "mousedown", dragInitSlidee, {}), self.initialized = 0, self - }; - var contentRect = {}; - self.getScrollPosition = function() { - return transform ? self._pos.cur : o.horizontal ? nativeScrollElement.scrollLeft : nativeScrollElement.scrollTop - }, self.getScrollSize = function() { - return transform ? slideeSize : o.horizontal ? nativeScrollElement.scrollWidth : nativeScrollElement.scrollHeight - }, self.init = function() { - if (!self.initialized) return transform ? (frame.style.overflow = "hidden", slideeElement.style["will-change"] = "transform", slideeElement.style.transition = "transform " + o.speed + "ms ease-out", o.horizontal ? slideeElement.classList.add("animatedScrollX") : slideeElement.classList.add("animatedScrollY")) : o.horizontal ? (layoutManager.desktop && !o.hideScrollbar ? nativeScrollElement.classList.add("scrollX") : (nativeScrollElement.classList.add("scrollX"), nativeScrollElement.classList.add("hiddenScrollX"), layoutManager.tv && !1 !== o.allowNativeSmoothScroll && nativeScrollElement.classList.add("smoothScrollX")), o.forceHideScrollbars && nativeScrollElement.classList.add("hiddenScrollX-forced")) : (layoutManager.desktop && !o.hideScrollbar ? nativeScrollElement.classList.add("scrollY") : (nativeScrollElement.classList.add("scrollY"), nativeScrollElement.classList.add("hiddenScrollY"), layoutManager.tv && !1 !== o.allowNativeSmoothScroll && nativeScrollElement.classList.add("smoothScrollY")), o.forceHideScrollbars && nativeScrollElement.classList.add("hiddenScrollY-forced")), (transform || layoutManager.tv) && dom.addEventListener(dragSourceElement, "mousedown", dragInitSlidee, {}), initFrameResizeObserver(), transform ? (dom.addEventListener(dragSourceElement, "touchstart", dragInitSlidee, { - passive: !0 - }), o.horizontal || dom.addEventListener(frame, "scroll", resetScroll, { - passive: !0 - }), o.mouseWheel && dom.addEventListener(scrollSource, wheelEvent, scrollHandler, { - passive: !0 - })) : o.horizontal && o.mouseWheel && dom.addEventListener(scrollSource, wheelEvent, scrollHandler, { - passive: !0 - }), dom.addEventListener(frame, "click", onFrameClick, { - passive: !0, - capture: !0 - }), self.initialized = 1, load(!0), self - } - }); - return scrollerFactory.prototype.slideBy = function(delta, immediate) { - delta && this.slideTo(this._pos.dest + delta, immediate) - }, scrollerFactory.prototype.to = function(location, item, immediate) { - if ("boolean" === type(item) && (immediate = item, item = void 0), void 0 === item) this.slideTo(this._pos[location], immediate); - else { - var itemPos = this.getPos(item); - itemPos && this.slideTo(itemPos[location], immediate, itemPos) + if (value == null) { + return String(value); } - }, scrollerFactory.prototype.toStart = function(item, immediate) { - this.to("start", item, immediate) - }, scrollerFactory.prototype.toEnd = function(item, immediate) { - this.to("end", item, immediate) - }, scrollerFactory.prototype.toCenter = function(item, immediate) { - this.to("center", item, immediate) - }, scrollerFactory.create = function(frame, options) { + + if (typeof value === 'object' || typeof value === 'function') { + return Object.prototype.toString.call(value).match(/\s([a-z]+)/i)[1].toLowerCase() || 'object'; + } + + return typeof value; + } + + /** + * Disables an event it was triggered on and unbinds itself. + * + * @param {Event} event + * + * @return {Void} + */ + function disableOneEvent(event) { + /*jshint validthis:true */ + event.preventDefault(); + event.stopPropagation(); + this.removeEventListener(event.type, disableOneEvent); + } + + /** + * Make sure that number is within the limits. + * + * @param {Number} number + * @param {Number} min + * @param {Number} max + * + * @return {Number} + */ + function within(number, min, max) { + return number < min ? min : number > max ? max : number; + } + + // Other global values + var dragMouseEvents = ['mousemove', 'mouseup']; + var dragTouchEvents = ['touchmove', 'touchend']; + var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel' : 'mousewheel'); + var interactiveElements = ['INPUT', 'SELECT', 'TEXTAREA']; + var tmpArray = []; + var time; + + // Math shorthands + var abs = Math.abs; + var sqrt = Math.sqrt; + var pow = Math.pow; + var round = Math.round; + var max = Math.max; + var min = Math.min; + + var scrollerFactory = function (frame, options) { + + // Extend options + var o = Object.assign({}, { + slidee: null, // Selector, DOM element, or jQuery object with DOM element representing SLIDEE. + horizontal: false, // Switch to horizontal mode. + + // Scrolling + mouseWheel: true, + scrollBy: 0, // Pixels or items to move per one mouse scroll. 0 to disable scrolling + + // Dragging + dragSource: null, // Selector or DOM element for catching dragging events. Default is FRAME. + mouseDragging: 1, // Enable navigation by dragging the SLIDEE with mouse cursor. + touchDragging: 1, // Enable navigation by dragging the SLIDEE with touch events. + dragThreshold: 3, // Distance in pixels before Sly recognizes dragging. + intervactive: null, // Selector for special interactive elements. + + // Mixed options + speed: 0, // Animations speed in milliseconds. 0 to disable animations. + + }, options); + + var isSmoothScrollSupported = 'scrollBehavior' in document.documentElement.style; + + // native scroll is a must with touch input + // also use native scroll when scrolling vertically in desktop mode - excluding horizontal because the mouse wheel support is choppy at the moment + // in cases with firefox, if the smooth scroll api is supported then use that because their implementation is very good + if (options.allowNativeScroll === false) { + options.enableNativeScroll = false; + } + else if (isSmoothScrollSupported && ((browser.firefox && !layoutManager.tv) || options.allowNativeSmoothScroll)) { + // native smooth scroll + options.enableNativeScroll = true; + } + else if (options.requireAnimation && (browser.animate || browser.supportsCssAnimation())) { + + // transform is the only way to guarantee animation + options.enableNativeScroll = false; + } + else if (!layoutManager.tv || !browser.animate) { + + options.enableNativeScroll = true; + } + + // Need this for the magic wheel. With the animated scroll the magic wheel will run off of the screen + if (browser.web0s) { + options.enableNativeScroll = true; + } + + // Private variables + var self = this; + self.options = o; + + // Frame + var slideeElement = o.slidee ? o.slidee : sibling(frame.firstChild)[0]; + self._pos = { + start: 0, + center: 0, + end: 0, + cur: 0, + dest: 0 + }; + + var transform = !options.enableNativeScroll; + + // Miscellaneous + var scrollSource = frame; + var dragSourceElement = o.dragSource ? o.dragSource : frame; + var dragging = { + released: 1 + }; + var scrolling = { + last: 0, + delta: 0, + resetTime: 200 + }; + + // Expose properties + self.initialized = 0; + self.slidee = slideeElement; + self.options = o; + self.dragging = dragging; + + var nativeScrollElement = frame; + + function sibling(n, elem) { + var matched = []; + + for (; n; n = n.nextSibling) { + if (n.nodeType === 1 && n !== elem) { + matched.push(n); + } + } + return matched; + } + + var requiresReflow = true; + + var frameSize = 0; + var slideeSize = 0; + function ensureSizeInfo() { + + if (requiresReflow) { + + requiresReflow = false; + + // Reset global variables + frameSize = o.horizontal ? (frame).offsetWidth : (frame).offsetHeight; + + slideeSize = o.scrollWidth || Math.max(slideeElement[o.horizontal ? 'offsetWidth' : 'offsetHeight'], slideeElement[o.horizontal ? 'scrollWidth' : 'scrollHeight']); + + // Set position limits & relativess + self._pos.end = max(slideeSize - frameSize, 0); + } + } + + /** + * Loading function. + * + * Populate arrays, set sizes, bind events, ... + * + * @param {Boolean} [isInit] Whether load is called from within self.init(). + * @return {Void} + */ + function load(isInit) { + + requiresReflow = true; + + if (!isInit) { + + ensureSizeInfo(); + + // Fix possible overflowing + var pos = self._pos; + self.slideTo(within(pos.dest, pos.start, pos.end)); + } + } + + function initFrameResizeObserver() { + + var observerOptions = {}; + + self.frameResizeObserver = new ResizeObserver(onResize, observerOptions); + + self.frameResizeObserver.observe(frame); + } + + self.reload = function () { load(); }; + + self.getScrollEventName = function () { + return transform ? 'scrollanimate' : 'scroll'; + }; + + self.getScrollSlider = function () { + return slideeElement; + }; + + self.getScrollFrame = function () { + return frame; + }; + + function nativeScrollTo(container, pos, immediate) { + + + if (container.scroll) { + if (o.horizontal) { + + container.scroll({ + left: pos, + behavior: immediate ? 'instant' : 'smooth' + }); + } else { + + container.scroll({ + top: pos, + behavior: immediate ? 'instant' : 'smooth' + }); + } + } + else if (!immediate && container.scrollTo) { + if (o.horizontal) { + container.scrollTo(Math.round(pos), 0); + } else { + container.scrollTo(0, Math.round(pos)); + } + } else { + if (o.horizontal) { + container.scrollLeft = Math.round(pos); + } else { + container.scrollTop = Math.round(pos); + } + } + } + + var lastAnimate; + + /** + * Animate to a position. + * + * @param {Int} newPos New position. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ + self.slideTo = function (newPos, immediate, fullItemPos) { + + ensureSizeInfo(); + var pos = self._pos; + + newPos = within(newPos, pos.start, pos.end); + + if (!transform) { + + nativeScrollTo(nativeScrollElement, newPos, immediate); + return; + } + + // Update the animation object + var from = pos.cur; + immediate = immediate || dragging.init || !o.speed; + + var now = new Date().getTime(); + + if (o.autoImmediate) { + if (!immediate && (now - (lastAnimate || 0)) <= 50) { + immediate = true; + } + } + + if (!immediate && o.skipSlideToWhenVisible && fullItemPos && fullItemPos.isVisible) { + + return; + } + + // Start animation rendering + if (newPos !== pos.dest) { + pos.dest = newPos; + + renderAnimateWithTransform(from, newPos, immediate); + + lastAnimate = now; + } + }; + + function setStyleProperty(elem, name, value, speed, resetTransition) { + + var style = elem.style; + + if (resetTransition || browser.edge) { + style.transition = 'none'; + void elem.offsetWidth; + } + + style.transition = 'transform ' + speed + 'ms ease-out'; + style[name] = value; + } + + function dispatchScrollEventIfNeeded() { + if (o.dispatchScrollEvent) { + frame.dispatchEvent(new CustomEvent(self.getScrollEventName(), { + bubbles: true, + cancelable: false + })); + } + } + + function renderAnimateWithTransform(fromPosition, toPosition, immediate) { + + var speed = o.speed; + + if (immediate) { + speed = o.immediateSpeed || 50; + } + + if (o.horizontal) { + setStyleProperty(slideeElement, 'transform', 'translateX(' + (-round(toPosition)) + 'px)', speed); + } else { + setStyleProperty(slideeElement, 'transform', 'translateY(' + (-round(toPosition)) + 'px)', speed); + } + self._pos.cur = toPosition; + + dispatchScrollEventIfNeeded(); + } + + function getBoundingClientRect(elem) { + + // Support: BlackBerry 5, iOS 3 (original iPhone) + // If we don't have gBCR, just use 0,0 rather than error + if (elem.getBoundingClientRect) { + return elem.getBoundingClientRect(); + } else { + return { top: 0, left: 0 }; + } + } + + /** + * Returns the position object. + * + * @param {Mixed} item + * + * @return {Object} + */ + self.getPos = function (item) { + + var scrollElement = transform ? slideeElement : nativeScrollElement; + var slideeOffset = getBoundingClientRect(scrollElement); + var itemOffset = getBoundingClientRect(item); + + var slideeStartPos = o.horizontal ? slideeOffset.left : slideeOffset.top; + var slideeEndPos = o.horizontal ? slideeOffset.right : slideeOffset.bottom; + + var offset = o.horizontal ? itemOffset.left - slideeOffset.left : itemOffset.top - slideeOffset.top; + + var size = o.horizontal ? itemOffset.width : itemOffset.height; + if (!size && size !== 0) { + size = item[o.horizontal ? 'offsetWidth' : 'offsetHeight']; + } + + var centerOffset = o.centerOffset || 0; + + if (!transform) { + centerOffset = 0; + if (o.horizontal) { + offset += nativeScrollElement.scrollLeft; + } else { + offset += nativeScrollElement.scrollTop; + } + } + + ensureSizeInfo(); + + var currentStart = self._pos.cur; + var currentEnd = currentStart + frameSize; + + //console.log('offset:' + offset + ' currentStart:' + currentStart + ' currentEnd:' + currentEnd); + var isVisible = offset >= currentStart && (offset + size) <= currentEnd; + + return { + start: offset, + center: offset + centerOffset - (frameSize / 2) + (size / 2), + end: offset - frameSize + size, + size: size, + isVisible: isVisible + }; + }; + + self.getCenterPosition = function (item) { + + ensureSizeInfo(); + + var pos = self.getPos(item); + return within(pos.center, pos.start, pos.end); + }; + + function dragInitSlidee(event) { + var isTouch = event.type === 'touchstart'; + + // Ignore when already in progress, or interactive element in non-touch navivagion + if (dragging.init || !isTouch && isInteractive(event.target)) { + return; + } + + // SLIDEE dragging conditions + if (!(isTouch ? o.touchDragging : o.mouseDragging && event.which < 2)) { + return; + } + + if (!isTouch) { + // prevents native image dragging in Firefox + event.preventDefault(); + } + + // Reset dragging object + dragging.released = 0; + + // Properties used in dragHandler + dragging.init = 0; + dragging.source = event.target; + dragging.touch = isTouch; + var pointer = isTouch ? event.touches[0] : event; + dragging.initX = pointer.pageX; + dragging.initY = pointer.pageY; + dragging.initPos = self._pos.cur; + dragging.start = +new Date(); + dragging.time = 0; + dragging.path = 0; + dragging.delta = 0; + dragging.locked = 0; + dragging.pathToLock = isTouch ? 30 : 10; + + // Bind dragging events + if (transform) { + + if (isTouch) { + dragTouchEvents.forEach(function (eventName) { + dom.addEventListener(document, eventName, dragHandler, { + passive: true + }); + }); + } else { + dragMouseEvents.forEach(function (eventName) { + dom.addEventListener(document, eventName, dragHandler, { + passive: true + }); + }); + } + } + } + + /** + * Handler for dragging scrollbar handle or SLIDEE. + * + * @param {Event} event + * + * @return {Void} + */ + function dragHandler(event) { + dragging.released = event.type === 'mouseup' || event.type === 'touchend'; + var pointer = dragging.touch ? event[dragging.released ? 'changedTouches' : 'touches'][0] : event; + dragging.pathX = pointer.pageX - dragging.initX; + dragging.pathY = pointer.pageY - dragging.initY; + dragging.path = sqrt(pow(dragging.pathX, 2) + pow(dragging.pathY, 2)); + dragging.delta = o.horizontal ? dragging.pathX : dragging.pathY; + + if (!dragging.released && dragging.path < 1) { + return; + } + + // We haven't decided whether this is a drag or not... + if (!dragging.init) { + // If the drag path was very short, maybe it's not a drag? + if (dragging.path < o.dragThreshold) { + // If the pointer was released, the path will not become longer and it's + // definitely not a drag. If not released yet, decide on next iteration + return dragging.released ? dragEnd() : undefined; + } else { + // If dragging path is sufficiently long we can confidently start a drag + // if drag is in different direction than scroll, ignore it + if (o.horizontal ? abs(dragging.pathX) > abs(dragging.pathY) : abs(dragging.pathX) < abs(dragging.pathY)) { + dragging.init = 1; + } else { + return dragEnd(); + } + } + } + + //event.preventDefault(); + + // Disable click on a source element, as it is unwelcome when dragging + if (!dragging.locked && dragging.path > dragging.pathToLock) { + dragging.locked = 1; + dragging.source.addEventListener('click', disableOneEvent); + } + + // Cancel dragging on release + if (dragging.released) { + dragEnd(); + } + + self.slideTo(round(dragging.initPos - dragging.delta)); + } + + /** + * Stops dragging and cleans up after it. + * + * @return {Void} + */ + function dragEnd() { + dragging.released = true; + + dragTouchEvents.forEach(function (eventName) { + dom.removeEventListener(document, eventName, dragHandler, { + passive: true + }); + }); + + dragMouseEvents.forEach(function (eventName) { + dom.removeEventListener(document, eventName, dragHandler, { + passive: true + }); + }); + + // Make sure that disableOneEvent is not active in next tick. + setTimeout(function () { + dragging.source.removeEventListener('click', disableOneEvent); + }); + + dragging.init = 0; + } + + /** + * Check whether element is interactive. + * + * @return {Boolean} + */ + function isInteractive(element) { + + while (element) { + + if (interactiveElements.indexOf(element.tagName) !== -1) { + return true; + } + + element = element.parentNode; + } + return false; + } + + /** + * Mouse wheel delta normalization. + * + * @param {Event} event + * + * @return {Int} + */ + function normalizeWheelDelta(event) { + // wheelDelta needed only for IE8- + scrolling.curDelta = ((o.horizontal ? event.deltaY || event.deltaX : event.deltaY) || -event.wheelDelta); + + if (transform) { + scrolling.curDelta /= event.deltaMode === 1 ? 3 : 100; + } + return scrolling.curDelta; + } + + /** + * Mouse scrolling handler. + * + * @param {Event} event + * + * @return {Void} + */ + function scrollHandler(event) { + + ensureSizeInfo(); + var pos = self._pos; + // Ignore if there is no scrolling to be done + if (!o.scrollBy || pos.start === pos.end) { + return; + } + var delta = normalizeWheelDelta(event); + + if (transform) { + // Trap scrolling only when necessary and/or requested + if (delta > 0 && pos.dest < pos.end || delta < 0 && pos.dest > pos.start) { + //stopDefault(event, 1); + } + + self.slideBy(o.scrollBy * delta); + } else { + + if (isSmoothScrollSupported) { + delta *= 12; + } + + if (o.horizontal) { + nativeScrollElement.scrollLeft += delta; + } else { + nativeScrollElement.scrollTop += delta; + } + } + } + + /** + * Destroys instance and everything it created. + * + * @return {Void} + */ + self.destroy = function () { + + if (self.frameResizeObserver) { + self.frameResizeObserver.disconnect(); + self.frameResizeObserver = null; + } + + // Reset native FRAME element scroll + dom.removeEventListener(frame, 'scroll', resetScroll, { + passive: true + }); + + dom.removeEventListener(scrollSource, wheelEvent, scrollHandler, { + passive: true + }); + + dom.removeEventListener(dragSourceElement, 'touchstart', dragInitSlidee, { + passive: true + }); + + dom.removeEventListener(frame, 'click', onFrameClick, { + passive: true, + capture: true + }); + + dom.removeEventListener(dragSourceElement, 'mousedown', dragInitSlidee, { + //passive: true + }); + + // Reset initialized status and return the instance + self.initialized = 0; + return self; + }; + + var contentRect = {}; + + function onResize(entries) { + + var entry = entries[0]; + + if (entry) { + + var newRect = entry.contentRect; + + // handle element being hidden + if (newRect.width === 0 || newRect.height === 0) { + return; + } + + if (newRect.width !== contentRect.width || newRect.height !== contentRect.height) { + + contentRect = newRect; + + load(false); + } + } + } + + function resetScroll() { + if (o.horizontal) { + this.scrollLeft = 0; + } else { + this.scrollTop = 0; + } + } + + function onFrameClick(e) { + if (e.which === 1) { + var focusableParent = focusManager.focusableParent(e.target); + if (focusableParent && focusableParent !== document.activeElement) { + focusableParent.focus(); + } + } + } + + self.getScrollPosition = function () { + + if (transform) { + return self._pos.cur; + } + + if (o.horizontal) { + return nativeScrollElement.scrollLeft; + } else { + return nativeScrollElement.scrollTop; + } + }; + + self.getScrollSize = function () { + + if (transform) { + return slideeSize; + } + + if (o.horizontal) { + return nativeScrollElement.scrollWidth; + } else { + return nativeScrollElement.scrollHeight; + } + }; + + /** + * Initialize. + * + * @return {Object} + */ + self.init = function () { + if (self.initialized) { + return; + } + + if (!transform) { + if (o.horizontal) { + if (layoutManager.desktop && !o.hideScrollbar) { + nativeScrollElement.classList.add('scrollX'); + } else { + nativeScrollElement.classList.add('scrollX'); + nativeScrollElement.classList.add('hiddenScrollX'); + + if (layoutManager.tv && o.allowNativeSmoothScroll !== false) { + nativeScrollElement.classList.add('smoothScrollX'); + } + } + + if (o.forceHideScrollbars) { + nativeScrollElement.classList.add('hiddenScrollX-forced'); + } + } else { + if (layoutManager.desktop && !o.hideScrollbar) { + nativeScrollElement.classList.add('scrollY'); + } else { + nativeScrollElement.classList.add('scrollY'); + nativeScrollElement.classList.add('hiddenScrollY'); + + if (layoutManager.tv && o.allowNativeSmoothScroll !== false) { + nativeScrollElement.classList.add('smoothScrollY'); + } + } + + if (o.forceHideScrollbars) { + nativeScrollElement.classList.add('hiddenScrollY-forced'); + } + } + } else { + frame.style.overflow = 'hidden'; + slideeElement.style['will-change'] = 'transform'; + slideeElement.style.transition = 'transform ' + o.speed + 'ms ease-out'; + + if (o.horizontal) { + slideeElement.classList.add('animatedScrollX'); + } else { + slideeElement.classList.add('animatedScrollY'); + } + } + + if (transform || layoutManager.tv) { + // This can prevent others from being able to listen to mouse events + dom.addEventListener(dragSourceElement, 'mousedown', dragInitSlidee, { + //passive: true + }); + } + + initFrameResizeObserver(); + + if (transform) { + + dom.addEventListener(dragSourceElement, 'touchstart', dragInitSlidee, { + passive: true + }); + + if (!o.horizontal) { + dom.addEventListener(frame, 'scroll', resetScroll, { + passive: true + }); + } + + if (o.mouseWheel) { + // Scrolling navigation + dom.addEventListener(scrollSource, wheelEvent, scrollHandler, { + passive: true + }); + } + + } else if (o.horizontal) { + + // Don't bind to mouse events with vertical scroll since the mouse wheel can handle this natively + + if (o.mouseWheel) { + // Scrolling navigation + dom.addEventListener(scrollSource, wheelEvent, scrollHandler, { + passive: true + }); + } + } + + dom.addEventListener(frame, 'click', onFrameClick, { + passive: true, + capture: true + }); + + // Mark instance as initialized + self.initialized = 1; + + // Load + load(true); + + // Return instance + return self; + }; + }; + + /** + * Slide SLIDEE by amount of pixels. + * + * @param {Int} delta Pixels/Items. Positive means forward, negative means backward. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ + scrollerFactory.prototype.slideBy = function (delta, immediate) { + if (!delta) { + return; + } + this.slideTo(this._pos.dest + delta, immediate); + }; + + /** + * Core method for handling `toLocation` methods. + * + * @param {String} location + * @param {Mixed} item + * @param {Bool} immediate + * + * @return {Void} + */ + scrollerFactory.prototype.to = function (location, item, immediate) { + // Optional arguments logic + if (type(item) === 'boolean') { + immediate = item; + item = undefined; + } + + if (item === undefined) { + this.slideTo(this._pos[location], immediate); + } else { + + //if (!transform) { + + // item.scrollIntoView(); + // return; + //} + + var itemPos = this.getPos(item); + + if (itemPos) { + this.slideTo(itemPos[location], immediate, itemPos); + } + } + }; + + /** + * Animate element or the whole SLIDEE to the start of the frame. + * + * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ + scrollerFactory.prototype.toStart = function (item, immediate) { + this.to('start', item, immediate); + }; + + /** + * Animate element or the whole SLIDEE to the end of the frame. + * + * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ + scrollerFactory.prototype.toEnd = function (item, immediate) { + this.to('end', item, immediate); + }; + + /** + * Animate element or the whole SLIDEE to the center of the frame. + * + * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ + scrollerFactory.prototype.toCenter = function (item, immediate) { + this.to('center', item, immediate); + }; + + scrollerFactory.create = function (frame, options) { var instance = new scrollerFactory(frame, options); - return Promise.resolve(instance) - }, scrollerFactory + return Promise.resolve(instance); + }; + + return scrollerFactory; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/scrollhelper.js b/src/bower_components/emby-webcomponents/scrollhelper.js index 4ae5262ec4..6280dc5062 100644 --- a/src/bower_components/emby-webcomponents/scrollhelper.js +++ b/src/bower_components/emby-webcomponents/scrollhelper.js @@ -1,77 +1,137 @@ -define(["focusManager", "dom", "scrollStyles"], function(focusManager, dom) { - "use strict"; +define(['focusManager', 'dom', 'scrollStyles'], function (focusManager, dom) { + 'use strict'; function getBoundingClientRect(elem) { - return elem.getBoundingClientRect ? elem.getBoundingClientRect() : { - top: 0, - left: 0 + + // Support: BlackBerry 5, iOS 3 (original iPhone) + // If we don't have gBCR, just use 0,0 rather than error + if (elem.getBoundingClientRect) { + return elem.getBoundingClientRect(); + } else { + return { top: 0, left: 0 }; } } function getPosition(scrollContainer, item, horizontal) { - var slideeOffset = getBoundingClientRect(scrollContainer), - itemOffset = getBoundingClientRect(item), - offset = horizontal ? itemOffset.left - slideeOffset.left : itemOffset.top - slideeOffset.top, - size = horizontal ? itemOffset.width : itemOffset.height; - size || 0 === size || (size = item[horizontal ? "offsetWidth" : "offsetHeight"]); + + var slideeOffset = getBoundingClientRect(scrollContainer); + var itemOffset = getBoundingClientRect(item); + + var offset = horizontal ? itemOffset.left - slideeOffset.left : itemOffset.top - slideeOffset.top; + var size = horizontal ? itemOffset.width : itemOffset.height; + if (!size && size !== 0) { + size = item[horizontal ? 'offsetWidth' : 'offsetHeight']; + } + var currentStart = horizontal ? scrollContainer.scrollLeft : scrollContainer.scrollTop; + offset += currentStart; - var frameSize = horizontal ? scrollContainer.offsetWidth : scrollContainer.offsetHeight, - currentEnd = currentStart + frameSize; + + var frameSize = horizontal ? scrollContainer.offsetWidth : scrollContainer.offsetHeight; + + var currentEnd = currentStart + frameSize; + + var isVisible = offset >= currentStart && (offset + size) <= currentEnd; + return { start: offset, - center: offset - frameSize / 2 + size / 2, + center: (offset - (frameSize / 2) + (size / 2)), end: offset - frameSize + size, size: size, - isVisible: offset >= currentStart && offset + size <= currentEnd - } + isVisible: isVisible + }; } function toCenter(container, elem, horizontal, skipWhenVisible) { var pos = getPosition(container, elem, horizontal); - skipWhenVisible && pos.isVisible || (container.scrollTo ? horizontal ? container.scrollTo(pos.center, 0) : container.scrollTo(0, pos.center) : horizontal ? container.scrollLeft = Math.round(pos.center) : container.scrollTop = Math.round(pos.center)) + + if (skipWhenVisible && pos.isVisible) { + return; + } + + if (container.scrollTo) { + if (horizontal) { + container.scrollTo(pos.center, 0); + } else { + container.scrollTo(0, pos.center); + } + } else { + if (horizontal) { + container.scrollLeft = Math.round(pos.center); + } else { + container.scrollTop = Math.round(pos.center); + } + } } function toStart(container, elem, horizontal, skipWhenVisible) { var pos = getPosition(container, elem, horizontal); - skipWhenVisible && pos.isVisible || (container.scrollTo ? horizontal ? container.scrollTo(pos.start, 0) : container.scrollTo(0, pos.start) : horizontal ? container.scrollLeft = Math.round(pos.start) : container.scrollTop = Math.round(pos.start)) + + if (skipWhenVisible && pos.isVisible) { + return; + } + + if (container.scrollTo) { + if (horizontal) { + container.scrollTo(pos.start, 0); + } else { + container.scrollTo(0, pos.start); + } + } else { + if (horizontal) { + container.scrollLeft = Math.round(pos.start); + } else { + container.scrollTop = Math.round(pos.start); + } + } } function centerOnFocus(e, scrollSlider, horizontal) { var focused = focusManager.focusableParent(e.target); - focused && toCenter(scrollSlider, focused, horizontal) + + if (focused) { + toCenter(scrollSlider, focused, horizontal); + } } function centerOnFocusHorizontal(e) { - centerOnFocus(e, this, !0) + centerOnFocus(e, this, true); + } + function centerOnFocusVertical(e) { + centerOnFocus(e, this, false); } - function centerOnFocusVertical(e) { - centerOnFocus(e, this, !1) - } return { getPosition: getPosition, centerFocus: { - on: function(element, horizontal) { - horizontal ? dom.addEventListener(element, "focus", centerOnFocusHorizontal, { - capture: !0, - passive: !0 - }) : dom.addEventListener(element, "focus", centerOnFocusVertical, { - capture: !0, - passive: !0 - }) + on: function (element, horizontal) { + if (horizontal) { + dom.addEventListener(element, 'focus', centerOnFocusHorizontal, { + capture: true, + passive: true + }); + } else { + dom.addEventListener(element, 'focus', centerOnFocusVertical, { + capture: true, + passive: true + }); + } }, - off: function(element, horizontal) { - horizontal ? dom.removeEventListener(element, "focus", centerOnFocusHorizontal, { - capture: !0, - passive: !0 - }) : dom.removeEventListener(element, "focus", centerOnFocusVertical, { - capture: !0, - passive: !0 - }) + off: function (element, horizontal) { + if (horizontal) { + dom.removeEventListener(element, 'focus', centerOnFocusHorizontal, { + capture: true, + passive: true + }); + } else { + dom.removeEventListener(element, 'focus', centerOnFocusVertical, { + capture: true, + passive: true + }); + } } }, toCenter: toCenter, toStart: toStart - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/scrollstyles.css b/src/bower_components/emby-webcomponents/scrollstyles.css index 7664b396c8..aa2f7dafad 100644 --- a/src/bower_components/emby-webcomponents/scrollstyles.css +++ b/src/bower_components/emby-webcomponents/scrollstyles.css @@ -1,49 +1,53 @@ -.smoothScrollX, -.smoothScrollY { - scroll-behavior: smooth -} - .scrollX { overflow-x: auto; -webkit-overflow-scrolling: touch; overflow-y: hidden; - white-space: nowrap + white-space: nowrap; } -.hiddenScrollX, -.layout-tv .scrollX { - -ms-overflow-style: none +.smoothScrollX { + scroll-behavior: smooth; +} + +.hiddenScrollX, .layout-tv .scrollX { + -ms-overflow-style: none; + /* Can't do this because it not only hides the scrollbar, but also prevents scrolling */ + /*overflow: -moz-scrollbars-none;*/ } .hiddenScrollX-forced { - overflow: -moz-scrollbars-none + overflow: -moz-scrollbars-none; +} + +.hiddenScrollX::-webkit-scrollbar, .layout-tv .scrollX::-webkit-scrollbar { + height: 0 !important; + display: none; +} + +.scrollY { + overflow-y: auto; + -webkit-overflow-scrolling: touch; + overflow-x: hidden; } -.scrollY, .smoothScrollY { overflow-y: auto; -webkit-overflow-scrolling: touch; - overflow-x: hidden + overflow-x: hidden; + scroll-behavior: smooth; } -.hiddenScrollX::-webkit-scrollbar, -.layout-tv .scrollX::-webkit-scrollbar { - height: 0 !important; - display: none -} - -.hiddenScrollY, -.layout-tv .smoothScrollY { - -ms-overflow-style: none +.hiddenScrollY, .layout-tv .smoothScrollY { + -ms-overflow-style: none; + /* Can't do this because it not only hides the scrollbar, but also prevents scrolling */ + /*overflow: -moz-scrollbars-none;*/ } .hiddenScrollY-forced { - overflow: -moz-scrollbars-none + overflow: -moz-scrollbars-none; } -.hiddenScrollY::-webkit-scrollbar, -.layout-tv .scrollY::-webkit-scrollbar, -.layout-tv .smoothScrollY::-webkit-scrollbar { +.hiddenScrollY::-webkit-scrollbar, .layout-tv .smoothScrollY::-webkit-scrollbar, .layout-tv .scrollY::-webkit-scrollbar { width: 0 !important; - display: none + display: none; } \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/search/searchfields.css b/src/bower_components/emby-webcomponents/search/searchfields.css index 114f25a2ae..4dea76771b 100644 --- a/src/bower_components/emby-webcomponents/search/searchfields.css +++ b/src/bower_components/emby-webcomponents/search/searchfields.css @@ -1,12 +1,11 @@ -.searchFieldsInner { +.searchFieldsInner { max-width: 60em; - margin: 0 auto + margin: 0 auto; } .searchfields-icon { margin-bottom: .1em; margin-right: .25em; font-size: 2em; - -webkit-align-self: flex-end; - align-self: flex-end -} \ No newline at end of file + align-self: flex-end; +} diff --git a/src/bower_components/emby-webcomponents/search/searchfields.js b/src/bower_components/emby-webcomponents/search/searchfields.js index e52fd7ef24..7468dffbd9 100644 --- a/src/bower_components/emby-webcomponents/search/searchfields.js +++ b/src/bower_components/emby-webcomponents/search/searchfields.js @@ -1,64 +1,124 @@ -define(["layoutManager", "globalize", "require", "events", "browser", "alphaPicker", "emby-input", "flexStyles", "material-icons", "css!./searchfields"], function(layoutManager, globalize, require, events, browser, AlphaPicker) { - "use strict"; +define(['layoutManager', 'globalize', 'require', 'events', 'browser', 'alphaPicker', 'emby-input', 'flexStyles', 'material-icons', 'css!./searchfields'], function (layoutManager, globalize, require, events, browser, AlphaPicker) { + 'use strict'; function onSearchTimeout() { - var instance = this, - value = instance.nextSearchValue; - value = (value || "").trim(), events.trigger(instance, "search", [value]) + + var instance = this; + var value = instance.nextSearchValue; + + value = (value || '').trim(); + events.trigger(instance, 'search', [value]); } function triggerSearch(instance, value) { - instance.searchTimeout && clearTimeout(instance.searchTimeout), instance.nextSearchValue = value, instance.searchTimeout = setTimeout(onSearchTimeout.bind(instance), 400) + + if (instance.searchTimeout) { + clearTimeout(instance.searchTimeout); + } + + instance.nextSearchValue = value; + instance.searchTimeout = setTimeout(onSearchTimeout.bind(instance), 400); } function onAlphaValueClicked(e) { - var value = e.detail.value, - searchFieldsInstance = this, - txtSearch = searchFieldsInstance.options.element.querySelector(".searchfields-txtSearch"); - if ("backspace" === value) { + + var value = e.detail.value; + var searchFieldsInstance = this; + + var txtSearch = searchFieldsInstance.options.element.querySelector('.searchfields-txtSearch'); + + if (value === 'backspace') { + var val = txtSearch.value; - txtSearch.value = val.length ? val.substring(0, val.length - 1) : "" - } else txtSearch.value += value; - txtSearch.dispatchEvent(new CustomEvent("input", { - bubbles: !0 - })) + txtSearch.value = val.length ? val.substring(0, val.length - 1) : ''; + + } else { + txtSearch.value += value; + } + + txtSearch.dispatchEvent(new CustomEvent('input', { + bubbles: true + })); } function initAlphaPicker(alphaPickerElement, instance) { + instance.alphaPicker = new AlphaPicker({ element: alphaPickerElement, - mode: "keyboard" - }), alphaPickerElement.addEventListener("alphavalueclicked", onAlphaValueClicked.bind(instance)) + mode: 'keyboard' + }); + + alphaPickerElement.addEventListener('alphavalueclicked', onAlphaValueClicked.bind(instance)); } function onSearchInput(e) { - triggerSearch(this, e.target.value) + + var value = e.target.value; + var searchFieldsInstance = this; + triggerSearch(searchFieldsInstance, value); } function embed(elem, instance, options) { - require(["text!./searchfields.template.html"], function(template) { - var html = globalize.translateDocument(template, "sharedcomponents"); - (browser.tizen || browser.orsay) && (html = html.replace("'; - return itemHtml += i.Name, itemHtml += "
    " - }).join(""), - searchSuggestions = context.querySelector(".searchSuggestions"); - searchSuggestions.querySelector(".searchSuggestionsList").innerHTML = html, result.Items.length && searchSuggestions.classList.remove("hide") - }) + + apiClient.getItems(apiClient.getCurrentUserId(), options).then(function (result) { + + if (instance.mode !== 'suggestions') { + result.Items = []; + } + + var html = result.Items.map(function (i) { + + var href = appRouter.getRouteUrl(i); + + var itemHtml = '
    '; + itemHtml += i.Name; + itemHtml += '
    '; + return itemHtml; + + }).join(''); + + var searchSuggestions = context.querySelector('.searchSuggestions'); + searchSuggestions.querySelector('.searchSuggestionsList').innerHTML = html; + + if (result.Items.length) { + searchSuggestions.classList.remove('hide'); + } + }); } function getSearchHints(instance, apiClient, query) { - if (!query.searchTerm) return Promise.resolve({ - SearchHints: [] - }); - var allowSearch = !0, - queryIncludeItemTypes = query.IncludeItemTypes; - if ("tvshows" === instance.options.collectionType ? query.IncludeArtists ? allowSearch = !1 : "Movie" !== queryIncludeItemTypes && "LiveTvProgram" !== queryIncludeItemTypes && "MusicAlbum" !== queryIncludeItemTypes && "Audio" !== queryIncludeItemTypes && "Book" !== queryIncludeItemTypes && "AudioBook" !== queryIncludeItemTypes && "Playlist" !== queryIncludeItemTypes && "PhotoAlbum" !== queryIncludeItemTypes && "Video" !== query.MediaTypes && "Photo" !== query.MediaTypes || (allowSearch = !1) : "movies" === instance.options.collectionType ? query.IncludeArtists ? allowSearch = !1 : "Series" !== queryIncludeItemTypes && "Episode" !== queryIncludeItemTypes && "LiveTvProgram" !== queryIncludeItemTypes && "MusicAlbum" !== queryIncludeItemTypes && "Audio" !== queryIncludeItemTypes && "Book" !== queryIncludeItemTypes && "AudioBook" !== queryIncludeItemTypes && "Playlist" !== queryIncludeItemTypes && "PhotoAlbum" !== queryIncludeItemTypes && "Video" !== query.MediaTypes && "Photo" !== query.MediaTypes || (allowSearch = !1) : "music" === instance.options.collectionType ? query.People ? allowSearch = !1 : "Series" !== queryIncludeItemTypes && "Episode" !== queryIncludeItemTypes && "LiveTvProgram" !== queryIncludeItemTypes && "Movie" !== queryIncludeItemTypes || (allowSearch = !1) : "livetv" === instance.options.collectionType && (query.IncludeArtists || query.IncludePeople ? allowSearch = !1 : "Series" !== queryIncludeItemTypes && "Episode" !== queryIncludeItemTypes && "MusicAlbum" !== queryIncludeItemTypes && "Audio" !== queryIncludeItemTypes && "Book" !== queryIncludeItemTypes && "AudioBook" !== queryIncludeItemTypes && "PhotoAlbum" !== queryIncludeItemTypes && "Movie" !== queryIncludeItemTypes && "Video" !== query.MediaTypes && "Photo" !== query.MediaTypes || (allowSearch = !1)), "NullType" === queryIncludeItemTypes && (allowSearch = !1), !allowSearch) return Promise.resolve({ - SearchHints: [] - }); - if (apiClient.isMinServerVersion("3.4.1.31")) { - query.Fields = "PrimaryImageAspectRatio,CanDelete,BasicSyncInfo,MediaSourceCount", query.Recursive = !0, query.EnableTotalRecordCount = !1, query.ImageTypeLimit = 1; - var methodName = "getItems"; - return query.IncludeMedia || (query.IncludePeople ? methodName = "getPeople" : query.IncludeArtists && (methodName = "getArtists")), apiClient[methodName](apiClient.getCurrentUserId(), query) + + if (!query.searchTerm) { + return Promise.resolve({ + SearchHints: [] + }); } - return query.UserId = apiClient.getCurrentUserId(), apiClient.getSearchHints(query) + + var allowSearch = true; + + var queryIncludeItemTypes = query.IncludeItemTypes; + + if (instance.options.collectionType === 'tvshows') { + if (query.IncludeArtists) { + allowSearch = false; + } + else if (queryIncludeItemTypes === 'Movie' || + queryIncludeItemTypes === 'LiveTvProgram' || + queryIncludeItemTypes === 'MusicAlbum' || + queryIncludeItemTypes === 'Audio' || + queryIncludeItemTypes === 'Book' || + queryIncludeItemTypes === 'AudioBook' || + queryIncludeItemTypes === 'Playlist' || + queryIncludeItemTypes === 'PhotoAlbum' || + query.MediaTypes === 'Video' || + query.MediaTypes === 'Photo') { + allowSearch = false; + } + } + else if (instance.options.collectionType === 'movies') { + if (query.IncludeArtists) { + allowSearch = false; + } + else if (queryIncludeItemTypes === 'Series' || + queryIncludeItemTypes === 'Episode' || + queryIncludeItemTypes === 'LiveTvProgram' || + queryIncludeItemTypes === 'MusicAlbum' || + queryIncludeItemTypes === 'Audio' || + queryIncludeItemTypes === 'Book' || + queryIncludeItemTypes === 'AudioBook' || + queryIncludeItemTypes === 'Playlist' || + queryIncludeItemTypes === 'PhotoAlbum' || + query.MediaTypes === 'Video' || + query.MediaTypes === 'Photo') { + allowSearch = false; + } + } + else if (instance.options.collectionType === 'music') { + if (query.People) { + allowSearch = false; + } + else if (queryIncludeItemTypes === 'Series' || + queryIncludeItemTypes === 'Episode' || + queryIncludeItemTypes === 'LiveTvProgram' || + queryIncludeItemTypes === 'Movie') { + allowSearch = false; + } + } + else if (instance.options.collectionType === 'livetv') { + if (query.IncludeArtists || query.IncludePeople) { + allowSearch = false; + } + else if (queryIncludeItemTypes === 'Series' || + queryIncludeItemTypes === 'Episode' || + queryIncludeItemTypes === 'MusicAlbum' || + queryIncludeItemTypes === 'Audio' || + queryIncludeItemTypes === 'Book' || + queryIncludeItemTypes === 'AudioBook' || + queryIncludeItemTypes === 'PhotoAlbum' || + queryIncludeItemTypes === 'Movie' || + query.MediaTypes === 'Video' || + query.MediaTypes === 'Photo') { + allowSearch = false; + } + } + if (queryIncludeItemTypes === 'NullType') { + allowSearch = false; + } + + if (!allowSearch) { + return Promise.resolve({ + SearchHints: [] + }); + } + + // Convert the search hint query to a regular item query + if (apiClient.isMinServerVersion('3.4.1.31')) { + + query.Fields = 'PrimaryImageAspectRatio,CanDelete,BasicSyncInfo,MediaSourceCount'; + query.Recursive = true; + query.EnableTotalRecordCount = false; + query.ImageTypeLimit = 1; + + var methodName = 'getItems'; + + if (!query.IncludeMedia) { + if (query.IncludePeople) { + methodName = 'getPeople'; + + } else if (query.IncludeArtists) { + methodName = 'getArtists'; + } + } + + return apiClient[methodName](apiClient.getCurrentUserId(), query); + } + + query.UserId = apiClient.getCurrentUserId(); + + return apiClient.getSearchHints(query); } function search(instance, apiClient, context, value) { - value || layoutManager.tv ? (instance.mode = "search", context.querySelector(".searchSuggestions").classList.add("hide")) : (instance.mode = "suggestions", loadSuggestions(instance, context, apiClient)), "livetv" === instance.options.collectionType ? searchType(instance, apiClient, { + + if (value || layoutManager.tv) { + instance.mode = 'search'; + context.querySelector('.searchSuggestions').classList.add('hide'); + } else { + instance.mode = 'suggestions'; + loadSuggestions(instance, context, apiClient); + } + + if (instance.options.collectionType === 'livetv') { + + searchType(instance, apiClient, { + searchTerm: value, + IncludePeople: false, + IncludeMedia: true, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: false, + IncludeItemTypes: "LiveTvProgram", + IsMovie: true, + IsKids: false, + IsNews: false + + }, context, '.movieResults', { + + preferThumb: true, + inheritThumb: false, + shape: (enableScrollX() ? 'overflowPortrait' : 'portrait'), + showParentTitleOrTitle: true, + showTitle: false, + centerText: true, + coverImage: true, + overlayText: false, + overlayMoreButton: true, + showAirTime: true, + showAirDateTime: true, + showChannelName: true + }); + } else { + + searchType(instance, apiClient, { + searchTerm: value, + IncludePeople: false, + IncludeMedia: true, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: false, + IncludeItemTypes: "Movie" + + }, context, '.movieResults', { + + showTitle: true, + overlayText: false, + centerText: true, + showYear: true + }); + } + + searchType(instance, apiClient, { searchTerm: value, - IncludePeople: !1, - IncludeMedia: !0, - IncludeGenres: !1, - IncludeStudios: !1, - IncludeArtists: !1, - IncludeItemTypes: "LiveTvProgram", - IsMovie: !0, - IsKids: !1, - IsNews: !1 - }, context, ".movieResults", { - preferThumb: !0, - inheritThumb: !1, - shape: enableScrollX() ? "overflowPortrait" : "portrait", - showParentTitleOrTitle: !0, - showTitle: !1, - centerText: !0, - coverImage: !0, - overlayText: !1, - overlayMoreButton: !0, - showAirTime: !0, - showAirDateTime: !0, - showChannelName: !0 - }) : searchType(instance, apiClient, { - searchTerm: value, - IncludePeople: !1, - IncludeMedia: !0, - IncludeGenres: !1, - IncludeStudios: !1, - IncludeArtists: !1, - IncludeItemTypes: "Movie" - }, context, ".movieResults", { - showTitle: !0, - overlayText: !1, - centerText: !0, - showYear: !0 - }), searchType(instance, apiClient, { - searchTerm: value, - IncludePeople: !1, - IncludeMedia: !0, - IncludeGenres: !1, - IncludeStudios: !1, - IncludeArtists: !1, + IncludePeople: false, + IncludeMedia: true, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: false, IncludeItemTypes: "Series" - }, context, ".seriesResults", { - showTitle: !0, - overlayText: !1, - centerText: !0, - showYear: !0 - }), "livetv" === instance.options.collectionType ? searchType(instance, apiClient, { + + }, context, '.seriesResults', { + + showTitle: true, + overlayText: false, + centerText: true, + showYear: true + }); + + if (instance.options.collectionType === 'livetv') { + + searchType(instance, apiClient, { + searchTerm: value, + IncludePeople: false, + IncludeMedia: true, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: false, + IncludeItemTypes: "LiveTvProgram", + IsSeries: true, + IsSports: false, + IsKids: false, + IsNews: false + + }, context, '.episodeResults', { + + preferThumb: true, + inheritThumb: false, + shape: (enableScrollX() ? 'overflowBackdrop' : 'backdrop'), + showParentTitleOrTitle: true, + showTitle: false, + centerText: true, + coverImage: true, + overlayText: false, + overlayMoreButton: true, + showAirTime: true, + showAirDateTime: true, + showChannelName: true + }); + + } else { + + searchType(instance, apiClient, { + searchTerm: value, + IncludePeople: false, + IncludeMedia: true, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: false, + IncludeItemTypes: "Episode" + + }, context, '.episodeResults', { + + coverImage: true, + showTitle: true, + showParentTitle: true + }); + } + + searchType(instance, apiClient, { searchTerm: value, - IncludePeople: !1, - IncludeMedia: !0, - IncludeGenres: !1, - IncludeStudios: !1, - IncludeArtists: !1, + IncludePeople: false, + IncludeMedia: true, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: false, + // NullType to hide + IncludeItemTypes: instance.options.collectionType === 'livetv' ? 'LiveTvProgram' : 'NullType', + IsSports: true + + }, context, '.sportsResults', { + + preferThumb: true, + inheritThumb: false, + shape: (enableScrollX() ? 'overflowBackdrop' : 'backdrop'), + showParentTitleOrTitle: true, + showTitle: false, + centerText: true, + coverImage: true, + overlayText: false, + overlayMoreButton: true, + showAirTime: true, + showAirDateTime: true, + showChannelName: true + + }); + + searchType(instance, apiClient, { + searchTerm: value, + IncludePeople: false, + IncludeMedia: true, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: false, + // NullType to hide + IncludeItemTypes: instance.options.collectionType === 'livetv' ? 'LiveTvProgram' : 'NullType', + IsKids: true + + }, context, '.kidsResults', { + + preferThumb: true, + inheritThumb: false, + shape: (enableScrollX() ? 'overflowBackdrop' : 'backdrop'), + showParentTitleOrTitle: true, + showTitle: false, + centerText: true, + coverImage: true, + overlayText: false, + overlayMoreButton: true, + showAirTime: true, + showAirDateTime: true, + showChannelName: true + + }); + + searchType(instance, apiClient, { + searchTerm: value, + IncludePeople: false, + IncludeMedia: true, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: false, + // NullType to hide + IncludeItemTypes: instance.options.collectionType === 'livetv' ? 'LiveTvProgram' : 'NullType', + IsNews: true + + }, context, '.newsResults', { + + preferThumb: true, + inheritThumb: false, + shape: (enableScrollX() ? 'overflowBackdrop' : 'backdrop'), + showParentTitleOrTitle: true, + showTitle: false, + centerText: true, + coverImage: true, + overlayText: false, + overlayMoreButton: true, + showAirTime: true, + showAirDateTime: true, + showChannelName: true + + }); + + searchType(instance, apiClient, { + searchTerm: value, + IncludePeople: false, + IncludeMedia: true, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: false, IncludeItemTypes: "LiveTvProgram", - IsSeries: !0, - IsSports: !1, - IsKids: !1, - IsNews: !1 - }, context, ".episodeResults", { - preferThumb: !0, - inheritThumb: !1, - shape: enableScrollX() ? "overflowBackdrop" : "backdrop", - showParentTitleOrTitle: !0, - showTitle: !1, - centerText: !0, - coverImage: !0, - overlayText: !1, - overlayMoreButton: !0, - showAirTime: !0, - showAirDateTime: !0, - showChannelName: !0 - }) : searchType(instance, apiClient, { + IsMovie: instance.options.collectionType === 'livetv' ? false : null, + IsSeries: instance.options.collectionType === 'livetv' ? false : null, + IsSports: instance.options.collectionType === 'livetv' ? false : null, + IsKids: instance.options.collectionType === 'livetv' ? false : null, + IsNews: instance.options.collectionType === 'livetv' ? false : null + + }, context, '.programResults', { + + preferThumb: true, + inheritThumb: false, + shape: (enableScrollX() ? 'overflowBackdrop' : 'backdrop'), + showParentTitleOrTitle: true, + showTitle: false, + centerText: true, + coverImage: true, + overlayText: false, + overlayMoreButton: true, + showAirTime: true, + showAirDateTime: true, + showChannelName: true + + }); + + searchType(instance, apiClient, { searchTerm: value, - IncludePeople: !1, - IncludeMedia: !0, - IncludeGenres: !1, - IncludeStudios: !1, - IncludeArtists: !1, - IncludeItemTypes: "Episode" - }, context, ".episodeResults", { - coverImage: !0, - showTitle: !0, - showParentTitle: !0 - }), searchType(instance, apiClient, { - searchTerm: value, - IncludePeople: !1, - IncludeMedia: !0, - IncludeGenres: !1, - IncludeStudios: !1, - IncludeArtists: !1, - IncludeItemTypes: "livetv" === instance.options.collectionType ? "LiveTvProgram" : "NullType", - IsSports: !0 - }, context, ".sportsResults", { - preferThumb: !0, - inheritThumb: !1, - shape: enableScrollX() ? "overflowBackdrop" : "backdrop", - showParentTitleOrTitle: !0, - showTitle: !1, - centerText: !0, - coverImage: !0, - overlayText: !1, - overlayMoreButton: !0, - showAirTime: !0, - showAirDateTime: !0, - showChannelName: !0 - }), searchType(instance, apiClient, { - searchTerm: value, - IncludePeople: !1, - IncludeMedia: !0, - IncludeGenres: !1, - IncludeStudios: !1, - IncludeArtists: !1, - IncludeItemTypes: "livetv" === instance.options.collectionType ? "LiveTvProgram" : "NullType", - IsKids: !0 - }, context, ".kidsResults", { - preferThumb: !0, - inheritThumb: !1, - shape: enableScrollX() ? "overflowBackdrop" : "backdrop", - showParentTitleOrTitle: !0, - showTitle: !1, - centerText: !0, - coverImage: !0, - overlayText: !1, - overlayMoreButton: !0, - showAirTime: !0, - showAirDateTime: !0, - showChannelName: !0 - }), searchType(instance, apiClient, { - searchTerm: value, - IncludePeople: !1, - IncludeMedia: !0, - IncludeGenres: !1, - IncludeStudios: !1, - IncludeArtists: !1, - IncludeItemTypes: "livetv" === instance.options.collectionType ? "LiveTvProgram" : "NullType", - IsNews: !0 - }, context, ".newsResults", { - preferThumb: !0, - inheritThumb: !1, - shape: enableScrollX() ? "overflowBackdrop" : "backdrop", - showParentTitleOrTitle: !0, - showTitle: !1, - centerText: !0, - coverImage: !0, - overlayText: !1, - overlayMoreButton: !0, - showAirTime: !0, - showAirDateTime: !0, - showChannelName: !0 - }), searchType(instance, apiClient, { - searchTerm: value, - IncludePeople: !1, - IncludeMedia: !0, - IncludeGenres: !1, - IncludeStudios: !1, - IncludeArtists: !1, - IncludeItemTypes: "LiveTvProgram", - IsMovie: "livetv" !== instance.options.collectionType && null, - IsSeries: "livetv" !== instance.options.collectionType && null, - IsSports: "livetv" !== instance.options.collectionType && null, - IsKids: "livetv" !== instance.options.collectionType && null, - IsNews: "livetv" !== instance.options.collectionType && null - }, context, ".programResults", { - preferThumb: !0, - inheritThumb: !1, - shape: enableScrollX() ? "overflowBackdrop" : "backdrop", - showParentTitleOrTitle: !0, - showTitle: !1, - centerText: !0, - coverImage: !0, - overlayText: !1, - overlayMoreButton: !0, - showAirTime: !0, - showAirDateTime: !0, - showChannelName: !0 - }), searchType(instance, apiClient, { - searchTerm: value, - IncludePeople: !1, - IncludeMedia: !0, - IncludeGenres: !1, - IncludeStudios: !1, - IncludeArtists: !1, + IncludePeople: false, + IncludeMedia: true, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: false, MediaTypes: "Video", ExcludeItemTypes: "Movie,Episode" - }, context, ".videoResults", { - showParentTitle: !0, - showTitle: !0, - overlayText: !1, - centerText: !0 - }), searchType(instance, apiClient, { + + }, context, '.videoResults', { + + showParentTitle: true, + showTitle: true, + overlayText: false, + centerText: true + }); + + searchType(instance, apiClient, { searchTerm: value, - IncludePeople: !0, - IncludeMedia: !1, - IncludeGenres: !1, - IncludeStudios: !1, - IncludeArtists: !1 - }, context, ".peopleResults", { - coverImage: !0, - showTitle: !0 - }), searchType(instance, apiClient, { + IncludePeople: true, + IncludeMedia: false, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: false + + }, context, '.peopleResults', { + + coverImage: true, + showTitle: true + }); + + searchType(instance, apiClient, { searchTerm: value, - IncludePeople: !1, - IncludeMedia: !1, - IncludeGenres: !1, - IncludeStudios: !1, - IncludeArtists: !0 - }, context, ".artistResults", { - coverImage: !0, - showTitle: !0 - }), searchType(instance, apiClient, { + IncludePeople: false, + IncludeMedia: false, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: true + + }, context, '.artistResults', { + coverImage: true, + showTitle: true + }); + + searchType(instance, apiClient, { searchTerm: value, - IncludePeople: !1, - IncludeMedia: !0, - IncludeGenres: !1, - IncludeStudios: !1, - IncludeArtists: !1, + IncludePeople: false, + IncludeMedia: true, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: false, IncludeItemTypes: "MusicAlbum" - }, context, ".albumResults", { - showParentTitle: !0, - showTitle: !0, - overlayText: !1, - centerText: !0 - }), searchType(instance, apiClient, { + + }, context, '.albumResults', { + + showParentTitle: true, + showTitle: true, + overlayText: false, + centerText: true + }); + + searchType(instance, apiClient, { searchTerm: value, - IncludePeople: !1, - IncludeMedia: !0, - IncludeGenres: !1, - IncludeStudios: !1, - IncludeArtists: !1, + IncludePeople: false, + IncludeMedia: true, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: false, IncludeItemTypes: "Audio" - }, context, ".songResults", { - showParentTitle: !0, - showTitle: !0, - overlayText: !1, - centerText: !0, - action: "play" - }), searchType(instance, apiClient, { + + }, context, '.songResults', { + + showParentTitle: true, + showTitle: true, + overlayText: false, + centerText: true, + action: 'play' + + }); + + searchType(instance, apiClient, { searchTerm: value, - IncludePeople: !1, - IncludeMedia: !0, - IncludeGenres: !1, - IncludeStudios: !1, - IncludeArtists: !1, + IncludePeople: false, + IncludeMedia: true, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: false, MediaTypes: "Photo" - }, context, ".photoResults", { - showParentTitle: !1, - showTitle: !0, - overlayText: !1, - centerText: !0 - }), searchType(instance, apiClient, { + + }, context, '.photoResults', { + + showParentTitle: false, + showTitle: true, + overlayText: false, + centerText: true + }); + + searchType(instance, apiClient, { searchTerm: value, - IncludePeople: !1, - IncludeMedia: !0, - IncludeGenres: !1, - IncludeStudios: !1, - IncludeArtists: !1, + IncludePeople: false, + IncludeMedia: true, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: false, IncludeItemTypes: "PhotoAlbum" - }, context, ".photoAlbumResults", { - showTitle: !0, - overlayText: !1, - centerText: !0 - }), searchType(instance, apiClient, { + + }, context, '.photoAlbumResults', { + + showTitle: true, + overlayText: false, + centerText: true + }); + + searchType(instance, apiClient, { searchTerm: value, - IncludePeople: !1, - IncludeMedia: !0, - IncludeGenres: !1, - IncludeStudios: !1, - IncludeArtists: !1, + IncludePeople: false, + IncludeMedia: true, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: false, IncludeItemTypes: "Book" - }, context, ".bookResults", { - showTitle: !0, - overlayText: !1, - centerText: !0 - }), searchType(instance, apiClient, { + + }, context, '.bookResults', { + + showTitle: true, + overlayText: false, + centerText: true + + }); + + searchType(instance, apiClient, { searchTerm: value, - IncludePeople: !1, - IncludeMedia: !0, - IncludeGenres: !1, - IncludeStudios: !1, - IncludeArtists: !1, + IncludePeople: false, + IncludeMedia: true, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: false, IncludeItemTypes: "AudioBook" - }, context, ".audioBookResults", { - showTitle: !0, - overlayText: !1, - centerText: !0 - }), searchType(instance, apiClient, { + + }, context, '.audioBookResults', { + + showTitle: true, + overlayText: false, + centerText: true + }); + + searchType(instance, apiClient, { searchTerm: value, - IncludePeople: !1, - IncludeMedia: !0, - IncludeGenres: !1, - IncludeStudios: !1, - IncludeArtists: !1, + IncludePeople: false, + IncludeMedia: true, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: false, IncludeItemTypes: "Playlist" - }, context, ".playlistResults", { - showTitle: !0, - overlayText: !1, - centerText: !0 - }) + + }, context, '.playlistResults', { + + showTitle: true, + overlayText: false, + centerText: true + }); } function searchType(instance, apiClient, query, context, section, cardOptions) { - query.Limit = enableScrollX() ? 24 : 16, query.ParentId = instance.options.parentId, getSearchHints(instance, apiClient, query).then(function(result) { - populateResults(result, context, section, cardOptions) - }) + + query.Limit = enableScrollX() ? 24 : 16; + query.ParentId = instance.options.parentId; + + getSearchHints(instance, apiClient, query).then(function (result) { + + populateResults(result, context, section, cardOptions); + }); } function populateResults(result, context, section, cardOptions) { + section = context.querySelector(section); - var items = result.Items || result.SearchHints, - itemsContainer = section.querySelector(".itemsContainer"); + + var items = result.Items || result.SearchHints; + + var itemsContainer = section.querySelector('.itemsContainer'); + cardBuilder.buildCards(items, Object.assign({ + itemsContainer: itemsContainer, parentContainer: section, - shape: enableScrollX() ? "autooverflow" : "auto", - scalable: !0, - overlayText: !1, - centerText: !0, + shape: enableScrollX() ? 'autooverflow' : 'auto', + scalable: true, + overlayText: false, + centerText: true, allowBottomPadding: !enableScrollX() - }, cardOptions || {})), section.querySelector(".emby-scroller").scrollToBeginning(!0) + + }, cardOptions || {})); + + section.querySelector('.emby-scroller').scrollToBeginning(true); } function enableScrollX() { - return !0 + return true; } function replaceAll(originalString, strReplace, strWith) { - var reg = new RegExp(strReplace, "ig"); - return originalString.replace(reg, strWith) + var reg = new RegExp(strReplace, 'ig'); + return originalString.replace(reg, strWith); } function embed(elem, instance, options) { - require(["text!./searchresults.template.html"], function(template) { - enableScrollX() || (template = replaceAll(template, 'data-horizontal="true"', 'data-horizontal="false"'), template = replaceAll(template, "itemsContainer scrollSlider", "itemsContainer scrollSlider vertical-wrap")); - var html = globalize.translateDocument(template, "sharedcomponents"); - elem.innerHTML = html, elem.classList.add("searchResults"), instance.search("") - }) + + require(['text!./searchresults.template.html'], function (template) { + + if (!enableScrollX()) { + template = replaceAll(template, 'data-horizontal="true"', 'data-horizontal="false"'); + template = replaceAll(template, 'itemsContainer scrollSlider', 'itemsContainer scrollSlider vertical-wrap'); + } + + var html = globalize.translateDocument(template, 'sharedcomponents'); + + elem.innerHTML = html; + + elem.classList.add('searchResults'); + instance.search(''); + }); } function SearchResults(options) { - this.options = options, embed(options.element, this, options) + + this.options = options; + embed(options.element, this, options); } - return SearchResults.prototype.search = function(value) { - search(this, connectionManager.getApiClient(this.options.serverId), this.options.element, value) - }, SearchResults.prototype.destroy = function() { + + SearchResults.prototype.search = function (value) { + + var apiClient = connectionManager.getApiClient(this.options.serverId); + + search(this, apiClient, this.options.element, value); + }; + + SearchResults.prototype.destroy = function () { + var options = this.options; - options && options.element.classList.remove("searchFields"), this.options = null - }, SearchResults + if (options) { + options.element.classList.remove('searchFields'); + } + this.options = null; + + }; + + return SearchResults; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/serverrestartdialog/serverrestartdialog.js b/src/bower_components/emby-webcomponents/serverrestartdialog/serverrestartdialog.js index a86fa3ef16..70747ee804 100644 --- a/src/bower_components/emby-webcomponents/serverrestartdialog/serverrestartdialog.js +++ b/src/bower_components/emby-webcomponents/serverrestartdialog/serverrestartdialog.js @@ -1,70 +1,173 @@ -define(["loading", "events", "dialogHelper", "dom", "layoutManager", "scrollHelper", "globalize", "require", "material-icons", "emby-button", "paper-icon-button-light", "emby-input", "formDialogStyle", "flexStyles"], function(loading, events, dialogHelper, dom, layoutManager, scrollHelper, globalize, require) { - "use strict"; +define(['loading', 'events', 'dialogHelper', 'dom', 'layoutManager', 'scrollHelper', 'globalize', 'require', 'material-icons', 'emby-button', 'paper-icon-button-light', 'emby-input', 'formDialogStyle', 'flexStyles'], function (loading, events, dialogHelper, dom, layoutManager, scrollHelper, globalize, require) { + 'use strict'; + + var currentApiClient; + var currentDlg; + var currentInstance; function reloadPageWhenServerAvailable(retryCount) { + var apiClient = currentApiClient; - apiClient && apiClient.getJSON(apiClient.getUrl("System/Info")).then(function(info) { - info.IsShuttingDown ? retryReload(retryCount) : (currentInstance.restarted = !0, dialogHelper.close(currentDlg)) - }, function() { - retryReload(retryCount) - }) + + if (!apiClient) { + return; + } + + // Don't use apiclient method because we don't want it reporting authentication under the old version + apiClient.getJSON(apiClient.getUrl("System/Info")).then(function (info) { + + // If this is back to false, the restart completed + if (!info.IsShuttingDown) { + currentInstance.restarted = true; + dialogHelper.close(currentDlg); + } else { + retryReload(retryCount); + } + + }, function () { + retryReload(retryCount); + }); } function retryReload(retryCount) { - setTimeout(function() { - retryCount = retryCount || 0, ++retryCount < 150 && reloadPageWhenServerAvailable(retryCount) - }, 500) + setTimeout(function () { + + retryCount = retryCount || 0; + retryCount++; + + if (retryCount < 150) { + reloadPageWhenServerAvailable(retryCount); + } + }, 500); } function startRestart(instance, apiClient, dlg) { - currentApiClient = apiClient, currentDlg = dlg, currentInstance = instance, apiClient.restartServer().then(function() { - setTimeout(reloadPageWhenServerAvailable, 250) - }) + + currentApiClient = apiClient; + currentDlg = dlg; + currentInstance = instance; + + apiClient.restartServer().then(function () { + + setTimeout(reloadPageWhenServerAvailable, 250); + + }); } function showDialog(instance, options, template) { - function onButtonClick() { - dialogHelper.close(dlg) - } + var dialogOptions = { - removeOnClose: !0, - scrollY: !1 - }, - enableTvLayout = layoutManager.tv; - enableTvLayout && (dialogOptions.size = "fullscreen"); - var dlg = dialogHelper.createDialog(dialogOptions), - configuredButtons = []; - dlg.classList.add("formDialog"), dlg.innerHTML = globalize.translateHtml(template, "sharedcomponents"), dlg.classList.add("align-items-center"), dlg.classList.add("justify-items-center"); - var formDialogContent = dlg.querySelector(".formDialogContent"); - formDialogContent.style["flex-grow"] = "initial", enableTvLayout ? (formDialogContent.style["max-width"] = "50%", formDialogContent.style["max-height"] = "60%", scrollHelper.centerFocus.on(formDialogContent, !1)) : (formDialogContent.style.maxWidth = Math.min(150 * configuredButtons.length + 200, dom.getWindowSize().innerWidth - 50) + "px", dlg.classList.add("dialog-fullscreen-lowres")), dlg.querySelector(".formDialogHeaderTitle").innerHTML = globalize.translate("sharedcomponents#HeaderRestartingEmbyServer"), dlg.querySelector(".text").innerHTML = globalize.translate("sharedcomponents#RestartPleaseWaitMessage"); - var i, length, html = ""; - for (i = 0, length = configuredButtons.length; i < length; i++) { - var item = configuredButtons[i], - autoFocus = 0 === i ? " autofocus" : "", - buttonClass = "btnOption raised formDialogFooterItem formDialogFooterItem-autosize"; - item.type && (buttonClass += " button-" + item.type), html += '" + removeOnClose: true, + scrollY: false + }; + + var enableTvLayout = layoutManager.tv; + + if (enableTvLayout) { + dialogOptions.size = 'fullscreen'; } - dlg.querySelector(".formDialogFooter").innerHTML = html; - var buttons = dlg.querySelectorAll(".btnOption"); - for (i = 0, length = buttons.length; i < length; i++) buttons[i].addEventListener("click", onButtonClick); + + var dlg = dialogHelper.createDialog(dialogOptions); + + var configuredButtons = []; + + dlg.classList.add('formDialog'); + + dlg.innerHTML = globalize.translateHtml(template, 'sharedcomponents'); + + dlg.classList.add('align-items-center'); + dlg.classList.add('justify-items-center'); + + var formDialogContent = dlg.querySelector('.formDialogContent'); + formDialogContent.style['flex-grow'] = 'initial'; + + if (enableTvLayout) { + formDialogContent.style['max-width'] = '50%'; + formDialogContent.style['max-height'] = '60%'; + scrollHelper.centerFocus.on(formDialogContent, false); + } else { + formDialogContent.style.maxWidth = (Math.min((configuredButtons.length * 150) + 200, dom.getWindowSize().innerWidth - 50)) + 'px'; + dlg.classList.add('dialog-fullscreen-lowres'); + } + + //dlg.querySelector('.btnCancel').addEventListener('click', function (e) { + // dialogHelper.close(dlg); + //}); + + dlg.querySelector('.formDialogHeaderTitle').innerHTML = globalize.translate('sharedcomponents#HeaderRestartingEmbyServer'); + + dlg.querySelector('.text').innerHTML = globalize.translate('sharedcomponents#RestartPleaseWaitMessage'); + + var i, length; + var html = ''; + for (i = 0, length = configuredButtons.length; i < length; i++) { + + var item = configuredButtons[i]; + var autoFocus = i === 0 ? ' autofocus' : ''; + + var buttonClass = 'btnOption raised formDialogFooterItem formDialogFooterItem-autosize'; + + if (item.type) { + buttonClass += ' button-' + item.type; + } + + html += ''; + } + + dlg.querySelector('.formDialogFooter').innerHTML = html; + + function onButtonClick() { + dialogHelper.close(dlg); + } + + var buttons = dlg.querySelectorAll('.btnOption'); + for (i = 0, length = buttons.length; i < length; i++) { + buttons[i].addEventListener('click', onButtonClick); + } + var dlgPromise = dialogHelper.open(dlg); - return startRestart(instance, options.apiClient, dlg), dlgPromise.then(function() { - enableTvLayout && scrollHelper.centerFocus.off(dlg.querySelector(".formDialogContent"), !1), instance.destroy(), loading.hide(), instance.restarted && events.trigger(instance, "restarted") - }) + + startRestart(instance, options.apiClient, dlg); + + return dlgPromise.then(function () { + + if (enableTvLayout) { + scrollHelper.centerFocus.off(dlg.querySelector('.formDialogContent'), false); + } + + instance.destroy(); + loading.hide(); + + if (instance.restarted) { + events.trigger(instance, 'restarted'); + } + }); } function ServerRestartDialog(options) { - this.options = options + + this.options = options; } - var currentApiClient, currentDlg, currentInstance; - return ServerRestartDialog.prototype.show = function() { + + ServerRestartDialog.prototype.show = function () { + var instance = this; - return loading.show(), new Promise(function(resolve, reject) { - require(["text!./../dialog/dialog.template.html"], function(template) { - showDialog(instance, instance.options, template).then(resolve, reject) - }) - }) - }, ServerRestartDialog.prototype.destroy = function() { - currentApiClient = null, currentDlg = null, currentInstance = null, this.options = null - }, ServerRestartDialog + loading.show(); + + return new Promise(function (resolve, reject) { + require(['text!./../dialog/dialog.template.html'], function (template) { + showDialog(instance, instance.options, template).then(resolve, reject); + }); + }); + }; + + ServerRestartDialog.prototype.destroy = function () { + + currentApiClient = null; + currentDlg = null; + currentInstance = null; + this.options = null; + }; + + return ServerRestartDialog; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/serviceworker/notifications.js b/src/bower_components/emby-webcomponents/serviceworker/notifications.js index e99b2f44c1..c566715bb7 100644 --- a/src/bower_components/emby-webcomponents/serviceworker/notifications.js +++ b/src/bower_components/emby-webcomponents/serviceworker/notifications.js @@ -1,31 +1,52 @@ -! function() { - "use strict"; +(function () { + 'use strict'; + + var connectionManager; function getApiClient(serverId) { - return connectionManager ? Promise.resolve(connectionManager.getApiClient(serverId)) : Promise.reject() + + if (connectionManager) { + return Promise.resolve(connectionManager.getApiClient(serverId)); + } + + //importScripts('serviceworker-cache-polyfill.js'); + + return Promise.reject(); } function executeAction(action, data, serverId) { - return getApiClient(serverId).then(function(apiClient) { + + return getApiClient(serverId).then(function (apiClient) { + switch (action) { - case "cancel-install": + case 'cancel-install': var id = data.id; return apiClient.cancelPackageInstallation(id); - case "restart": + case 'restart': return apiClient.restartServer(); default: - return clients.openWindow("/"), Promise.resolve() + clients.openWindow("/"); + return Promise.resolve(); } - }) + }); } - var connectionManager; - self.addEventListener("notificationclick", function(event) { + + self.addEventListener('notificationclick', function (event) { + var notification = event.notification; notification.close(); - var data = notification.data, - serverId = data.serverId, - action = event.action; - if (!action) return clients.openWindow("/"), void event.waitUntil(Promise.resolve()); - event.waitUntil(executeAction(action, data, serverId)) - }, !1) -}(); \ No newline at end of file + + var data = notification.data; + var serverId = data.serverId; + var action = event.action; + + if (!action) { + clients.openWindow("/"); + event.waitUntil(Promise.resolve()); + return; + } + + event.waitUntil(executeAction(action, data, serverId)); + + }, false); +})(); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/serviceworker/sync.js b/src/bower_components/emby-webcomponents/serviceworker/sync.js index 5fdaac113d..3dcff8f363 100644 --- a/src/bower_components/emby-webcomponents/serviceworker/sync.js +++ b/src/bower_components/emby-webcomponents/serviceworker/sync.js @@ -1,4 +1,6 @@ -self.addEventListener("sync", function(event) { - "use strict"; - event.tag +self.addEventListener('sync', function (event) { + 'use strict'; + + if (event.tag === 'emby-sync') { + } }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/sessionplayer.js b/src/bower_components/emby-webcomponents/sessionplayer.js index f7c45f561a..0b6aa0e54d 100644 --- a/src/bower_components/emby-webcomponents/sessionplayer.js +++ b/src/bower_components/emby-webcomponents/sessionplayer.js @@ -1,108 +1,259 @@ -define(["playbackManager", "events", "serverNotifications", "connectionManager"], function(playbackManager, events, serverNotifications, connectionManager) { - "use strict"; +define(['playbackManager', 'events', 'serverNotifications', 'connectionManager'], function (playbackManager, events, serverNotifications, connectionManager) { + 'use strict'; function getActivePlayerId() { var info = playbackManager.getPlayerInfo(); - return info ? info.id : null + return info ? info.id : null; } function sendPlayCommand(apiClient, options, playType) { - var sessionId = getActivePlayerId(), - ids = options.ids || options.items.map(function(i) { - return i.Id - }), - remoteOptions = { - ItemIds: ids.join(","), - PlayCommand: playType - }; - return options.startPositionTicks && (remoteOptions.StartPositionTicks = options.startPositionTicks), options.mediaSourceId && (remoteOptions.MediaSourceId = options.mediaSourceId), null != options.audioStreamIndex && (remoteOptions.AudioStreamIndex = options.audioStreamIndex), null != options.subtitleStreamIndex && (remoteOptions.SubtitleStreamIndex = options.subtitleStreamIndex), null != options.startIndex && (remoteOptions.StartIndex = options.startIndex), apiClient.sendPlayCommand(sessionId, remoteOptions) + + var sessionId = getActivePlayerId(); + + var ids = options.ids || options.items.map(function (i) { + return i.Id; + }); + + var remoteOptions = { + ItemIds: ids.join(','), + + PlayCommand: playType + }; + + if (options.startPositionTicks) { + remoteOptions.StartPositionTicks = options.startPositionTicks; + } + + if (options.mediaSourceId) { + remoteOptions.MediaSourceId = options.mediaSourceId; + } + + if (options.audioStreamIndex != null) { + remoteOptions.AudioStreamIndex = options.audioStreamIndex; + } + + if (options.subtitleStreamIndex != null) { + remoteOptions.SubtitleStreamIndex = options.subtitleStreamIndex; + } + + if (options.startIndex != null) { + remoteOptions.StartIndex = options.startIndex; + } + + return apiClient.sendPlayCommand(sessionId, remoteOptions); } function sendPlayStateCommand(apiClient, command, options) { + var sessionId = getActivePlayerId(); - apiClient.sendPlayStateCommand(sessionId, command, options) + + apiClient.sendPlayStateCommand(sessionId, command, options); } function getCurrentApiClient(instance) { + var currentServerId = instance.currentServerId; - return currentServerId ? connectionManager.getApiClient(currentServerId) : connectionManager.currentApiClient() + + if (currentServerId) { + return connectionManager.getApiClient(currentServerId); + } + + return connectionManager.currentApiClient(); } function sendCommandByName(instance, name, options) { + var command = { Name: name }; - options && (command.Arguments = options), instance.sendCommand(command) + + if (options) { + command.Arguments = options; + } + + instance.sendCommand(command); } function unsubscribeFromPlayerUpdates(instance) { - instance.isUpdating = !0, getCurrentApiClient(instance).sendMessage("SessionsStop"), instance.pollInterval && (clearInterval(instance.pollInterval), instance.pollInterval = null) + + instance.isUpdating = true; + + var apiClient = getCurrentApiClient(instance); + apiClient.sendMessage("SessionsStop"); + if (instance.pollInterval) { + clearInterval(instance.pollInterval); + instance.pollInterval = null; + } } function processUpdatedSessions(instance, sessions, apiClient) { + var serverId = apiClient.serverId(); - sessions.map(function(s) { - s.NowPlayingItem && (s.NowPlayingItem.ServerId = serverId) + + sessions.map(function (s) { + if (s.NowPlayingItem) { + s.NowPlayingItem.ServerId = serverId; + } }); - var currentTargetId = getActivePlayerId(), - session = sessions.filter(function(s) { - return s.Id === currentTargetId - })[0]; + + var currentTargetId = getActivePlayerId(); + + var session = sessions.filter(function (s) { + return s.Id === currentTargetId; + })[0]; + if (session) { + normalizeImages(session, apiClient); + var eventNames = getChangedEvents(instance.lastPlayerData, session); instance.lastPlayerData = session; - for (var i = 0, length = eventNames.length; i < length; i++) events.trigger(instance, eventNames[i], [session]) - } else instance.lastPlayerData = session, playbackManager.setDefaultPlayerActive() + + for (var i = 0, length = eventNames.length; i < length; i++) { + events.trigger(instance, eventNames[i], [session]); + } + + } else { + + instance.lastPlayerData = session; + + playbackManager.setDefaultPlayerActive(); + } } function getChangedEvents(state1, state2) { + var names = []; - return names.push("statechange"), names.push("timeupdate"), names.push("pause"), names + + if (!state1) { + names.push('statechange'); + names.push('timeupdate'); + names.push('pause'); + + return names; + } + + // TODO: Trim these down to prevent the UI from over-refreshing + names.push('statechange'); + names.push('timeupdate'); + names.push('pause'); + + return names; } function onPollIntervalFired() { - var instance = this, - apiClient = getCurrentApiClient(instance); - apiClient.isMessageChannelOpen() || apiClient.getSessions().then(function(sessions) { - processUpdatedSessions(instance, sessions, apiClient) - }) + + var instance = this; + var apiClient = getCurrentApiClient(instance); + if (!apiClient.isMessageChannelOpen()) { + + apiClient.getSessions().then(function (sessions) { + processUpdatedSessions(instance, sessions, apiClient); + }); + } } function subscribeToPlayerUpdates(instance) { - instance.isUpdating = !0, getCurrentApiClient(instance).sendMessage("SessionsStart", "100,800"), instance.pollInterval && (clearInterval(instance.pollInterval), instance.pollInterval = null), instance.pollInterval = setInterval(onPollIntervalFired.bind(instance), 5e3) + + instance.isUpdating = true; + + var apiClient = getCurrentApiClient(instance); + apiClient.sendMessage("SessionsStart", "100,800"); + if (instance.pollInterval) { + clearInterval(instance.pollInterval); + instance.pollInterval = null; + } + instance.pollInterval = setInterval(onPollIntervalFired.bind(instance), 5000); } function normalizeImages(state, apiClient) { + if (state && state.NowPlayingItem) { + var item = state.NowPlayingItem; - item.ImageTags && item.ImageTags.Primary || item.PrimaryImageTag && (item.ImageTags = item.ImageTags || {}, item.ImageTags.Primary = item.PrimaryImageTag), item.BackdropImageTag && item.BackdropItemId === item.Id && (item.BackdropImageTags = [item.BackdropImageTag]), item.BackdropImageTag && item.BackdropItemId !== item.Id && (item.ParentBackdropImageTags = [item.BackdropImageTag], item.ParentBackdropItemId = item.BackdropItemId), item.ServerId || (item.ServerId = apiClient.serverId()) + + if (!item.ImageTags || !item.ImageTags.Primary) { + if (item.PrimaryImageTag) { + item.ImageTags = item.ImageTags || {}; + item.ImageTags.Primary = item.PrimaryImageTag; + } + } + if (item.BackdropImageTag && item.BackdropItemId === item.Id) { + item.BackdropImageTags = [item.BackdropImageTag]; + } + if (item.BackdropImageTag && item.BackdropItemId !== item.Id) { + item.ParentBackdropImageTags = [item.BackdropImageTag]; + item.ParentBackdropItemId = item.BackdropItemId; + } + if (!item.ServerId) { + item.ServerId = apiClient.serverId(); + } } } function SessionPlayer() { + var self = this; - this.name = "Remote Control", this.type = "mediaplayer", this.isLocalPlayer = !1, this.id = "remoteplayer", events.on(serverNotifications, "Sessions", function(e, apiClient, data) { - processUpdatedSessions(self, data, apiClient) - }) + + this.name = 'Remote Control'; + this.type = 'mediaplayer'; + this.isLocalPlayer = false; + this.id = 'remoteplayer'; + + events.on(serverNotifications, 'Sessions', function (e, apiClient, data) { + processUpdatedSessions(self, data, apiClient); + }); } - return SessionPlayer.prototype.beginPlayerUpdates = function() { - this.playerListenerCount = this.playerListenerCount || 0, this.playerListenerCount <= 0 && (this.playerListenerCount = 0, subscribeToPlayerUpdates(this)), this.playerListenerCount++ - }, SessionPlayer.prototype.endPlayerUpdates = function() { - this.playerListenerCount = this.playerListenerCount || 0, --this.playerListenerCount <= 0 && (unsubscribeFromPlayerUpdates(this), this.playerListenerCount = 0) - }, SessionPlayer.prototype.getPlayerState = function() { - return this.lastPlayerData || {} - }, SessionPlayer.prototype.getTargets = function() { - var apiClient = getCurrentApiClient(this), - sessionQuery = { - ControllableByUserId: apiClient.getCurrentUserId() - }; + + SessionPlayer.prototype.beginPlayerUpdates = function () { + + this.playerListenerCount = this.playerListenerCount || 0; + + if (this.playerListenerCount <= 0) { + + this.playerListenerCount = 0; + + subscribeToPlayerUpdates(this); + } + + this.playerListenerCount++; + }; + + SessionPlayer.prototype.endPlayerUpdates = function () { + + this.playerListenerCount = this.playerListenerCount || 0; + this.playerListenerCount--; + + if (this.playerListenerCount <= 0) { + + unsubscribeFromPlayerUpdates(this); + this.playerListenerCount = 0; + } + }; + + SessionPlayer.prototype.getPlayerState = function () { + + return this.lastPlayerData || {}; + }; + + SessionPlayer.prototype.getTargets = function () { + + var apiClient = getCurrentApiClient(this); + + var sessionQuery = { + ControllableByUserId: apiClient.getCurrentUserId() + }; + if (apiClient) { + var name = this.name; - return apiClient.getSessions(sessionQuery).then(function(sessions) { - return sessions.filter(function(s) { - return s.DeviceId !== apiClient.deviceId() - }).map(function(s) { + + return apiClient.getSessions(sessionQuery).then(function (sessions) { + + return sessions.filter(function (s) { + return s.DeviceId !== apiClient.deviceId(); + + }).map(function (s) { return { name: s.DeviceName, deviceName: s.DeviceName, @@ -111,136 +262,291 @@ define(["playbackManager", "events", "serverNotifications", "connectionManager"] playerName: name, appName: s.Client, playableMediaTypes: s.PlayableMediaTypes, - isLocalPlayer: !1, + isLocalPlayer: false, supportedCommands: s.SupportedCommands, user: s.UserId ? { + Id: s.UserId, Name: s.UserName, PrimaryImageTag: s.UserPrimaryImageTag + } : null - } - }) - }) + }; + }); + + }); + + } else { + return Promise.resolve([]); } - return Promise.resolve([]) - }, SessionPlayer.prototype.sendCommand = function(command) { + }; + + SessionPlayer.prototype.sendCommand = function (command) { + var sessionId = getActivePlayerId(); - getCurrentApiClient(this).sendCommand(sessionId, command) - }, SessionPlayer.prototype.play = function(options) { - return options = Object.assign({}, options), options.items && (options.ids = options.items.map(function(i) { - return i.Id - }), options.items = null), sendPlayCommand(getCurrentApiClient(this), options, "PlayNow") - }, SessionPlayer.prototype.shuffle = function(item) { - sendPlayCommand(getCurrentApiClient(this), { - ids: [item.Id] - }, "PlayShuffle") - }, SessionPlayer.prototype.instantMix = function(item) { - sendPlayCommand(getCurrentApiClient(this), { - ids: [item.Id] - }, "PlayInstantMix") - }, SessionPlayer.prototype.queue = function(options) { - sendPlayCommand(getCurrentApiClient(this), options, "PlayNext") - }, SessionPlayer.prototype.queueNext = function(options) { - sendPlayCommand(getCurrentApiClient(this), options, "PlayLast") - }, SessionPlayer.prototype.canPlayMediaType = function(mediaType) { - return "audio" === (mediaType = (mediaType || "").toLowerCase()) || "video" === mediaType - }, SessionPlayer.prototype.canQueueMediaType = function(mediaType) { - return this.canPlayMediaType(mediaType) - }, SessionPlayer.prototype.stop = function() { - sendPlayStateCommand(getCurrentApiClient(this), "stop") - }, SessionPlayer.prototype.nextTrack = function() { - sendPlayStateCommand(getCurrentApiClient(this), "nextTrack") - }, SessionPlayer.prototype.previousTrack = function() { - sendPlayStateCommand(getCurrentApiClient(this), "previousTrack") - }, SessionPlayer.prototype.seek = function(positionTicks) { - sendPlayStateCommand(getCurrentApiClient(this), "seek", { - SeekPositionTicks: positionTicks - }) - }, SessionPlayer.prototype.currentTime = function(val) { - if (null != val) return this.seek(val); + + var apiClient = getCurrentApiClient(this); + apiClient.sendCommand(sessionId, command); + }; + + SessionPlayer.prototype.play = function (options) { + + options = Object.assign({}, options); + + if (options.items) { + options.ids = options.items.map(function (i) { + return i.Id; + }); + + options.items = null; + } + + return sendPlayCommand(getCurrentApiClient(this), options, 'PlayNow'); + }; + + SessionPlayer.prototype.shuffle = function (item) { + + sendPlayCommand(getCurrentApiClient(this), { ids: [item.Id] }, 'PlayShuffle'); + }; + + SessionPlayer.prototype.instantMix = function (item) { + + sendPlayCommand(getCurrentApiClient(this), { ids: [item.Id] }, 'PlayInstantMix'); + }; + + SessionPlayer.prototype.queue = function (options) { + + sendPlayCommand(getCurrentApiClient(this), options, 'PlayNext'); + }; + + SessionPlayer.prototype.queueNext = function (options) { + + sendPlayCommand(getCurrentApiClient(this), options, 'PlayLast'); + }; + + SessionPlayer.prototype.canPlayMediaType = function (mediaType) { + + mediaType = (mediaType || '').toLowerCase(); + return mediaType === 'audio' || mediaType === 'video'; + }; + + SessionPlayer.prototype.canQueueMediaType = function (mediaType) { + return this.canPlayMediaType(mediaType); + }; + + SessionPlayer.prototype.stop = function () { + sendPlayStateCommand(getCurrentApiClient(this), 'stop'); + }; + + SessionPlayer.prototype.nextTrack = function () { + sendPlayStateCommand(getCurrentApiClient(this), 'nextTrack'); + }; + + SessionPlayer.prototype.previousTrack = function () { + sendPlayStateCommand(getCurrentApiClient(this), 'previousTrack'); + }; + + SessionPlayer.prototype.seek = function (positionTicks) { + sendPlayStateCommand(getCurrentApiClient(this), 'seek', + { + SeekPositionTicks: positionTicks + }); + }; + + SessionPlayer.prototype.currentTime = function (val) { + + if (val != null) { + return this.seek(val); + } + var state = this.lastPlayerData || {}; - return state = state.PlayState || {}, state.PositionTicks - }, SessionPlayer.prototype.duration = function() { + state = state.PlayState || {}; + return state.PositionTicks; + }; + + SessionPlayer.prototype.duration = function () { var state = this.lastPlayerData || {}; - return state = state.NowPlayingItem || {}, state.RunTimeTicks - }, SessionPlayer.prototype.paused = function() { + state = state.NowPlayingItem || {}; + return state.RunTimeTicks; + }; + + SessionPlayer.prototype.paused = function () { var state = this.lastPlayerData || {}; - return state = state.PlayState || {}, state.IsPaused - }, SessionPlayer.prototype.getVolume = function() { + state = state.PlayState || {}; + return state.IsPaused; + }; + + SessionPlayer.prototype.getVolume = function () { var state = this.lastPlayerData || {}; - return state = state.PlayState || {}, state.VolumeLevel - }, SessionPlayer.prototype.isMuted = function() { + state = state.PlayState || {}; + return state.VolumeLevel; + }; + + SessionPlayer.prototype.isMuted = function () { var state = this.lastPlayerData || {}; - return state = state.PlayState || {}, state.IsMuted - }, SessionPlayer.prototype.pause = function() { - sendPlayStateCommand(getCurrentApiClient(this), "Pause") - }, SessionPlayer.prototype.unpause = function() { - sendPlayStateCommand(getCurrentApiClient(this), "Unpause") - }, SessionPlayer.prototype.playPause = function() { - sendPlayStateCommand(getCurrentApiClient(this), "PlayPause") - }, SessionPlayer.prototype.setMute = function(isMuted) { - isMuted ? sendCommandByName(this, "Mute") : sendCommandByName(this, "Unmute") - }, SessionPlayer.prototype.toggleMute = function() { - sendCommandByName(this, "ToggleMute") - }, SessionPlayer.prototype.setVolume = function(vol) { - sendCommandByName(this, "SetVolume", { + state = state.PlayState || {}; + return state.IsMuted; + }; + + SessionPlayer.prototype.pause = function () { + sendPlayStateCommand(getCurrentApiClient(this), 'Pause'); + }; + + SessionPlayer.prototype.unpause = function () { + sendPlayStateCommand(getCurrentApiClient(this), 'Unpause'); + }; + + SessionPlayer.prototype.playPause = function () { + sendPlayStateCommand(getCurrentApiClient(this), 'PlayPause'); + }; + + SessionPlayer.prototype.setMute = function (isMuted) { + + if (isMuted) { + sendCommandByName(this, 'Mute'); + } else { + sendCommandByName(this, 'Unmute'); + } + }; + + SessionPlayer.prototype.toggleMute = function () { + sendCommandByName(this, 'ToggleMute'); + }; + + SessionPlayer.prototype.setVolume = function (vol) { + sendCommandByName(this, 'SetVolume', { Volume: vol - }) - }, SessionPlayer.prototype.volumeUp = function() { - sendCommandByName(this, "VolumeUp") - }, SessionPlayer.prototype.volumeDown = function() { - sendCommandByName(this, "VolumeDown") - }, SessionPlayer.prototype.toggleFullscreen = function() { - sendCommandByName(this, "ToggleFullscreen") - }, SessionPlayer.prototype.audioTracks = function() { + }); + }; + + SessionPlayer.prototype.volumeUp = function () { + sendCommandByName(this, 'VolumeUp'); + }; + + SessionPlayer.prototype.volumeDown = function () { + sendCommandByName(this, 'VolumeDown'); + }; + + SessionPlayer.prototype.toggleFullscreen = function () { + sendCommandByName(this, 'ToggleFullscreen'); + }; + + SessionPlayer.prototype.audioTracks = function () { var state = this.lastPlayerData || {}; - return state = state.NowPlayingItem || {}, (state.MediaStreams || []).filter(function(s) { - return "Audio" === s.Type - }) - }, SessionPlayer.prototype.getAudioStreamIndex = function() { + state = state.NowPlayingItem || {}; + var streams = state.MediaStreams || []; + return streams.filter(function (s) { + return s.Type === 'Audio'; + }); + }; + + SessionPlayer.prototype.getAudioStreamIndex = function () { var state = this.lastPlayerData || {}; - return state = state.PlayState || {}, state.AudioStreamIndex - }, SessionPlayer.prototype.playTrailers = function(item) { - sendCommandByName(this, "PlayTrailers", { + state = state.PlayState || {}; + return state.AudioStreamIndex; + }; + + SessionPlayer.prototype.playTrailers = function (item) { + sendCommandByName(this, 'PlayTrailers', { ItemId: item.Id - }) - }, SessionPlayer.prototype.setAudioStreamIndex = function(index) { - sendCommandByName(this, "SetAudioStreamIndex", { + }); + }; + + SessionPlayer.prototype.setAudioStreamIndex = function (index) { + sendCommandByName(this, 'SetAudioStreamIndex', { Index: index - }) - }, SessionPlayer.prototype.subtitleTracks = function() { + }); + }; + + SessionPlayer.prototype.subtitleTracks = function () { var state = this.lastPlayerData || {}; - return state = state.NowPlayingItem || {}, (state.MediaStreams || []).filter(function(s) { - return "Subtitle" === s.Type - }) - }, SessionPlayer.prototype.getSubtitleStreamIndex = function() { + state = state.NowPlayingItem || {}; + var streams = state.MediaStreams || []; + return streams.filter(function (s) { + return s.Type === 'Subtitle'; + }); + }; + + SessionPlayer.prototype.getSubtitleStreamIndex = function () { var state = this.lastPlayerData || {}; - return state = state.PlayState || {}, state.SubtitleStreamIndex - }, SessionPlayer.prototype.setSubtitleStreamIndex = function(index) { - sendCommandByName(this, "SetSubtitleStreamIndex", { + state = state.PlayState || {}; + return state.SubtitleStreamIndex; + }; + + SessionPlayer.prototype.setSubtitleStreamIndex = function (index) { + sendCommandByName(this, 'SetSubtitleStreamIndex', { Index: index - }) - }, SessionPlayer.prototype.getMaxStreamingBitrate = function() {}, SessionPlayer.prototype.setMaxStreamingBitrate = function(options) {}, SessionPlayer.prototype.isFullscreen = function() {}, SessionPlayer.prototype.toggleFullscreen = function() {}, SessionPlayer.prototype.getRepeatMode = function() {}, SessionPlayer.prototype.setRepeatMode = function(mode) { - sendCommandByName(this, "SetRepeatMode", { + }); + }; + + SessionPlayer.prototype.getMaxStreamingBitrate = function () { + + }; + + SessionPlayer.prototype.setMaxStreamingBitrate = function (options) { + + }; + + SessionPlayer.prototype.isFullscreen = function () { + + }; + + SessionPlayer.prototype.toggleFullscreen = function () { + + }; + + SessionPlayer.prototype.getRepeatMode = function () { + + }; + + SessionPlayer.prototype.setRepeatMode = function (mode) { + + sendCommandByName(this, 'SetRepeatMode', { RepeatMode: mode - }) - }, SessionPlayer.prototype.displayContent = function(options) { - sendCommandByName(this, "DisplayContent", options) - }, SessionPlayer.prototype.isPlaying = function() { - return null != (this.lastPlayerData || {}).NowPlayingItem - }, SessionPlayer.prototype.isPlayingVideo = function() { + }); + }; + + SessionPlayer.prototype.displayContent = function (options) { + + sendCommandByName(this, 'DisplayContent', options); + }; + + SessionPlayer.prototype.isPlaying = function () { var state = this.lastPlayerData || {}; - return state = state.NowPlayingItem || {}, "Video" === state.MediaType - }, SessionPlayer.prototype.isPlayingAudio = function() { + return state.NowPlayingItem != null; + }; + + SessionPlayer.prototype.isPlayingVideo = function () { var state = this.lastPlayerData || {}; - return state = state.NowPlayingItem || {}, "Audio" === state.MediaType - }, SessionPlayer.prototype.getPlaylist = function() { - return Promise.resolve([]) - }, SessionPlayer.prototype.getCurrentPlaylistItemId = function() {}, SessionPlayer.prototype.setCurrentPlaylistItem = function(playlistItemId) { - return Promise.resolve() - }, SessionPlayer.prototype.removeFromPlaylist = function(playlistItemIds) { - return Promise.resolve() - }, SessionPlayer.prototype.tryPair = function(target) { - return Promise.resolve() - }, SessionPlayer + state = state.NowPlayingItem || {}; + return state.MediaType === 'Video'; + }; + + SessionPlayer.prototype.isPlayingAudio = function () { + var state = this.lastPlayerData || {}; + state = state.NowPlayingItem || {}; + return state.MediaType === 'Audio'; + }; + + SessionPlayer.prototype.getPlaylist = function () { + return Promise.resolve([]); + }; + + SessionPlayer.prototype.getCurrentPlaylistItemId = function () { + }; + + SessionPlayer.prototype.setCurrentPlaylistItem = function (playlistItemId) { + return Promise.resolve(); + }; + + SessionPlayer.prototype.removeFromPlaylist = function (playlistItemIds) { + return Promise.resolve(); + }; + + SessionPlayer.prototype.tryPair = function (target) { + + return Promise.resolve(); + }; + + return SessionPlayer; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/shell.js b/src/bower_components/emby-webcomponents/shell.js index 4c07437286..0083404e19 100644 --- a/src/bower_components/emby-webcomponents/shell.js +++ b/src/bower_components/emby-webcomponents/shell.js @@ -1,12 +1,21 @@ -define([], function() { - "use strict"; +define([], function () { + 'use strict'; + return { - openUrl: function(url) { - window.open(url, "_blank") + openUrl: function (url) { + window.open(url, '_blank'); }, - canExec: !1, - exec: function(options) { - return Promise.reject() + canExec: false, + exec: function (options) { + // options.path + // options.arguments + return Promise.reject(); + }, + enableFullscreen: function () { + // do nothing since this is for native apps + }, + disableFullscreen: function () { + // do nothing since this is for native apps } - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/shortcuts.js b/src/bower_components/emby-webcomponents/shortcuts.js index f14e219fe7..f446bd216c 100644 --- a/src/bower_components/emby-webcomponents/shortcuts.js +++ b/src/bower_components/emby-webcomponents/shortcuts.js @@ -1,248 +1,457 @@ -define(["playbackManager", "inputManager", "connectionManager", "appRouter", "globalize", "loading", "dom", "recordingHelper"], function(playbackManager, inputManager, connectionManager, appRouter, globalize, loading, dom, recordingHelper) { - "use strict"; +define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'globalize', 'loading', 'dom', 'recordingHelper'], function (playbackManager, inputManager, connectionManager, appRouter, globalize, loading, dom, recordingHelper) { + 'use strict'; function playAllFromHere(card, serverId, queue) { - for (var startIndex, parent = card.parentNode, className = card.classList.length ? "." + card.classList[0] : "", cards = parent.querySelectorAll(className + "[data-id]"), ids = [], foundCard = !1, i = 0, length = cards.length; i < length; i++) cards[i] === card && (foundCard = !0, startIndex = i), !foundCard && queue || ids.push(cards[i].getAttribute("data-id")); - var itemsContainer = dom.parentWithClass(card, "itemsContainer"); - if (itemsContainer && itemsContainer.fetchData) { - var queryOptions = queue ? { - StartIndex: startIndex - } : {}; - return itemsContainer.fetchData(queryOptions).then(function(result) { - return queue ? playbackManager.queue({ - items: result.Items - }) : playbackManager.play({ - items: result.Items, - startIndex: startIndex - }) - }) + + var parent = card.parentNode; + var className = card.classList.length ? ('.' + card.classList[0]) : ''; + var cards = parent.querySelectorAll(className + '[data-id]'); + + var ids = []; + + var foundCard = false; + var startIndex; + + for (var i = 0, length = cards.length; i < length; i++) { + if (cards[i] === card) { + foundCard = true; + startIndex = i; + } + if (foundCard || !queue) { + ids.push(cards[i].getAttribute('data-id')); + } + } + + var itemsContainer = dom.parentWithClass(card, 'itemsContainer'); + if (itemsContainer && itemsContainer.fetchData) { + + var queryOptions = queue ? { StartIndex: startIndex } : {}; + + return itemsContainer.fetchData(queryOptions).then(function (result) { + + if (queue) { + return playbackManager.queue({ + items: result.Items + }); + } else { + + return playbackManager.play({ + items: result.Items, + startIndex: startIndex + }); + } + }); + } + + if (!ids.length) { + return; + } + + if (queue) { + return playbackManager.queue({ + ids: ids, + serverId: serverId + }); + } else { + + return playbackManager.play({ + ids: ids, + serverId: serverId, + startIndex: startIndex + }); } - if (ids.length) return queue ? playbackManager.queue({ - ids: ids, - serverId: serverId - }) : playbackManager.play({ - ids: ids, - serverId: serverId, - startIndex: startIndex - }) } function showProgramDialog(item) { - require(["recordingCreator"], function(recordingCreator) { - recordingCreator.show(item.Id, item.ServerId) - }) + + require(['recordingCreator'], function (recordingCreator) { + + recordingCreator.show(item.Id, item.ServerId); + }); } function getItem(button) { - button = dom.parentWithAttribute(button, "data-id"); - var serverId = button.getAttribute("data-serverid"), - id = button.getAttribute("data-id"), - type = button.getAttribute("data-type"), - apiClient = connectionManager.getApiClient(serverId); - return "Timer" === type ? apiClient.getLiveTvTimer(id) : "SeriesTimer" === type ? apiClient.getLiveTvSeriesTimer(id) : apiClient.getItem(apiClient.getCurrentUserId(), id) + + button = dom.parentWithAttribute(button, 'data-id'); + var serverId = button.getAttribute('data-serverid'); + var id = button.getAttribute('data-id'); + var type = button.getAttribute('data-type'); + + var apiClient = connectionManager.getApiClient(serverId); + + if (type === 'Timer') { + return apiClient.getLiveTvTimer(id); + } + if (type === 'SeriesTimer') { + return apiClient.getLiveTvSeriesTimer(id); + } + return apiClient.getItem(apiClient.getCurrentUserId(), id); } function notifyRefreshNeeded(childElement, itemsContainer) { - (itemsContainer = itemsContainer || dom.parentWithAttribute(childElement, "is", "emby-itemscontainer")) && itemsContainer.notifyRefreshNeeded(!0) + + itemsContainer = itemsContainer || dom.parentWithAttribute(childElement, 'is', 'emby-itemscontainer'); + + if (itemsContainer) { + itemsContainer.notifyRefreshNeeded(true); + } } function showContextMenu(card, options) { - getItem(card).then(function(item) { - var playlistId = card.getAttribute("data-playlistid"), - collectionId = card.getAttribute("data-collectionid"); + + getItem(card).then(function (item) { + + var playlistId = card.getAttribute('data-playlistid'); + var collectionId = card.getAttribute('data-collectionid'); + if (playlistId) { - var elem = dom.parentWithAttribute(card, "data-playlistitemid"); - item.PlaylistItemId = elem ? elem.getAttribute("data-playlistitemid") : null + var elem = dom.parentWithAttribute(card, 'data-playlistitemid'); + item.PlaylistItemId = elem ? elem.getAttribute('data-playlistitemid') : null; } - require(["itemContextMenu"], function(itemContextMenu) { - connectionManager.getApiClient(item.ServerId).getCurrentUser().then(function(user) { + + require(['itemContextMenu'], function (itemContextMenu) { + + connectionManager.getApiClient(item.ServerId).getCurrentUser().then(function (user) { itemContextMenu.show(Object.assign({ item: item, - play: !0, - queue: !0, + play: true, + queue: true, playAllFromHere: !item.IsFolder, queueAllFromHere: !item.IsFolder, playlistId: playlistId, collectionId: collectionId, user: user - }, options || {})).then(function(result) { - "playallfromhere" === result.command || "queueallfromhere" === result.command ? executeAction(card, options.positionTo, result.command) : (result.updated || result.deleted) && notifyRefreshNeeded(card, options.itemsContainer) - }) - }) - }) - }) + + }, options || {})).then(function (result) { + + var itemsContainer; + + if (result.command === 'playallfromhere' || result.command === 'queueallfromhere') { + executeAction(card, options.positionTo, result.command); + } + else if (result.updated || result.deleted) { + notifyRefreshNeeded(card, options.itemsContainer); + } + }); + }); + }); + }); } function getItemInfoFromCard(card) { + return { - Type: card.getAttribute("data-type"), - Id: card.getAttribute("data-id"), - TimerId: card.getAttribute("data-timerid"), - CollectionType: card.getAttribute("data-collectiontype"), - ChannelId: card.getAttribute("data-channelid"), - SeriesId: card.getAttribute("data-seriesid"), - ServerId: card.getAttribute("data-serverid"), - MediaType: card.getAttribute("data-mediatype"), - IsFolder: "true" === card.getAttribute("data-isfolder"), + Type: card.getAttribute('data-type'), + Id: card.getAttribute('data-id'), + TimerId: card.getAttribute('data-timerid'), + CollectionType: card.getAttribute('data-collectiontype'), + ChannelId: card.getAttribute('data-channelid'), + SeriesId: card.getAttribute('data-seriesid'), + ServerId: card.getAttribute('data-serverid'), + MediaType: card.getAttribute('data-mediatype'), + IsFolder: card.getAttribute('data-isfolder') === 'true', UserData: { - PlaybackPositionTicks: parseInt(card.getAttribute("data-positionticks") || "0") + PlaybackPositionTicks: parseInt(card.getAttribute('data-positionticks') || '0') } - } + }; } function showPlayMenu(card, target) { + var item = getItemInfoFromCard(card); - require(["playMenu"], function(playMenu) { + + require(['playMenu'], function (playMenu) { + playMenu.show({ + item: item, positionTo: target - }) - }) + }); + }); } function sendToast(text) { - require(["toast"], function(toast) { - toast(text) - }) + require(['toast'], function (toast) { + toast(text); + }); } function executeAction(card, target, action) { + target = target || card; - var id = card.getAttribute("data-id"); - id || (card = dom.parentWithAttribute(card, "data-id"), id = card.getAttribute("data-id")); - var item = getItemInfoFromCard(card), - serverId = item.ServerId, - type = item.Type, - playableItemId = "Program" === type ? item.ChannelId : item.Id; - if ("Photo" === item.MediaType && "link" === action && (action = "play"), "link" === action) appRouter.showItem(item, { - context: card.getAttribute("data-context"), - parentId: card.getAttribute("data-parentid") - }); - else if ("programdialog" === action) showProgramDialog(item); - else if ("instantmix" === action) playbackManager.instantMix({ - Id: playableItemId, - ServerId: serverId - }); - else if ("play" === action || "resume" === action) { - var startPositionTicks = parseInt(card.getAttribute("data-positionticks") || "0"); + + var id = card.getAttribute('data-id'); + + if (!id) { + card = dom.parentWithAttribute(card, 'data-id'); + id = card.getAttribute('data-id'); + } + + var item = getItemInfoFromCard(card); + + var serverId = item.ServerId; + var type = item.Type; + + var playableItemId = type === 'Program' ? item.ChannelId : item.Id; + + if (item.MediaType === 'Photo' && action === 'link') { + action = 'play'; + } + + if (action === 'link') { + + appRouter.showItem(item, { + context: card.getAttribute('data-context'), + parentId: card.getAttribute('data-parentid') + }); + } + + else if (action === 'programdialog') { + + showProgramDialog(item); + } + + else if (action === 'instantmix') { + playbackManager.instantMix({ + Id: playableItemId, + ServerId: serverId + }); + } + + else if (action === 'play' || action === 'resume') { + + var startPositionTicks = parseInt(card.getAttribute('data-positionticks') || '0'); + playbackManager.play({ ids: [playableItemId], startPositionTicks: startPositionTicks, serverId: serverId - }) - } else if ("queue" === action) playbackManager.isPlaying() ? (playbackManager.queue({ - ids: [playableItemId], - serverId: serverId - }), sendToast(globalize.translate("sharedcomponents#MediaQueued"))) : playbackManager.queue({ - ids: [playableItemId], - serverId: serverId - }); - else if ("playallfromhere" === action) playAllFromHere(card, serverId); - else if ("queueallfromhere" === action) playAllFromHere(card, serverId, !0); - else if ("setplaylistindex" === action) playbackManager.setCurrentPlaylistItem(card.getAttribute("data-playlistitemid")); - else if ("record" === action) onRecordCommand(serverId, id, type, card.getAttribute("data-timerid"), card.getAttribute("data-seriestimerid")); - else if ("menu" === action) { - var options = "false" === target.getAttribute("data-playoptions") ? { - shuffle: !1, - instantMix: !1, - play: !1, - playAllFromHere: !1, - queue: !1, - queueAllFromHere: !1 - } : {}; - options.positionTo = target, showContextMenu(card, options) - } else if ("playmenu" === action) showPlayMenu(card, target); - else if ("edit" === action) getItem(target).then(function(item) { - editItem(item, serverId) - }); - else if ("playtrailer" === action) getItem(target).then(playTrailer); - else if ("addtoplaylist" === action) getItem(target).then(addToPlaylist); - else if ("custom" === action) { - var customAction = target.getAttribute("data-customaction"); - card.dispatchEvent(new CustomEvent("action-" + customAction, { + }); + } + + else if (action === 'queue') { + + if (playbackManager.isPlaying()) { + playbackManager.queue({ + ids: [playableItemId], + serverId: serverId + }); + sendToast(globalize.translate('sharedcomponents#MediaQueued')); + } else { + playbackManager.queue({ + ids: [playableItemId], + serverId: serverId + }); + } + } + + else if (action === 'playallfromhere') { + playAllFromHere(card, serverId); + } + + else if (action === 'queueallfromhere') { + playAllFromHere(card, serverId, true); + } + + else if (action === 'setplaylistindex') { + playbackManager.setCurrentPlaylistItem(card.getAttribute('data-playlistitemid')); + } + + else if (action === 'record') { + onRecordCommand(serverId, id, type, card.getAttribute('data-timerid'), card.getAttribute('data-seriestimerid')); + } + + else if (action === 'menu') { + + var options = target.getAttribute('data-playoptions') === 'false' ? + { + shuffle: false, + instantMix: false, + play: false, + playAllFromHere: false, + queue: false, + queueAllFromHere: false + } : + {}; + + options.positionTo = target; + + showContextMenu(card, options); + } + + else if (action === 'playmenu') { + showPlayMenu(card, target); + } + + else if (action === 'edit') { + getItem(target).then(function (item) { + editItem(item, serverId); + }); + } + + else if (action === 'playtrailer') { + getItem(target).then(playTrailer); + } + + else if (action === 'addtoplaylist') { + getItem(target).then(addToPlaylist); + } + + else if (action === 'custom') { + + var customAction = target.getAttribute('data-customaction'); + + card.dispatchEvent(new CustomEvent('action-' + customAction, { detail: { - playlistItemId: card.getAttribute("data-playlistitemid") + playlistItemId: card.getAttribute('data-playlistitemid') }, - cancelable: !1, - bubbles: !0 - })) + cancelable: false, + bubbles: true + })); } } function addToPlaylist(item) { - require(["playlistEditor"], function(playlistEditor) { - (new playlistEditor).show({ + require(['playlistEditor'], function (playlistEditor) { + + new playlistEditor().show({ items: [item.Id], serverId: item.ServerId - }) - }) + + }); + }); } function playTrailer(item) { + var apiClient = connectionManager.getApiClient(item.ServerId); - apiClient.getLocalTrailers(apiClient.getCurrentUserId(), item.Id).then(function(trailers) { - playbackManager.play({ - items: trailers - }) - }) + + apiClient.getLocalTrailers(apiClient.getCurrentUserId(), item.Id).then(function (trailers) { + playbackManager.play({ items: trailers }); + }); } function editItem(item, serverId) { + var apiClient = connectionManager.getApiClient(serverId); - return new Promise(function(resolve, reject) { + + return new Promise(function (resolve, reject) { + var serverId = apiClient.serverInfo().Id; - "Timer" === item.Type ? item.ProgramId ? require(["recordingCreator"], function(recordingCreator) { - recordingCreator.show(item.ProgramId, serverId).then(resolve, reject) - }) : require(["recordingEditor"], function(recordingEditor) { - recordingEditor.show(item.Id, serverId).then(resolve, reject) - }) : require(["metadataEditor"], function(metadataEditor) { - metadataEditor.show(item.Id, serverId).then(resolve, reject) - }) - }) + + if (item.Type === 'Timer') { + if (item.ProgramId) { + require(['recordingCreator'], function (recordingCreator) { + + recordingCreator.show(item.ProgramId, serverId).then(resolve, reject); + }); + } else { + require(['recordingEditor'], function (recordingEditor) { + + recordingEditor.show(item.Id, serverId).then(resolve, reject); + }); + } + } else { + require(['metadataEditor'], function (metadataEditor) { + + metadataEditor.show(item.Id, serverId).then(resolve, reject); + }); + } + }); } function onRecordCommand(serverId, id, type, timerId, seriesTimerId) { - if ("Program" === type || timerId || seriesTimerId) { - var programId = "Program" === type ? id : null; - recordingHelper.toggleRecording(serverId, programId, timerId, seriesTimerId) + + if (type === 'Program' || timerId || seriesTimerId) { + + var programId = type === 'Program' ? id : null; + recordingHelper.toggleRecording(serverId, programId, timerId, seriesTimerId); } } function onClick(e) { - var card = dom.parentWithClass(e.target, "itemAction"); + + var card = dom.parentWithClass(e.target, 'itemAction'); + if (card) { - var actionElement = card, - action = actionElement.getAttribute("data-action"); - if (action || (actionElement = dom.parentWithAttribute(actionElement, "data-action")) && (action = actionElement.getAttribute("data-action")), action) return executeAction(card, actionElement, action), e.preventDefault(), e.stopPropagation(), !1 + + var actionElement = card; + var action = actionElement.getAttribute('data-action'); + + if (!action) { + actionElement = dom.parentWithAttribute(actionElement, 'data-action'); + if (actionElement) { + action = actionElement.getAttribute('data-action'); + } + } + + if (action) { + executeAction(card, actionElement, action); + + e.preventDefault(); + e.stopPropagation(); + return false; + } } } function onCommand(e) { + var cmd = e.detail.command; - if ("play" === cmd || "resume" === cmd || "record" === cmd || "menu" === cmd || "info" === cmd) { - var target = e.target, - card = dom.parentWithClass(target, "itemAction") || dom.parentWithAttribute(target, "data-id"); - card && (e.preventDefault(), e.stopPropagation(), executeAction(card, card, cmd)) + + if (cmd === 'play' || cmd === 'resume' || cmd === 'record' || cmd === 'menu' || cmd === 'info') { + + var target = e.target; + var card = dom.parentWithClass(target, 'itemAction') || dom.parentWithAttribute(target, 'data-id'); + + if (card) { + e.preventDefault(); + e.stopPropagation(); + executeAction(card, card, cmd); + } } } function on(context, options) { - options = options || {}, !1 !== options.click && context.addEventListener("click", onClick), !1 !== options.command && inputManager.on(context, onCommand) + + options = options || {}; + + if (options.click !== false) { + context.addEventListener('click', onClick); + } + + if (options.command !== false) { + inputManager.on(context, onCommand); + } } function off(context, options) { - options = options || {}, context.removeEventListener("click", onClick), !1 !== options.command && inputManager.off(context, onCommand) + options = options || {}; + + context.removeEventListener('click', onClick); + + if (options.command !== false) { + inputManager.off(context, onCommand); + } } function getShortcutAttributesHtml(item, serverId) { - var html = 'data-id="' + item.Id + '" data-serverid="' + (serverId || item.ServerId) + '" data-type="' + item.Type + '" data-mediatype="' + item.MediaType + '" data-channelid="' + item.ChannelId + '" data-isfolder="' + item.IsFolder + '"', - collectionType = item.CollectionType; - return collectionType && (html += ' data-collectiontype="' + collectionType + '"'), html + + var html = 'data-id="' + item.Id + '" data-serverid="' + (serverId || item.ServerId) + '" data-type="' + item.Type + '" data-mediatype="' + item.MediaType + '" data-channelid="' + item.ChannelId + '" data-isfolder="' + item.IsFolder + '"'; + + var collectionType = item.CollectionType; + if (collectionType) { + html += ' data-collectiontype="' + collectionType + '"'; + } + + return html; } + return { on: on, off: off, onClick: onClick, getShortcutAttributesHtml: getShortcutAttributesHtml - } + }; + }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/skinmanager.js b/src/bower_components/emby-webcomponents/skinmanager.js index 962b3319af..67b4e63c2d 100644 --- a/src/bower_components/emby-webcomponents/skinmanager.js +++ b/src/bower_components/emby-webcomponents/skinmanager.js @@ -1,186 +1,393 @@ -define(["apphost", "userSettings", "browser", "events", "pluginManager", "backdrop", "globalize", "require", "appSettings"], function(appHost, userSettings, browser, events, pluginManager, backdrop, globalize, require, appSettings) { - "use strict"; +define(['apphost', 'userSettings', 'browser', 'events', 'pluginManager', 'backdrop', 'globalize', 'require', 'appSettings'], function (appHost, userSettings, browser, events, pluginManager, backdrop, globalize, require, appSettings) { + 'use strict'; + + var currentSkin; function getCurrentSkin() { - return currentSkin + return currentSkin; } function getRequirePromise(deps) { - return new Promise(function(resolve, reject) { - require(deps, resolve) - }) + return new Promise(function (resolve, reject) { + + require(deps, resolve); + }); } function loadSkin(id) { - var newSkin = pluginManager.plugins().filter(function(p) { - return p.id === id + + var newSkin = pluginManager.plugins().filter(function (p) { + return p.id === id; })[0]; - newSkin || (newSkin = pluginManager.plugins().filter(function(p) { - return "defaultskin" === p.id - })[0]); + + if (!newSkin) { + newSkin = pluginManager.plugins().filter(function (p) { + return p.id === 'defaultskin'; + })[0]; + } + var unloadPromise; + if (currentSkin) { - if (currentSkin.id === newSkin.id) return Promise.resolve(currentSkin); - unloadPromise = unloadSkin(currentSkin) - } else unloadPromise = Promise.resolve(); - return unloadPromise.then(function() { + + if (currentSkin.id === newSkin.id) { + // Nothing to do, it's already the active skin + return Promise.resolve(currentSkin); + } + unloadPromise = unloadSkin(currentSkin); + } else { + unloadPromise = Promise.resolve(); + } + + return unloadPromise.then(function () { var deps = newSkin.getDependencies(); - return console.log("Loading skin dependencies"), getRequirePromise(deps).then(function() { - console.log("Skin dependencies loaded"); + + console.log('Loading skin dependencies'); + + return getRequirePromise(deps).then(function () { + + console.log('Skin dependencies loaded'); + var strings = newSkin.getTranslations ? newSkin.getTranslations() : []; + return globalize.loadStrings({ + name: newSkin.id, strings: strings - }).then(function() { - return globalize.defaultModule(newSkin.id), loadSkinHeader(newSkin) - }) - }) - }) + + }).then(function () { + + globalize.defaultModule(newSkin.id); + return loadSkinHeader(newSkin); + }); + }); + }); } function unloadSkin(skin) { - return unloadTheme(), backdrop.clear(), console.log("Unloading skin: " + skin.name), skin.unload().then(function() { + + unloadTheme(); + backdrop.clear(); + + console.log('Unloading skin: ' + skin.name); + + // TODO: unload css + + return skin.unload().then(function () { document.dispatchEvent(new CustomEvent("skinunload", { detail: { name: skin.name } - })) - }) + })); + }); } function loadSkinHeader(skin) { - return getSkinHeader(skin).then(function(headerHtml) { - return document.querySelector(".skinHeader").innerHTML = headerHtml, currentSkin = skin, skin.load(), skin - }) + + return getSkinHeader(skin).then(function (headerHtml) { + + document.querySelector('.skinHeader').innerHTML = headerHtml; + + currentSkin = skin; + skin.load(); + + return skin; + }); } + var cacheParam = new Date().getTime(); + function getSkinHeader(skin) { - return new Promise(function(resolve, reject) { - if (!skin.getHeaderTemplate) return void resolve(""); - var xhr = new XMLHttpRequest, - url = skin.getHeaderTemplate(); - url += -1 === url.indexOf("?") ? "?" : "&", url += "v=" + cacheParam, xhr.open("GET", url, !0), xhr.onload = function(e) { - resolve(this.status < 400 ? this.response : "") - }, xhr.send() - }) + + return new Promise(function (resolve, reject) { + + if (!skin.getHeaderTemplate) { + resolve(''); + return; + } + + var xhr = new XMLHttpRequest(); + + var url = skin.getHeaderTemplate(); + url += url.indexOf('?') === -1 ? '?' : '&'; + url += 'v=' + cacheParam; + + xhr.open('GET', url, true); + + xhr.onload = function (e) { + if (this.status < 400) { + resolve(this.response); + } else { + resolve(''); + } + }; + + xhr.send(); + }); } function loadUserSkin(options) { - loadSkin(userSettings.get("skin", !1) || "defaultskin").then(function(skin) { - options = options || {}, options.start ? Emby.Page.invokeShortcut(options.start) : Emby.Page.goHome() - }) + + var skin = userSettings.get('skin', false) || 'defaultskin'; + + loadSkin(skin).then(function (skin) { + + options = options || {}; + if (options.start) { + Emby.Page.invokeShortcut(options.start); + } else { + Emby.Page.goHome(); + } + }); } + events.on(userSettings, 'change', function (e, name) { + if (name === 'skin' || name === 'language') { + loadUserSkin(); + } + }); + + var themeStyleElement; + var currentThemeId; function unloadTheme() { var elem = themeStyleElement; - elem && (elem.parentNode.removeChild(elem), themeStyleElement = null, currentThemeId = null) + if (elem) { + + elem.parentNode.removeChild(elem); + themeStyleElement = null; + currentThemeId = null; + } } function getThemes() { - return currentSkin.getThemes ? currentSkin.getThemes() : [] + + if (currentSkin.getThemes) { + return currentSkin.getThemes(); + } + + return []; } + var skinManager = { + getCurrentSkin: getCurrentSkin, + loadSkin: loadSkin, + loadUserSkin: loadUserSkin, + getThemes: getThemes + }; + function onRegistrationSuccess() { - appSettings.set("appthemesregistered", "true") + appSettings.set('appthemesregistered', 'true'); } function onRegistrationFailure() { - appSettings.set("appthemesregistered", "false") + appSettings.set('appthemesregistered', 'false'); } function isRegistered() { - return getRequirePromise(["registrationServices"]).then(function(registrationServices) { - registrationServices.validateFeature("themes", { - showDialog: !1 - }).then(onRegistrationSuccess, onRegistrationFailure) - }), "false" !== appSettings.get("appthemesregistered") + + getRequirePromise(['registrationServices']).then(function (registrationServices) { + registrationServices.validateFeature('themes', { + + showDialog: false + + }).then(onRegistrationSuccess, onRegistrationFailure); + }); + + return appSettings.get('appthemesregistered') !== 'false'; } function getThemeStylesheetInfo(id, requiresRegistration, isDefaultProperty) { - for (var defaultTheme, selectedTheme, themes = skinManager.getThemes(), i = 0, length = themes.length; i < length; i++) { + + var themes = skinManager.getThemes(); + var defaultTheme; + var selectedTheme; + + for (var i = 0, length = themes.length; i < length; i++) { + var theme = themes[i]; - theme[isDefaultProperty] && (defaultTheme = theme), id === theme.id && (selectedTheme = theme) + if (theme[isDefaultProperty]) { + defaultTheme = theme; + } + if (id === theme.id) { + selectedTheme = theme; + } } - selectedTheme = selectedTheme || defaultTheme, selectedTheme.id !== defaultTheme.id && requiresRegistration && !isRegistered() && (selectedTheme = defaultTheme); + + selectedTheme = selectedTheme || defaultTheme; + + if (selectedTheme.id !== defaultTheme.id && requiresRegistration && !isRegistered()) { + selectedTheme = defaultTheme; + } + + var embyWebComponentsBowerPath = 'bower_components/emby-webcomponents'; + return { - stylesheetPath: require.toUrl("bower_components/emby-webcomponents/themes/" + selectedTheme.id + "/theme.css"), + stylesheetPath: require.toUrl(embyWebComponentsBowerPath + '/themes/' + selectedTheme.id + '/theme.css'), themeId: selectedTheme.id - } + }; } + var themeResources = {}; function modifyThemeForSeasonal(id) { - if (!userSettings.enableSeasonalThemes()) return id; - var date = new Date, - month = date.getMonth(), - day = date.getDate(); - return 9 === month && day >= 30 ? "halloween" : id + + if (!userSettings.enableSeasonalThemes()) { + return id; + } + + var date = new Date(); + var month = date.getMonth(); + var day = date.getDate(); + + if (month === 9 && day >= 30) { + return 'halloween'; + } + + return id; } + var lastSound = 0; + var currentSound; + function loadThemeResources(id) { - if (lastSound = 0, currentSound && (currentSound.stop(), currentSound = null), backdrop.clear(), "halloween" === id) return void(themeResources = { - themeSong: "https://github.com/MediaBrowser/Emby.Resources/raw/master/themes/halloween/monsterparadefade.mp3", - effect: "https://github.com/MediaBrowser/Emby.Resources/raw/master/themes/halloween/howl.wav", - backdrop: "https://github.com/MediaBrowser/Emby.Resources/raw/master/themes/halloween/bg.jpg" - }); - themeResources = {} + + lastSound = 0; + + if (currentSound) { + currentSound.stop(); + currentSound = null; + } + + backdrop.clear(); + + if (id === 'halloween') { + themeResources = { + themeSong: 'https://github.com/MediaBrowser/Emby.Resources/raw/master/themes/halloween/monsterparadefade.mp3', + effect: 'https://github.com/MediaBrowser/Emby.Resources/raw/master/themes/halloween/howl.wav', + backdrop: 'https://github.com/MediaBrowser/Emby.Resources/raw/master/themes/halloween/bg.jpg' + }; + return; + } + + themeResources = { + }; } function onThemeLoaded() { - document.documentElement.classList.remove("preload"); + document.documentElement.classList.remove('preload'); + + try { - var color = getComputedStyle(document.querySelector(".skinHeader")).getPropertyValue("background-color"); - color && appHost.setThemeColor(color) - } catch (err) { - console.log("Error setting theme color: " + err) + var color = getComputedStyle(document.querySelector('.skinHeader')).getPropertyValue("background-color"); + + if (color) { + appHost.setThemeColor(color); + } + } + catch (err) { + console.log('Error setting theme color: ' + err); } } + skinManager.setTheme = function (id, context) { + + return new Promise(function (resolve, reject) { + + var requiresRegistration = true; + + if (context !== 'serverdashboard') { + + var newId = modifyThemeForSeasonal(id); + if (newId !== id) { + requiresRegistration = false; + } + id = newId; + } + + if (currentThemeId && currentThemeId === id) { + resolve(); + return; + } + + var isDefaultProperty = context === 'serverdashboard' ? 'isDefaultServerDashboard' : 'isDefault'; + var info = getThemeStylesheetInfo(id, requiresRegistration, isDefaultProperty); + + if (currentThemeId && currentThemeId === info.themeId) { + resolve(); + return; + } + + var linkUrl = info.stylesheetPath; + + unloadTheme(); + + var link = document.createElement('link'); + + link.setAttribute('rel', 'stylesheet'); + link.setAttribute('type', 'text/css'); + link.onload = function () { + + onThemeLoaded(); + resolve(); + }; + + link.setAttribute('href', linkUrl); + document.head.appendChild(link); + themeStyleElement = link; + currentThemeId = info.themeId; + loadThemeResources(info.themeId); + + onViewBeforeShow({}); + }); + }; + function onViewBeforeShow(e) { - e.detail && "video-osd" === e.detail.type || (themeResources.backdrop && backdrop.setBackdrop(themeResources.backdrop), !browser.mobile && userSettings.enableThemeSongs() && (0 === lastSound ? themeResources.themeSong && playSound(themeResources.themeSong) : (new Date).getTime() - lastSound > 3e4 && themeResources.effect && playSound(themeResources.effect))) + + if (e.detail && e.detail.type === 'video-osd') { + return; + } + + if (themeResources.backdrop) { + + backdrop.setBackdrop(themeResources.backdrop); + } + + if (!browser.mobile && userSettings.enableThemeSongs()) { + if (lastSound === 0) { + + if (themeResources.themeSong) { + playSound(themeResources.themeSong); + } + + } else if ((new Date().getTime() - lastSound) > 30000) { + if (themeResources.effect) { + playSound(themeResources.effect); + } + } + } } + document.addEventListener('viewshow', onViewBeforeShow); + function playSound(path, volume) { - lastSound = (new Date).getTime(), require(["howler"], function(howler) { + + lastSound = new Date().getTime(); + + require(['howler'], function (howler) { + try { var sound = new Howl({ src: [path], - volume: volume || .1 + volume: volume || 0.1 }); - sound.play(), currentSound = sound - } catch (err) { - console.log("Error playing sound: " + err) + + sound.play(); + currentSound = sound; } - }) + catch (err) { + console.log('Error playing sound: ' + err); + } + }); } - var currentSkin, cacheParam = (new Date).getTime(); - events.on(userSettings, "change", function(e, name) { - "skin" !== name && "language" !== name || loadUserSkin() - }); - var themeStyleElement, currentThemeId, currentSound, skinManager = { - getCurrentSkin: getCurrentSkin, - loadSkin: loadSkin, - loadUserSkin: loadUserSkin, - getThemes: getThemes - }, - themeResources = {}, - lastSound = 0; - return skinManager.setTheme = function(id, context) { - return new Promise(function(resolve, reject) { - var requiresRegistration = !0; - if ("serverdashboard" !== context) { - var newId = modifyThemeForSeasonal(id); - newId !== id && (requiresRegistration = !1), id = newId - } - if (currentThemeId && currentThemeId === id) return void resolve(); - var isDefaultProperty = "serverdashboard" === context ? "isDefaultServerDashboard" : "isDefault", - info = getThemeStylesheetInfo(id, requiresRegistration, isDefaultProperty); - if (currentThemeId && currentThemeId === info.themeId) return void resolve(); - var linkUrl = info.stylesheetPath; - unloadTheme(); - var link = document.createElement("link"); - link.setAttribute("rel", "stylesheet"), link.setAttribute("type", "text/css"), link.onload = function() { - onThemeLoaded(), resolve() - }, link.setAttribute("href", linkUrl), document.head.appendChild(link), themeStyleElement = link, currentThemeId = info.themeId, loadThemeResources(info.themeId), onViewBeforeShow({}) - }) - }, document.addEventListener("viewshow", onViewBeforeShow), skinManager + + return skinManager; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/slideshow/slideshow.js b/src/bower_components/emby-webcomponents/slideshow/slideshow.js index c5aeffc832..42cdabfffd 100644 --- a/src/bower_components/emby-webcomponents/slideshow/slideshow.js +++ b/src/bower_components/emby-webcomponents/slideshow/slideshow.js @@ -1,317 +1,643 @@ -define(["dialogHelper", "inputManager", "connectionManager", "layoutManager", "focusManager", "browser", "apphost", "loading", "css!./style", "material-icons", "paper-icon-button-light"], function(dialogHelper, inputmanager, connectionManager, layoutManager, focusManager, browser, appHost, loading) { - "use strict"; +define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'focusManager', 'browser', 'apphost', 'loading', 'css!./style', 'material-icons', 'paper-icon-button-light'], function (dialogHelper, inputmanager, connectionManager, layoutManager, focusManager, browser, appHost, loading) { + 'use strict'; function getImageUrl(item, options, apiClient) { - return options = options || {}, options.type = options.type || "Primary", "string" == typeof item ? apiClient.getScaledImageUrl(item, options) : item.ImageTags && item.ImageTags[options.type] ? (options.tag = item.ImageTags[options.type], apiClient.getScaledImageUrl(item.Id, options)) : "Primary" === options.type && item.AlbumId && item.AlbumPrimaryImageTag ? (options.tag = item.AlbumPrimaryImageTag, apiClient.getScaledImageUrl(item.AlbumId, options)) : null + + options = options || {}; + options.type = options.type || "Primary"; + + if (typeof (item) === 'string') { + return apiClient.getScaledImageUrl(item, options); + } + + if (item.ImageTags && item.ImageTags[options.type]) { + + options.tag = item.ImageTags[options.type]; + return apiClient.getScaledImageUrl(item.Id, options); + } + + if (options.type === 'Primary') { + if (item.AlbumId && item.AlbumPrimaryImageTag) { + + options.tag = item.AlbumPrimaryImageTag; + return apiClient.getScaledImageUrl(item.AlbumId, options); + } + } + + return null; } function getBackdropImageUrl(item, options, apiClient) { - return options = options || {}, options.type = options.type || "Backdrop", options.maxWidth || options.width || options.maxHeight || options.height || (options.quality = 100), item.BackdropImageTags && item.BackdropImageTags.length ? (options.tag = item.BackdropImageTags[0], apiClient.getScaledImageUrl(item.Id, options)) : null + + options = options || {}; + options.type = options.type || "Backdrop"; + + // If not resizing, get the original image + if (!options.maxWidth && !options.width && !options.maxHeight && !options.height) { + options.quality = 100; + } + + if (item.BackdropImageTags && item.BackdropImageTags.length) { + + options.tag = item.BackdropImageTags[0]; + return apiClient.getScaledImageUrl(item.Id, options); + } + + return null; } function getImgUrl(item, original) { - var apiClient = connectionManager.getApiClient(item.ServerId), - imageOptions = {}; - return original || (imageOptions.maxWidth = screen.availWidth), item.BackdropImageTags && item.BackdropImageTags.length ? getBackdropImageUrl(item, imageOptions, apiClient) : "Photo" === item.MediaType && original ? apiClient.getItemDownloadUrl(item.Id) : (imageOptions.type = "Primary", getImageUrl(item, imageOptions, apiClient)) + + var apiClient = connectionManager.getApiClient(item.ServerId); + var imageOptions = {}; + + if (!original) { + imageOptions.maxWidth = screen.availWidth; + } + if (item.BackdropImageTags && item.BackdropImageTags.length) { + return getBackdropImageUrl(item, imageOptions, apiClient); + } else { + + if (item.MediaType === 'Photo' && original) { + return apiClient.getItemDownloadUrl(item.Id); + } + imageOptions.type = "Primary"; + return getImageUrl(item, imageOptions, apiClient); + } } function getIcon(icon, cssClass, canFocus, autoFocus) { - var tabIndex = canFocus ? "" : ' tabindex="-1"'; - return autoFocus = autoFocus ? " autofocus" : "", '" + + var tabIndex = canFocus ? '' : ' tabindex="-1"'; + autoFocus = autoFocus ? ' autofocus' : ''; + return ''; } function setUserScalable(scalable) { + try { - appHost.setUserScalable(scalable) - } catch (err) { - console.log("error in appHost.setUserScalable: " + err) + appHost.setUserScalable(scalable); + } + catch (err) { + console.log('error in appHost.setUserScalable: ' + err); } } - return function(options) { + + return function (options) { + + var self = this; + var swiperInstance; + var dlg; + var currentTimeout; + var currentIntervalMs; + var currentOptions; + var currentIndex; + + // small hack since this is not possible anyway + if (browser.chromecast) { + options.interactive = false; + } + function createElements(options) { + dlg = dialogHelper.createDialog({ exitAnimationDuration: options.interactive ? 400 : 800, - size: "fullscreen", - autoFocus: !1, - scrollY: !1, - exitAnimation: "fadeout", - removeOnClose: !0 - }), dlg.classList.add("slideshowDialog"); - var html = ""; + size: 'fullscreen', + autoFocus: false, + scrollY: false, + exitAnimation: 'fadeout', + removeOnClose: true + }); + + dlg.classList.add('slideshowDialog'); + + var html = ''; + if (options.interactive) { + var actionButtonsOnTop = layoutManager.mobile; - html += "
    ", html += '
    ', html += getIcon("keyboard_arrow_left", "btnSlideshowPrevious slideshowButton hide-mouse-idle-tv", !1), html += getIcon("keyboard_arrow_right", "btnSlideshowNext slideshowButton hide-mouse-idle-tv", !1), html += '
    ', actionButtonsOnTop && (appHost.supports("filedownload") && (html += getIcon("file_download", "btnDownload slideshowButton", !0)), appHost.supports("sharing") && (html += getIcon("share", "btnShare slideshowButton", !0))), html += getIcon("close", "slideshowButton btnSlideshowExit hide-mouse-idle-tv", !1), html += "
    ", actionButtonsOnTop || (html += '
    ', html += getIcon("pause", "btnSlideshowPause slideshowButton", !0, !0), appHost.supports("filedownload") && (html += getIcon("file_download", "btnDownload slideshowButton", !0)), appHost.supports("sharing") && (html += getIcon("share", "btnShare slideshowButton", !0)), html += "
    "), html += "
    " - } else html += '

    '; - if (dlg.innerHTML = html, options.interactive) { - dlg.querySelector(".btnSlideshowExit").addEventListener("click", function(e) { - dialogHelper.close(dlg) - }), dlg.querySelector(".btnSlideshowNext").addEventListener("click", nextImage), dlg.querySelector(".btnSlideshowPrevious").addEventListener("click", previousImage); - var btnPause = dlg.querySelector(".btnSlideshowPause"); - btnPause && btnPause.addEventListener("click", playPause); - var btnDownload = dlg.querySelector(".btnDownload"); - btnDownload && btnDownload.addEventListener("click", download); - var btnShare = dlg.querySelector(".btnShare"); - btnShare && btnShare.addEventListener("click", share) + + html += '
    '; + html += '
    '; + + html += getIcon('keyboard_arrow_left', 'btnSlideshowPrevious slideshowButton hide-mouse-idle-tv', false); + html += getIcon('keyboard_arrow_right', 'btnSlideshowNext slideshowButton hide-mouse-idle-tv', false); + + html += '
    '; + if (actionButtonsOnTop) { + if (appHost.supports('filedownload')) { + html += getIcon('file_download', 'btnDownload slideshowButton', true); + } + if (appHost.supports('sharing')) { + html += getIcon('share', 'btnShare slideshowButton', true); + } + } + html += getIcon('close', 'slideshowButton btnSlideshowExit hide-mouse-idle-tv', false); + html += '
    '; + + if (!actionButtonsOnTop) { + html += '
    '; + + html += getIcon('pause', 'btnSlideshowPause slideshowButton', true, true); + if (appHost.supports('filedownload')) { + html += getIcon('file_download', 'btnDownload slideshowButton', true); + } + if (appHost.supports('sharing')) { + html += getIcon('share', 'btnShare slideshowButton', true); + } + + html += '
    '; + } + + html += '
    '; + + } else { + html += '

    '; + } + + dlg.innerHTML = html; + + if (options.interactive) { + dlg.querySelector('.btnSlideshowExit').addEventListener('click', function (e) { + + dialogHelper.close(dlg); + }); + dlg.querySelector('.btnSlideshowNext').addEventListener('click', nextImage); + dlg.querySelector('.btnSlideshowPrevious').addEventListener('click', previousImage); + + var btnPause = dlg.querySelector('.btnSlideshowPause'); + if (btnPause) { + btnPause.addEventListener('click', playPause); + } + + var btnDownload = dlg.querySelector('.btnDownload'); + if (btnDownload) { + btnDownload.addEventListener('click', download); + } + + var btnShare = dlg.querySelector('.btnShare'); + if (btnShare) { + btnShare.addEventListener('click', share); + } + } + + setUserScalable(true); + + dialogHelper.open(dlg).then(function () { + + setUserScalable(false); + stopInterval(); + }); + + inputmanager.on(window, onInputCommand); + document.addEventListener((window.PointerEvent ? 'pointermove' : 'mousemove'), onPointerMove); + + dlg.addEventListener('close', onDialogClosed); + + if (options.interactive) { + loadSwiper(dlg); } - setUserScalable(!0), dialogHelper.open(dlg).then(function() { - setUserScalable(!1), stopInterval() - }), inputmanager.on(window, onInputCommand), document.addEventListener(window.PointerEvent ? "pointermove" : "mousemove", onPointerMove), dlg.addEventListener("close", onDialogClosed), options.interactive && loadSwiper(dlg) } function loadSwiper(dlg) { - currentOptions.slides ? dlg.querySelector(".swiper-wrapper").innerHTML = currentOptions.slides.map(getSwiperSlideHtmlFromSlide).join("") : dlg.querySelector(".swiper-wrapper").innerHTML = currentOptions.items.map(getSwiperSlideHtmlFromItem).join(""), require(["swiper"], function(swiper) { - swiperInstance = new Swiper(dlg.querySelector(".slideshowSwiperContainer"), { - direction: "horizontal", - loop: !1 !== options.loop, - autoplay: options.interval || 8e3, - preloadImages: !1, - lazyLoading: !0, - lazyLoadingInPrevNext: !0, - autoplayDisableOnInteraction: !1, + + if (currentOptions.slides) { + dlg.querySelector('.swiper-wrapper').innerHTML = currentOptions.slides.map(getSwiperSlideHtmlFromSlide).join(''); + } else { + dlg.querySelector('.swiper-wrapper').innerHTML = currentOptions.items.map(getSwiperSlideHtmlFromItem).join(''); + } + + require(['swiper'], function (swiper) { + + swiperInstance = new Swiper(dlg.querySelector('.slideshowSwiperContainer'), { + // Optional parameters + direction: 'horizontal', + loop: options.loop !== false, + autoplay: options.interval || 8000, + // Disable preloading of all images + preloadImages: false, + // Enable lazy loading + lazyLoading: true, + lazyLoadingInPrevNext: true, + autoplayDisableOnInteraction: false, initialSlide: options.startIndex || 0, speed: 240 - }), layoutManager.mobile ? pause() : play() - }) + }); + + if (layoutManager.mobile) { + pause(); + } else { + play(); + } + }); } function getSwiperSlideHtmlFromItem(item) { + return getSwiperSlideHtmlFromSlide({ imageUrl: getImgUrl(item), - originalImage: getImgUrl(item, !0), + originalImage: getImgUrl(item, true), + //title: item.Name, + //description: item.Overview Id: item.Id, ServerId: item.ServerId - }) + }); } function getSwiperSlideHtmlFromSlide(item) { - var html = ""; - return html += '
    ', html += '', (item.title || item.subtitle) && (html += '
    ', html += '
    ', item.title && (html += '

    ', html += item.title, html += "

    "), item.description && (html += '
    ', html += item.description, html += "
    "), html += "
    ", html += "
    "), html += "
    " + + var html = ''; + html += '
    '; + html += ''; + if (item.title || item.subtitle) { + html += '
    '; + html += '
    '; + if (item.title) { + html += '

    '; + html += item.title; + html += '

    '; + } + if (item.description) { + html += '
    '; + html += item.description; + html += '
    '; + } + html += '
    '; + html += '
    '; + } + html += '
    '; + + return html; } function previousImage() { - swiperInstance ? swiperInstance.slidePrev() : (stopInterval(), showNextImage(currentIndex - 1)) + if (swiperInstance) { + swiperInstance.slidePrev(); + } else { + stopInterval(); + showNextImage(currentIndex - 1); + } } function nextImage() { if (swiperInstance) { - if (!1 === options.loop && swiperInstance.activeIndex >= swiperInstance.slides.length - 1) return void dialogHelper.close(dlg); - swiperInstance.slideNext() - } else stopInterval(), showNextImage(currentIndex + 1) + + if (options.loop === false) { + + if (swiperInstance.activeIndex >= swiperInstance.slides.length - 1) { + dialogHelper.close(dlg); + return; + } + } + + swiperInstance.slideNext(); + } else { + stopInterval(); + showNextImage(currentIndex + 1); + } } function getCurrentImageInfo() { + if (swiperInstance) { - var slide = document.querySelector(".swiper-slide-active"); - return slide ? { - url: slide.getAttribute("data-original"), - shareUrl: slide.getAttribute("data-imageurl"), - itemId: slide.getAttribute("data-itemid"), - serverId: slide.getAttribute("data-serverid") - } : null + var slide = document.querySelector('.swiper-slide-active'); + + if (slide) { + return { + url: slide.getAttribute('data-original'), + shareUrl: slide.getAttribute('data-imageurl'), + itemId: slide.getAttribute('data-itemid'), + serverId: slide.getAttribute('data-serverid') + }; + } + return null; + } else { + return null; } - return null } function download() { + var imageInfo = getCurrentImageInfo(); - require(["fileDownloader"], function(fileDownloader) { - fileDownloader.download([imageInfo]) - }) + + require(['fileDownloader'], function (fileDownloader) { + fileDownloader.download([imageInfo]); + }); } function share() { + var imageInfo = getCurrentImageInfo(); + navigator.share({ url: imageInfo.shareUrl - }) + }); } function play() { - var btnSlideshowPause = dlg.querySelector(".btnSlideshowPause i"); - btnSlideshowPause && (btnSlideshowPause.innerHTML = "pause"), swiperInstance.startAutoplay() + + var btnSlideshowPause = dlg.querySelector('.btnSlideshowPause i'); + if (btnSlideshowPause) { + btnSlideshowPause.innerHTML = "pause"; + } + + swiperInstance.startAutoplay(); } function pause() { - var btnSlideshowPause = dlg.querySelector(".btnSlideshowPause i"); - btnSlideshowPause && (btnSlideshowPause.innerHTML = "play_arrow"), swiperInstance.stopAutoplay() + + var btnSlideshowPause = dlg.querySelector('.btnSlideshowPause i'); + if (btnSlideshowPause) { + btnSlideshowPause.innerHTML = "play_arrow"; + } + + swiperInstance.stopAutoplay(); } function playPause() { - "pause" !== dlg.querySelector(".btnSlideshowPause i").innerHTML ? play() : pause() + + var paused = dlg.querySelector('.btnSlideshowPause i').innerHTML !== "pause"; + if (paused) { + play(); + } else { + pause(); + } } function onDialogClosed() { + var swiper = swiperInstance; - swiper && (swiper.destroy(!0, !0), swiperInstance = null), inputmanager.off(window, onInputCommand), document.removeEventListener(window.PointerEvent ? "pointermove" : "mousemove", onPointerMove) + if (swiper) { + swiper.destroy(true, true); + swiperInstance = null; + } + + inputmanager.off(window, onInputCommand); + document.removeEventListener((window.PointerEvent ? 'pointermove' : 'mousemove'), onPointerMove); } function startInterval(options) { - currentOptions = options, stopInterval(), createElements(options), options.interactive || (currentIntervalMs = options.interval || 11e3, showNextImage(options.startIndex || 0, !0)) + + currentOptions = options; + + stopInterval(); + createElements(options); + + if (!options.interactive) { + currentIntervalMs = options.interval || 11000; + showNextImage(options.startIndex || 0, true); + } } + var _osdOpen = false; + function isOsdOpen() { - return _osdOpen + return _osdOpen; } function getOsdBottom() { - return dlg.querySelector(".slideshowBottomBar") + return dlg.querySelector('.slideshowBottomBar'); } function showOsd() { + var bottom = getOsdBottom(); - bottom && (slideUpToShow(bottom), startHideTimer()) + if (bottom) { + slideUpToShow(bottom); + startHideTimer(); + } } function hideOsd() { + var bottom = getOsdBottom(); - bottom && slideDownToHide(bottom) + if (bottom) { + slideDownToHide(bottom); + } } + var hideTimeout; + function startHideTimer() { - stopHideTimer(), hideTimeout = setTimeout(hideOsd, 4e3) + stopHideTimer(); + hideTimeout = setTimeout(hideOsd, 4000); } function stopHideTimer() { - hideTimeout && (clearTimeout(hideTimeout), hideTimeout = null) + if (hideTimeout) { + clearTimeout(hideTimeout); + hideTimeout = null; + } } function slideUpToShow(elem) { - if (elem.classList.contains("hide")) { - _osdOpen = !0, elem.classList.remove("hide"); - var onFinish = function() { - focusManager.focus(elem.querySelector(".btnSlideshowPause")) - }; - if (!elem.animate) return void onFinish(); - requestAnimationFrame(function() { - var keyframes = [{ - transform: "translate3d(0," + elem.offsetHeight + "px,0)", - opacity: ".3", - offset: 0 - }, { - transform: "translate3d(0,0,0)", - opacity: "1", - offset: 1 - }], - timing = { - duration: 300, - iterations: 1, - easing: "ease-out" - }; - elem.animate(keyframes, timing).onfinish = onFinish - }) + + if (!elem.classList.contains('hide')) { + return; } + + _osdOpen = true; + elem.classList.remove('hide'); + + var onFinish = function () { + focusManager.focus(elem.querySelector('.btnSlideshowPause')); + }; + + if (!elem.animate) { + onFinish(); + return; + } + + requestAnimationFrame(function () { + + var keyframes = [ + { transform: 'translate3d(0,' + elem.offsetHeight + 'px,0)', opacity: '.3', offset: 0 }, + { transform: 'translate3d(0,0,0)', opacity: '1', offset: 1 } + ]; + var timing = { duration: 300, iterations: 1, easing: 'ease-out' }; + elem.animate(keyframes, timing).onfinish = onFinish; + }); } function slideDownToHide(elem) { - if (!elem.classList.contains("hide")) { - var onFinish = function() { - elem.classList.add("hide"), _osdOpen = !1 - }; - if (!elem.animate) return void onFinish(); - requestAnimationFrame(function() { - var keyframes = [{ - transform: "translate3d(0,0,0)", - opacity: "1", - offset: 0 - }, { - transform: "translate3d(0," + elem.offsetHeight + "px,0)", - opacity: ".3", - offset: 1 - }], - timing = { - duration: 300, - iterations: 1, - easing: "ease-out" - }; - elem.animate(keyframes, timing).onfinish = onFinish - }) + + if (elem.classList.contains('hide')) { + return; } + + var onFinish = function () { + elem.classList.add('hide'); + _osdOpen = false; + }; + + if (!elem.animate) { + onFinish(); + return; + } + + requestAnimationFrame(function () { + + var keyframes = [ + { transform: 'translate3d(0,0,0)', opacity: '1', offset: 0 }, + { transform: 'translate3d(0,' + elem.offsetHeight + 'px,0)', opacity: '.3', offset: 1 } + ]; + var timing = { duration: 300, iterations: 1, easing: 'ease-out' }; + elem.animate(keyframes, timing).onfinish = onFinish; + }); } + var lastMouseMoveData; + function onPointerMove(e) { - if ("mouse" === (e.pointerType || (layoutManager.mobile ? "touch" : "mouse"))) { - var eventX = e.screenX || 0, - eventY = e.screenY || 0, - obj = lastMouseMoveData; - if (!obj) return void(lastMouseMoveData = { - x: eventX, - y: eventY - }); - if (Math.abs(eventX - obj.x) < 10 && Math.abs(eventY - obj.y) < 10) return; - obj.x = eventX, obj.y = eventY, showOsd() + + var pointerType = e.pointerType || (layoutManager.mobile ? 'touch' : 'mouse'); + + if (pointerType === 'mouse') { + var eventX = e.screenX || 0; + var eventY = e.screenY || 0; + + var obj = lastMouseMoveData; + if (!obj) { + lastMouseMoveData = { + x: eventX, + y: eventY + }; + return; + } + + // if coord are same, it didn't move + if (Math.abs(eventX - obj.x) < 10 && Math.abs(eventY - obj.y) < 10) { + return; + } + + obj.x = eventX; + obj.y = eventY; + + showOsd(); } } function onInputCommand(e) { + switch (e.detail.command) { - case "left": - isOsdOpen() || (e.preventDefault(), e.stopPropagation(), previousImage()); + + case 'left': + if (!isOsdOpen()) { + e.preventDefault(); + e.stopPropagation(); + previousImage(); + } break; - case "right": - isOsdOpen() || (e.preventDefault(), e.stopPropagation(), nextImage()); + case 'right': + if (!isOsdOpen()) { + e.preventDefault(); + e.stopPropagation(); + nextImage(); + } + break; + case 'up': + case 'down': + case 'select': + case 'menu': + case 'info': + case 'play': + case 'playpause': + case 'pause': + showOsd(); + break; + default: break; - case "up": - case "down": - case "select": - case "menu": - case "info": - case "play": - case "playpause": - case "pause": - showOsd() } } function showNextImage(index, skipPreload) { - index = Math.max(0, index), index >= currentOptions.items.length && (index = 0), currentIndex = index; - var options = currentOptions, - items = options.items, - item = items[index], - imgUrl = getImgUrl(item), - onSrcLoaded = function() { - var cardImageContainer = dlg.querySelector(".slideshowImage"), - newCardImageContainer = document.createElement("div"); - newCardImageContainer.className = cardImageContainer.className, options.cover && newCardImageContainer.classList.add("slideshowImage-cover"), newCardImageContainer.style.backgroundImage = "url('" + imgUrl + "')", newCardImageContainer.classList.add("hide"), cardImageContainer.parentNode.appendChild(newCardImageContainer), options.showTitle ? dlg.querySelector(".slideshowImageText").innerHTML = item.Name : dlg.querySelector(".slideshowImageText").innerHTML = "", newCardImageContainer.classList.remove("hide"); - var onAnimationFinished = function() { - var parentNode = cardImageContainer.parentNode; - parentNode && parentNode.removeChild(cardImageContainer) - }; - if (newCardImageContainer.animate) { - var keyframes = [{ - opacity: "0", - offset: 0 - }, { - opacity: "1", - offset: 1 - }], - timing = { - duration: 1200, - iterations: 1 - }; - newCardImageContainer.animate(keyframes, timing).onfinish = onAnimationFinished - } else onAnimationFinished(); - stopInterval(), currentTimeout = setTimeout(function() { - showNextImage(index + 1, !0) - }, currentIntervalMs) + + index = Math.max(0, index); + if (index >= currentOptions.items.length) { + index = 0; + } + currentIndex = index; + + var options = currentOptions; + var items = options.items; + var item = items[index]; + var imgUrl = getImgUrl(item); + + var onSrcLoaded = function () { + var cardImageContainer = dlg.querySelector('.slideshowImage'); + + var newCardImageContainer = document.createElement('div'); + newCardImageContainer.className = cardImageContainer.className; + + if (options.cover) { + newCardImageContainer.classList.add('slideshowImage-cover'); + } + + newCardImageContainer.style.backgroundImage = "url('" + imgUrl + "')"; + newCardImageContainer.classList.add('hide'); + cardImageContainer.parentNode.appendChild(newCardImageContainer); + + if (options.showTitle) { + dlg.querySelector('.slideshowImageText').innerHTML = item.Name; + } else { + dlg.querySelector('.slideshowImageText').innerHTML = ''; + } + + newCardImageContainer.classList.remove('hide'); + var onAnimationFinished = function () { + + var parentNode = cardImageContainer.parentNode; + if (parentNode) { + parentNode.removeChild(cardImageContainer); + } }; - if (skipPreload) onSrcLoaded(); - else { - var img = new Image; - img.onload = onSrcLoaded, img.src = imgUrl + + if (newCardImageContainer.animate) { + + var keyframes = [ + { opacity: '0', offset: 0 }, + { opacity: '1', offset: 1 } + ]; + var timing = { duration: 1200, iterations: 1 }; + newCardImageContainer.animate(keyframes, timing).onfinish = onAnimationFinished; + } else { + onAnimationFinished(); + } + + stopInterval(); + currentTimeout = setTimeout(function () { + showNextImage(index + 1, true); + + }, currentIntervalMs); + }; + + if (!skipPreload) { + var img = new Image(); + img.onload = onSrcLoaded; + img.src = imgUrl; + } else { + onSrcLoaded(); } } function stopInterval() { - currentTimeout && (clearTimeout(currentTimeout), currentTimeout = null) + if (currentTimeout) { + clearTimeout(currentTimeout); + currentTimeout = null; + } } - var swiperInstance, dlg, currentTimeout, currentIntervalMs, currentOptions, currentIndex, self = this; - browser.chromecast && (options.interactive = !1); - var hideTimeout, lastMouseMoveData, _osdOpen = !1; - self.show = function() { - startInterval(options) - }, self.hide = function() { + + self.show = function () { + startInterval(options); + }; + + self.hide = function () { + var dialog = dlg; - dialog && dialogHelper.close(dialog) - } - } + if (dialog) { + + dialogHelper.close(dialog); + } + }; + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/slideshow/style.css b/src/bower_components/emby-webcomponents/slideshow/style.css index 85cae77d29..43211a8fef 100644 --- a/src/bower_components/emby-webcomponents/slideshow/style.css +++ b/src/bower_components/emby-webcomponents/slideshow/style.css @@ -1,29 +1,28 @@ -.slideshowDialog, -.slideshowSwiperContainer, -.swiper-slide, -.swiper-wrapper { - background: #000 +.slideshowDialog { + /* Themes may use non-black backgrounds for dialogs, so override that here */ + background: #000; } -.slideshowImage, -.slideshowSwiperContainer { +.slideshowSwiperContainer, .swiper-wrapper, .swiper-slide { + background: #000; +} + +.slideshowImage, .slideshowSwiperContainer { position: fixed; top: 0; right: 0; left: 0; bottom: 0; background-position: center center; - -webkit-background-size: contain; background-size: contain; background-repeat: no-repeat; margin: 0 !important; color: #fff; - line-height: normal + line-height: normal; } .slideshowImage-cover { - -webkit-background-size: cover; - background-size: cover + background-size: cover; } .slideshowImageText { @@ -32,8 +31,9 @@ right: .5em; color: #fff; z-index: 1002; - font-weight: 400; - text-shadow: 3px 3px 0 #000, -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000 + font-weight: normal; + /* Add an outline */ + text-shadow: 3px 3px 0 #000, -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000; } .swiper-slide-img { @@ -41,86 +41,73 @@ height: auto; max-width: 100%; max-height: 100%; + -ms-transform: translate(-50%, -50%); -webkit-transform: translate(-50%, -50%); + -moz-transform: translate(-50%, -50%); transform: translate(-50%, -50%); position: absolute; left: 50%; - top: 50% -} - -.btnSlideshowNext, -.btnSlideshowPrevious { - top: 45vh; - z-index: 1002; - position: absolute + top: 50%; } .slideshowButtonIcon { color: #fff; - opacity: .7 + opacity: .7; } .btnSlideshowPrevious { - left: .5vh + left: .5vh; + top: 45vh; + z-index: 1002; + position: absolute; } .btnSlideshowNext { - right: .5vh + right: .5vh; + top: 45vh; + z-index: 1002; + position: absolute; } .topActionButtons { right: .5vh; top: .5vh; z-index: 1002; - position: absolute -} - -.slideshowBottomBar, -.slideshowTopBar { - position: fixed; - background-color: rgba(0, 0, 0, .7); - color: #fff; - padding: .5%; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - left: 0; - right: 0 + position: absolute; } .slideshowBottomBar { + position: fixed; + left: 0; bottom: 0; - display: -webkit-box; - display: -webkit-flex; + right: 0; + background-color: rgba(0, 0, 0, .7); + color: #fff; + padding: .5%; display: flex; - -webkit-flex-direction: row; flex-direction: row; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; - justify-content: center + justify-content: center; } .slideshowTopBar { + position: fixed; + left: 0; top: 0; - display: -webkit-box; - display: -webkit-flex; + right: 0; + background-color: rgba(0, 0, 0, .7); + color: #fff; + padding: .5%; display: flex; - -webkit-flex-direction: row; flex-direction: row; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; text-align: right; - -webkit-box-pack: end; - -webkit-justify-content: flex-end; - justify-content: flex-end + justify-content: flex-end; } .slideshowExtraButtons { margin-left: auto; - text-align: right + text-align: right; } .slideText { @@ -128,23 +115,22 @@ left: 0; right: 0; bottom: 10vh; - text-align: center + text-align: center; } .slideTextInner { margin: 0 auto; max-width: 60%; - background: rgba(0, 0, 0, .8); + background: rgba(0,0,0,.8); display: inline-block; padding: .5em 1em; - -webkit-border-radius: .25em; - border-radius: .25em + border-radius: .25em; } .slideTitle { - margin: 0 0 .25em + margin: 0 0 .25em; } .slideSubtitle { - color: #ccc -} \ No newline at end of file + color: #ccc; +} diff --git a/src/bower_components/emby-webcomponents/sortmenu/sortmenu.js b/src/bower_components/emby-webcomponents/sortmenu/sortmenu.js index b2298230d9..2dd08cf481 100644 --- a/src/bower_components/emby-webcomponents/sortmenu/sortmenu.js +++ b/src/bower_components/emby-webcomponents/sortmenu/sortmenu.js @@ -1,54 +1,124 @@ -define(["require", "dom", "focusManager", "dialogHelper", "loading", "layoutManager", "connectionManager", "globalize", "userSettings", "emby-select", "paper-icon-button-light", "material-icons", "css!./../formdialog", "emby-button", "emby-linkbutton", "flexStyles"], function(require, dom, focusManager, dialogHelper, loading, layoutManager, connectionManager, globalize, userSettings) { - "use strict"; +define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'layoutManager', 'connectionManager', 'globalize', 'userSettings', 'emby-select', 'paper-icon-button-light', 'material-icons', 'css!./../formdialog', 'emby-button', 'emby-linkbutton', 'flexStyles'], function (require, dom, focusManager, dialogHelper, loading, layoutManager, connectionManager, globalize, userSettings) { + 'use strict'; function onSubmit(e) { - return e.preventDefault(), !1 + + e.preventDefault(); + return false; } function initEditor(context, settings) { - context.querySelector("form").addEventListener("submit", onSubmit), context.querySelector(".selectSortOrder").value = settings.sortOrder, context.querySelector(".selectSortBy").value = settings.sortBy + + context.querySelector('form').addEventListener('submit', onSubmit); + + context.querySelector('.selectSortOrder').value = settings.sortOrder; + context.querySelector('.selectSortBy').value = settings.sortBy; } function centerFocus(elem, horiz, on) { - require(["scrollHelper"], function(scrollHelper) { - var fn = on ? "on" : "off"; - scrollHelper.centerFocus[fn](elem, horiz) - }) + require(['scrollHelper'], function (scrollHelper) { + var fn = on ? 'on' : 'off'; + scrollHelper.centerFocus[fn](elem, horiz); + }); } function fillSortBy(context, options) { - context.querySelector(".selectSortBy").innerHTML = options.map(function(o) { - return '" - }).join("") + var selectSortBy = context.querySelector('.selectSortBy'); + + selectSortBy.innerHTML = options.map(function (o) { + + return ''; + + }).join(''); } function saveValues(context, settings, settingsKey) { - userSettings.setFilter(settingsKey + "-sortorder", context.querySelector(".selectSortOrder").value), userSettings.setFilter(settingsKey + "-sortby", context.querySelector(".selectSortBy").value) + + userSettings.setFilter(settingsKey + '-sortorder', context.querySelector('.selectSortOrder').value); + userSettings.setFilter(settingsKey + '-sortby', context.querySelector('.selectSortBy').value); } - function SortMenu() {} - return SortMenu.prototype.show = function(options) { - return new Promise(function(resolve, reject) { - require(["text!./sortmenu.template.html"], function(template) { + function SortMenu() { + + } + + SortMenu.prototype.show = function (options) { + + return new Promise(function (resolve, reject) { + + require(['text!./sortmenu.template.html'], function (template) { + var dialogOptions = { - removeOnClose: !0, - scrollY: !1 + removeOnClose: true, + scrollY: false }; - layoutManager.tv ? dialogOptions.size = "fullscreen" : dialogOptions.size = "small"; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'small'; + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"); - var html = ""; - html += '
    ', html += '', html += '

    ${Sort}

    ', html += "
    ", html += template, dlg.innerHTML = globalize.translateDocument(html, "sharedcomponents"), fillSortBy(dlg, options.sortOptions), initEditor(dlg, options.settings), dlg.querySelector(".btnCancel").addEventListener("click", function() { - dialogHelper.close(dlg) - }), layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !0); + + dlg.classList.add('formDialog'); + + var html = ''; + + html += '
    '; + html += ''; + html += '

    ${Sort}

    '; + + html += '
    '; + + html += template; + + dlg.innerHTML = globalize.translateDocument(html, 'sharedcomponents'); + + fillSortBy(dlg, options.sortOptions); + initEditor(dlg, options.settings); + + dlg.querySelector('.btnCancel').addEventListener('click', function () { + + dialogHelper.close(dlg); + }); + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, true); + } + var submitted; - dlg.querySelector("form").addEventListener("change", function() { - submitted = !0 - }, !0), dialogHelper.open(dlg).then(function() { - if (layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !1), submitted) return saveValues(dlg, options.settings, options.settingsKey), void resolve(); - reject() - }) - }) - }) - }, SortMenu + + dlg.querySelector('form').addEventListener('change', function () { + + submitted = true; + //if (options.onChange) { + // saveValues(dlg, options.settings, options.settingsKey); + // options.onChange(); + //} + + }, true); + + dialogHelper.open(dlg).then(function () { + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, false); + } + + if (submitted) { + + //if (!options.onChange) { + saveValues(dlg, options.settings, options.settingsKey); + resolve(); + //} + return; + } + + reject(); + }); + }); + }); + }; + + return SortMenu; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/staticbackdrops.js b/src/bower_components/emby-webcomponents/staticbackdrops.js index 62a9a1f71d..f69d0109b2 100644 --- a/src/bower_components/emby-webcomponents/staticbackdrops.js +++ b/src/bower_components/emby-webcomponents/staticbackdrops.js @@ -1,66 +1,129 @@ -define([], function() { - "use strict"; +define([], function () { + 'use strict'; function getStaticBackdrops() { var list = []; - return list.push([{ - url: "https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg1-1920.jpg", - width: 1920 - }]), list.push([{ - url: "https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg2-1920.jpg", - width: 1920 - }]), list.push([{ - url: "https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg3-1920.jpg", - width: 1920 - }]), list.push([{ - url: "https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg4-1920.jpg", - width: 1920 - }]), list.push([{ - url: "https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg5-1920.jpg", - width: 1920 - }]), list.push([{ - url: "https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg6-1920.jpg", - width: 1920 - }]), list.push([{ - url: "https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg7-1920.jpg", - width: 1920 - }]), list.push([{ - url: "https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg8-1920.jpg", - width: 1920 - }]), list.push([{ - url: "https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg9-1920.jpg", - width: 1920 - }]), list.push([{ - url: "https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg10-1920.jpg", - width: 1920 - }]), list.push([{ - url: "https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg11-1920.jpg", - width: 1920 - }]), list.push([{ - url: "https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg12-1920.jpg", - width: 1920 - }]), list.push([{ - url: "https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg13-1920.jpg", - width: 1920 - }]), list.push([{ - url: "https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg14-1920.jpg", - width: 1920 - }]), list.push([{ - url: "https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg15-1920.jpg", - width: 1920 - }]), list + + list.push([ + { + url: 'https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg1-1920.jpg', + width: 1920 + } + ]); + + list.push([ + { + url: 'https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg2-1920.jpg', + width: 1920 + } + ]); + + list.push([ + { + url: 'https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg3-1920.jpg', + width: 1920 + } + ]); + + list.push([ + { + url: 'https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg4-1920.jpg', + width: 1920 + } + ]); + + list.push([ + { + url: 'https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg5-1920.jpg', + width: 1920 + } + ]); + + list.push([ + { + url: 'https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg6-1920.jpg', + width: 1920 + } + ]); + + list.push([ + { + url: 'https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg7-1920.jpg', + width: 1920 + } + ]); + + list.push([ + { + url: 'https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg8-1920.jpg', + width: 1920 + } + ]); + + list.push([ + { + url: 'https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg9-1920.jpg', + width: 1920 + } + ]); + + list.push([ + { + url: 'https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg10-1920.jpg', + width: 1920 + } + ]); + + list.push([ + { + url: 'https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg11-1920.jpg', + width: 1920 + } + ]); + + list.push([ + { + url: 'https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg12-1920.jpg', + width: 1920 + } + ]); + + list.push([ + { + url: 'https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg13-1920.jpg', + width: 1920 + } + ]); + + list.push([ + { + url: 'https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg14-1920.jpg', + width: 1920 + } + ]); + + list.push([ + { + url: 'https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/wallpaper/bg15-1920.jpg', + width: 1920 + } + ]); + + return list; } function getRandomInt(min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min + return Math.floor(Math.random() * (max - min + 1)) + min; } function getRandomImageUrl() { var images = getStaticBackdrops(); - return images[getRandomInt(0, images.length - 1)][0].url + var index = getRandomInt(0, images.length - 1); + return images[index][0].url; } + return { getStaticBackdrops: getStaticBackdrops, getRandomImageUrl: getRandomImageUrl - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/strings/ar.json b/src/bower_components/emby-webcomponents/strings/ar.json index fcc1670523..70e3b7cf96 100644 --- a/src/bower_components/emby-webcomponents/strings/ar.json +++ b/src/bower_components/emby-webcomponents/strings/ar.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "New", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "الغاء", "ButtonGotIt": "Got It", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metadata Settings", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "New Recording", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "You Said...", "Help": "Help", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Item saved.", @@ -674,9 +672,6 @@ "ViewAlbum": "View album", "ViewArtist": "View artist", "VoiceInput": "Voice Input", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "الاربعاء", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/be-by.json b/src/bower_components/emby-webcomponents/strings/be-by.json index cf046fb941..e8d394ddd6 100644 --- a/src/bower_components/emby-webcomponents/strings/be-by.json +++ b/src/bower_components/emby-webcomponents/strings/be-by.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "New", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Адмяніць", "ButtonGotIt": "Got It", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metadata Settings", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "New Recording", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "You Said...", "Help": "Help", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Item saved.", @@ -674,9 +672,6 @@ "ViewAlbum": "View album", "ViewArtist": "View artist", "VoiceInput": "Voice Input", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "Wednesday", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/bg-bg.json b/src/bower_components/emby-webcomponents/strings/bg-bg.json index db408e1018..4f98fb470a 100644 --- a/src/bower_components/emby-webcomponents/strings/bg-bg.json +++ b/src/bower_components/emby-webcomponents/strings/bg-bg.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "Нови", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Книги", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Разглеждане", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Отмяна", "ButtonGotIt": "Добре", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Настройки на метаданните", "HeaderMusicQuality": "Качество на музиката", "HeaderMyDevice": "Моето устройство", - "HeaderMyDownloads": "Моите изтегляния", "HeaderMyMedia": "Моята медия", "HeaderMyMediaSmall": "Моята медия (малък)", "HeaderNewRecording": "Нов запис", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Качество на видеото", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "You Said...", "Help": "Помощ", "Hide": "Скриване", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Item saved.", @@ -674,9 +672,6 @@ "ViewAlbum": "Преглед на албума", "ViewArtist": "Преглед на изпълнителя", "VoiceInput": "Voice Input", - "WakeServer": "Събуждане", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Изгледано", "Wednesday": "Сряда", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/ca.json b/src/bower_components/emby-webcomponents/strings/ca.json index fe25dd0c36..bc7cf2dd38 100644 --- a/src/bower_components/emby-webcomponents/strings/ca.json +++ b/src/bower_components/emby-webcomponents/strings/ca.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "Tants com sigui possible", "Ascending": "Ascending", "AspectRatio": "Relació d'aspecte", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "Nou", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Cancel·la", "ButtonGotIt": "Entesos", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Preferències de Metadades", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "El meu dispositiu", - "HeaderMyDownloads": "Les meves descàrregues", "HeaderMyMedia": "Els meus mitjans", "HeaderMyMediaSmall": "Els meus mitjans (petit)", "HeaderNewRecording": "Nou Enregistrament", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Esperant Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "Has dit...", "Help": "Ajuda", "Hide": "Amaga", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Descàrrega encuada.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Ítem desat.", @@ -674,9 +672,6 @@ "ViewAlbum": "Veure àlbum", "ViewArtist": "Veure artista", "VoiceInput": "Voice Input", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Vists", "Wednesday": "Dimecres", "WifiRequiredToDownload": "Es requereix una connexió Wifi per continuar descarregant.", diff --git a/src/bower_components/emby-webcomponents/strings/cs.json b/src/bower_components/emby-webcomponents/strings/cs.json index eb9291a360..76d670aa7b 100644 --- a/src/bower_components/emby-webcomponents/strings/cs.json +++ b/src/bower_components/emby-webcomponents/strings/cs.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "Tolikrát jak je možné", "Ascending": "Ascending", "AspectRatio": "Poměr stran", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "Nové", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Datový tok audia není podporován", @@ -53,7 +52,6 @@ "Books": "Knihy", "Box": "Pouzdro", "BoxRear": "Zadní část pouzdra", - "Browse": "Procházet", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Zrušit", "ButtonGotIt": "Mám to", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Nastavení metadat", "HeaderMusicQuality": "Kvalita hudby", "HeaderMyDevice": "Moje zařízení", - "HeaderMyDownloads": "Moje stahování", "HeaderMyMedia": "Moje média", "HeaderMyMediaSmall": "Moje média (malé)", "HeaderNewRecording": "Nový záznam", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Kvalita videa", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Čekání na Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "Zmínil ses...", "Help": "Nápověda", "Hide": "Skrýt", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Stažení zařazeno.", "MessageFileReadError": "Došlo k chybě při čtení souboru. Prosím zkuste to znovu.", "MessageIfYouBlockedVoice": "Pokud byl Váš přístup odepřen pomocí hlasové aplikace, budete ji muset překonfigurovat před dalším pokusem.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "E-mail byl odeslán na adresu {0} s výzvou k registraci s Jellyfin.", "MessageInvitationSentToUser": "E-mail byl odeslán na adresu {0} a přijmutím této pozvnánky akceptujete vaší pozvánku ke sdílení.", "MessageItemSaved": "Položka uložena.", @@ -674,9 +672,6 @@ "ViewAlbum": "Zobrazit album", "ViewArtist": "Zobrazit úmělce", "VoiceInput": "Hlasový vstup", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Shlédnuto", "Wednesday": "Středa", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/da.json b/src/bower_components/emby-webcomponents/strings/da.json index 1f3081bce2..61cfe345aa 100644 --- a/src/bower_components/emby-webcomponents/strings/da.json +++ b/src/bower_components/emby-webcomponents/strings/da.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "Så mange som muligt", "Ascending": "Ascending", "AspectRatio": "Billedformat", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "Ny", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Annuller", "ButtonGotIt": "Forstået", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Indstillinger for metadata", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "Min Enhed", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "Ny optagelse", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "Du sagde...", "Help": "Hjælp", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download sat i kø.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "Hvis du afslog adgang til tale for appen, skal du re-konfigurere før du prøver igen.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Element gemt.", @@ -674,9 +672,6 @@ "ViewAlbum": "Vis album", "ViewArtist": "Vis kunstner", "VoiceInput": "Taleinput", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "Onsdag", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/de.json b/src/bower_components/emby-webcomponents/strings/de.json index 74577ea047..7d1a1acc51 100644 --- a/src/bower_components/emby-webcomponents/strings/de.json +++ b/src/bower_components/emby-webcomponents/strings/de.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "So viele wie möglich", "Ascending": "Aufsteigend", "AspectRatio": "Seitenverhältnis", - "AttemptingWakeServer": "Versuche Server aufwecken. Bitte warten...", "AttributeNew": "Neu", "AudioBitDepthNotSupported": "Audiobittiefe nicht unterstützt", "AudioBitrateNotSupported": "Audio Bitrate nicht unterstützt", @@ -53,7 +52,6 @@ "Books": "Bücher", "Box": "Box", "BoxRear": "Box (Rückseite)", - "Browse": "Blättern", "BurnSubtitlesHelp": "Legt fest, ob der Server die Untertitel basierend auf deren Format einbrennen soll während der Videokonvertierung. Die Vermeidung des Einbrennen von Untertiteln verbessert die Serverperformance. Wähle Auto, um Bildfomate (z.B. VOBSUB, PGS, SUB/IDX, etc.) sowie bestimmte ASS/SSA-Untertitel einbrennen zu lassen.", "ButtonCancel": "Abbrechen", "ButtonGotIt": "Verstanden", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metadaten Einstellungen", "HeaderMusicQuality": "Musikqualität", "HeaderMyDevice": "Mein Gerät", - "HeaderMyDownloads": "Meine Downloads", "HeaderMyMedia": "Meine Medien", "HeaderMyMediaSmall": "Meine Medien (Klein)", "HeaderNewRecording": "Neue Aufnahme", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Videoqualität", "HeaderVideoType": "Videotyp:", "HeaderWaitingForWifi": "Warte auf WLAN", - "HeaderWakeServer": "Server aufwecken", "HeaderYouSaid": "Du sagtest...", "Help": "Hilfe", "Hide": "Verstecke", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download eingereiht.", "MessageFileReadError": "Es gab einen Fehler beim Lesen der Datei. Bitte versuche es erneut.", "MessageIfYouBlockedVoice": "Wenn du die Sprachsteuerung für die App nicht erlaubt hast, musst du dies vor einem erneuten Versuch ändern.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "Eine Email wurde an {0} mit einer Einladung zur Anmeldung an Jellyfin gesendet.", "MessageInvitationSentToUser": "Eine E-Mail mit der Einladung zum Sharing ist an {0} geschickt worden.", "MessageItemSaved": "Element gespeichert", @@ -674,9 +672,6 @@ "ViewAlbum": "Zeige Album", "ViewArtist": "Zeige Darsteller", "VoiceInput": "Spracheingabe", - "WakeServer": "Server aufwecken", - "WakeServerError": "Wake On LAN Pakete wurden an deinen Server geschickt, aber wir konnten deinen Jellyfin Server nicht erreichen. Deine Maschine braucht vielleicht etwas länger um aufzuwachen oder Jellyfin Server wird zur Zeit nicht ausgeführt.", - "WakeServerSuccess": "Erfolgreich!", "Watched": "Gesehen", "Wednesday": "Mittwoch", "WifiRequiredToDownload": "Um den Download fortzusetzen wird eine WLAN-Verbindung benötigt.", diff --git a/src/bower_components/emby-webcomponents/strings/el.json b/src/bower_components/emby-webcomponents/strings/el.json index d27a8e89cb..2997301643 100644 --- a/src/bower_components/emby-webcomponents/strings/el.json +++ b/src/bower_components/emby-webcomponents/strings/el.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "Οσο το δυνατον περισσοτερα", "Ascending": "Αύξουσα", "AspectRatio": "Αρχικός λόγος διαστάσεων:", - "AttemptingWakeServer": "Ενεργοποίηση server σε εξέλιξη. Παρακαλώ περιμένετε...", "AttributeNew": "Νέο", "AudioBitDepthNotSupported": "Το βάθος bitrate ήχου δεν υποστηρίζεται", "AudioBitrateNotSupported": "Το bitrate ήχου δεν υποστηρίζεται", @@ -53,7 +52,6 @@ "Books": "Βιβλία", "Box": "Κουτί", "BoxRear": "Box (rear)", - "Browse": "Αναζητήστε", "BurnSubtitlesHelp": "Καθορίζει αν ο διακομιστής πρέπει να εγγράψει στους υπότιτλους κατά τη μετατροπή βίντεο ανάλογα με τη μορφή των υπότιτλων. Η αποφυγή της εγγραφής στους υπότιτλους θα βελτιώσει την απόδοση του διακομιστή. Επιλέξτε Αυτόματα για να εγγράψετε μορφές βασισμένες σε εικόνες (π.χ. VOBSUB, PGS, SUB / IDX κ.λπ.) καθώς και ορισμένους υπότιτλους ASS / SSA", "ButtonCancel": "Ακύρωση ", "ButtonGotIt": "Το κατάλαβα", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Ρυθμίσεις μεταδεδομένων", "HeaderMusicQuality": "Ποιότητα Μουσικής", "HeaderMyDevice": "Η Συσκευή μου", - "HeaderMyDownloads": "Οι Λήψεις μου", "HeaderMyMedia": "Τα Πολυμέσα μου", "HeaderMyMediaSmall": "Τα Πολυμέσα μου (μικρά)", "HeaderNewRecording": "Νέα Εγγραφή", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Ποιότητα βίντεο", "HeaderVideoType": "Τύπος Βίντεο", "HeaderWaitingForWifi": "Αναμονή για Wifi", - "HeaderWakeServer": "Ενεργοποίηση Server", "HeaderYouSaid": "Είπατε...", "Help": "Βοήθεια", "Hide": "Κρύψε", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Η λήψη προγραμματίστηκε.", "MessageFileReadError": "Παρουσιάστηκε σφάλμα κατά την ανάγνωση του αρχείου. Παρακαλώ προσπάθησε ξανά.", "MessageIfYouBlockedVoice": "Αν αρνηθήκατε τη φωνητική πρόσβαση στην εφαρμογή, θα χρειαστεί να επαναρυθμίσετε τη ρύθμιση πριν δοκιμάσετε ξανά.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "Έγινε αποστολή ενός μηνύματος ηλεκτρονικού ταχυδρομείου στο {0}, καλώντας τα να εγγραφούν στο Jellyfin.", "MessageInvitationSentToUser": "Ένα μήνυμα ηλεκτρονικού ταχυδρομείου έχει σταλεί στο {0}, καλώντας τα να αποδεχθούν την πρόσκληση κοινής χρήσης.", "MessageItemSaved": "Το στοιχείο αποθηκεύτηκε", @@ -674,9 +672,6 @@ "ViewAlbum": "Προβολή άλμπουμ", "ViewArtist": "Εμφάνιση Καλλιτέχνη", "VoiceInput": "Είσοδος Ήχου", - "WakeServer": "Ενεργοποίηση server", - "WakeServerError": "Τα πακέτα Wake On LAN στάλθηκαν στον server σας, αλλά δεν είναι δυνατή η σύνδεση με τον Jellyfin Server. Το μηχάνημά σας μπορεί να χρειαστεί λίγο περισσότερο χρόνο για να ενεργοποιηθεί, ή ο Jellyfin Server ενδέχεται να μην εκτελείται ενεργά στο μηχάνημα.", - "WakeServerSuccess": "Επιτυχία!", "Watched": "Έχει γίνει παρακολούθηση", "Wednesday": "Τετάρτη", "WifiRequiredToDownload": "Απαιτείται σύνδεση Wi-Fi για να συνεχίσετε τη λήψη.", diff --git a/src/bower_components/emby-webcomponents/strings/en-gb.json b/src/bower_components/emby-webcomponents/strings/en-gb.json index 7a1b075395..7100ecf6e5 100644 --- a/src/bower_components/emby-webcomponents/strings/en-gb.json +++ b/src/bower_components/emby-webcomponents/strings/en-gb.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "New", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Cancel", "ButtonGotIt": "Got It", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metadata Settings", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "New Recording", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wi-Fi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "You Said...", "Help": "Help", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0}, inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Item saved.", @@ -674,9 +672,6 @@ "ViewAlbum": "View album", "ViewArtist": "View artist", "VoiceInput": "Voice Input", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "Wednesday", "WifiRequiredToDownload": "A Wi-Fi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/en-us.json b/src/bower_components/emby-webcomponents/strings/en-us.json index ec81e54290..6534cab223 100644 --- a/src/bower_components/emby-webcomponents/strings/en-us.json +++ b/src/bower_components/emby-webcomponents/strings/en-us.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "New", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Cancel", "ButtonGotIt": "Got It", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metadata Settings", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "New Recording", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "You Said...", "Help": "Help", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Item saved.", @@ -674,9 +672,6 @@ "ViewAlbum": "View album", "ViewArtist": "View artist", "VoiceInput": "Voice Input", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "Wednesday", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/es-ar.json b/src/bower_components/emby-webcomponents/strings/es-ar.json index 0711679784..4f5b3022c6 100644 --- a/src/bower_components/emby-webcomponents/strings/es-ar.json +++ b/src/bower_components/emby-webcomponents/strings/es-ar.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "New", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Cancel", "ButtonGotIt": "Got It", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metadata Settings", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "New Recording", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "You Said...", "Help": "Help", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Item saved.", @@ -674,9 +672,6 @@ "ViewAlbum": "View album", "ViewArtist": "View artist", "VoiceInput": "Voice Input", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "Wednesday", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/es-mx.json b/src/bower_components/emby-webcomponents/strings/es-mx.json index 3c35481fc1..49aaf8b50f 100644 --- a/src/bower_components/emby-webcomponents/strings/es-mx.json +++ b/src/bower_components/emby-webcomponents/strings/es-mx.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "Tantos como sea posible", "Ascending": "Ascendente", "AspectRatio": "Relación de aspecto", - "AttemptingWakeServer": "Intentando despertar el servidor. Por favor espere...", "AttributeNew": "Nuevo", "AudioBitDepthNotSupported": "Profundidad de bits de Audio no soportado", "AudioBitrateNotSupported": "Tasa de bits de audio no soportado", @@ -53,7 +52,6 @@ "Books": "Libros", "Box": "Caja", "BoxRear": "Reverso de caja", - "Browse": "Navegar", "BurnSubtitlesHelp": "Determina si el servidor debería quemar los subtitulos al convertir el video dependiendo en el formato de los subtitulos. Evitar los subtitulos quemados mejorara el rendimiento del servidor. Elija \"Auto\" para quemar los formatos basados en imágenes (por ejemplo VOBSUB, PGS, SUB/IDX, etc.) así como ciertos subtitulos ASS/SSA", "ButtonCancel": "Cancelar", "ButtonGotIt": "Hecho", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Configuración de metadatos", "HeaderMusicQuality": "Calidad de Musica", "HeaderMyDevice": "Mi Dispositivo", - "HeaderMyDownloads": "Mis Descargas", "HeaderMyMedia": "Mis Medios", "HeaderMyMediaSmall": "Mis medios (pequeño)", "HeaderNewRecording": "Nueva Grabación", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Calidad de Video", "HeaderVideoType": "Tipo de Video", "HeaderWaitingForWifi": "Esperando Wifi", - "HeaderWakeServer": "Despertar Servidor", "HeaderYouSaid": "Ha Dicho...", "Help": "Ayuda", "Hide": "Ocultar", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Descargar cola.", "MessageFileReadError": "Hubo un error al leer el archivo. Por favor intente de nuevo.", "MessageIfYouBlockedVoice": "Si ha negado el acceso a la voz a la aplicación necesitara reconfigurar antes de intentarlo de nuevo.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "Un correo electrónico se ha enviado a {0} invitándolos a registrarse en Jellyfin.", "MessageInvitationSentToUser": "Se ha enviado un correo electrónico a {0}, invitándolo a aceptar tu invitación para compartir.", "MessageItemSaved": "Ítem guardado.", @@ -674,9 +672,6 @@ "ViewAlbum": "Ver album", "ViewArtist": "Ver artista", "VoiceInput": "Entrada de Voz", - "WakeServer": "Despertar servidor", - "WakeServerError": "Se enviaron los paquetes Wake On LAN (Despertar por red) a su computadora servidor, pero no ha sido posible contactar a su Servidor Jellyfin. Su computadora quizás necesite un poco mas de tiempo para despertar, o tal vez la aplicación de Servidor Jellyfin no se esta ejecutando en la computadora.", - "WakeServerSuccess": "¡Éxito!", "Watched": "Visto", "Wednesday": "Miércoles", "WifiRequiredToDownload": "Se necesita de una conexión Wifi para continuar descargando.", diff --git a/src/bower_components/emby-webcomponents/strings/es.json b/src/bower_components/emby-webcomponents/strings/es.json index 31c35bf32d..86cae6abf1 100644 --- a/src/bower_components/emby-webcomponents/strings/es.json +++ b/src/bower_components/emby-webcomponents/strings/es.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "Tantos como sea posible", "Ascending": "Ascending", "AspectRatio": "Relación de aspecto", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "Nuevo", "AudioBitDepthNotSupported": "Profundidad de bits de audio no soportada", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Libros", "Box": "Caja", "BoxRear": "Caja (trasera)", - "Browse": "Navegar", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Cancelar", "ButtonGotIt": "Lo tengo", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Ajustes de metadatos", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "Mi dispositivo", - "HeaderMyDownloads": "Mis Descargas", "HeaderMyMedia": "Mis Contenidos", "HeaderMyMediaSmall": "Mis Contenidos (pequeño)", "HeaderNewRecording": "Nueva grabación", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Calidad de Video", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Esperando a la red Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "Dijiste...", "Help": "Ayuda", "Hide": "Esconder", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Descarga en cola.", "MessageFileReadError": "Ha habido un error leyendo el fichero. Por favor inténtalo más tarde.", "MessageIfYouBlockedVoice": "Si has denegado el acceso a la voz a la aplicación tendrás que reconfigurarlo antes de intentarlo de nuevo.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "Se le ha enviado un correo a {0}, invitándolo a que se registre en Jellyfin.", "MessageInvitationSentToUser": "Se le ha enviado un correo a {0}, invitándolo a que acepte lo que has compartido.", "MessageItemSaved": "Elemento grabado.", @@ -674,9 +672,6 @@ "ViewAlbum": "Ver album", "ViewArtist": "Ver artista", "VoiceInput": "Entrada de voz", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Visto", "Wednesday": "Miércoles", "WifiRequiredToDownload": "Una red Wifi es necesaria para continuar la descarga.", diff --git a/src/bower_components/emby-webcomponents/strings/fi.json b/src/bower_components/emby-webcomponents/strings/fi.json index c6329252ec..e8502275d8 100644 --- a/src/bower_components/emby-webcomponents/strings/fi.json +++ b/src/bower_components/emby-webcomponents/strings/fi.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "New", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Lopeta", "ButtonGotIt": "Got It", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metadata Settings", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "New Recording", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "You Said...", "Help": "Help", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Item saved.", @@ -674,9 +672,6 @@ "ViewAlbum": "View album", "ViewArtist": "View artist", "VoiceInput": "Voice Input", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "Wednesday", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/fr-ca.json b/src/bower_components/emby-webcomponents/strings/fr-ca.json index 4002a0c0f9..31d1b932c8 100644 --- a/src/bower_components/emby-webcomponents/strings/fr-ca.json +++ b/src/bower_components/emby-webcomponents/strings/fr-ca.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "Nouveau", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Annuler", "ButtonGotIt": "J'ai compris", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metadata Settings", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "Mes Médias", "HeaderMyMediaSmall": "Mes médias (petit)", "HeaderNewRecording": "Nouvel enregistrement", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "You Said...", "Help": "Aide", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Item saved.", @@ -674,9 +672,6 @@ "ViewAlbum": "View album", "ViewArtist": "View artist", "VoiceInput": "Voice Input", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "Mercredi", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/fr.json b/src/bower_components/emby-webcomponents/strings/fr.json index 15aa3315a2..53be76fc9c 100644 --- a/src/bower_components/emby-webcomponents/strings/fr.json +++ b/src/bower_components/emby-webcomponents/strings/fr.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "Autant que possible", "Ascending": "Croissant", "AspectRatio": "Ratio d'aspect original", - "AttemptingWakeServer": "Essai de réveil du serveur. Veuillez patienter...", "AttributeNew": "Nouveau", "AudioBitDepthNotSupported": "Profondeur des échantillons de l'audio non prise en charge", "AudioBitrateNotSupported": "Débit audio non pris en charge", @@ -53,7 +52,6 @@ "Books": "Livres", "Box": "Boîtier", "BoxRear": "Dos de boîtier", - "Browse": "Parcourir", "BurnSubtitlesHelp": "Détermine si le serveur doit graver les sous-titres lors de la conversion vidéo en fonction du format des sous-titres. Éviter la gravure des sous-titres améliorera les performances du serveur. Sélectionnez Auto pour graver les formats basés sur l'image (par exemple, VOBSUB, PGS, SUB/IDX etc) ainsi que certains sous-titres ASS/SSA", "ButtonCancel": "Annuler", "ButtonGotIt": "Compris", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Paramètres des métadonnées", "HeaderMusicQuality": "Qualité de la musique :", "HeaderMyDevice": "Cet appareil", - "HeaderMyDownloads": "Mes téléchargements", "HeaderMyMedia": "Mes Médias", "HeaderMyMediaSmall": "Mes médias (Petit)", "HeaderNewRecording": "Nouvel enregistrement", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Qualité vidéo", "HeaderVideoType": "Type de vidéo", "HeaderWaitingForWifi": "En attente du Wi-Fi", - "HeaderWakeServer": "Réveiller le serveur", "HeaderYouSaid": "Vous avez dit...", "Help": "Aide", "Hide": "Cacher", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Téléchargement mis en file d'attente.", "MessageFileReadError": "Une erreur est survenue lors de la lecture du fichier. Veuillez réessayer.", "MessageIfYouBlockedVoice": "Si vous avez supprimé l'accès par commande vocale à l'application, vous devrez le reconfigurer avant de réessayer.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "Un courriel a été envoyé à {0}, les invitant à s'inscrire à Jellyfin.", "MessageInvitationSentToUser": "Un courriel a été envoyé à {0} avec votre invitation de partage.", "MessageItemSaved": "Élément enregistré.", @@ -674,9 +672,6 @@ "ViewAlbum": "Voir l'album", "ViewArtist": "Voir l'artiste", "VoiceInput": "Entrée vocale", - "WakeServer": "Réveiller le serveur", - "WakeServerError": "Des paquets Wake-On-LAN on été envoyé à votre serveur, mais nous ne pouvons pas nous connecter à votre serveur Jellyfin. Votre machine a peut-être besoin de plus de temps pour se réveiller, ou le serveur Jellyfin n'est peut-être pas lancé sur la machine.", - "WakeServerSuccess": "Réussi !", "Watched": "Lu", "Wednesday": "Mercredi", "WifiRequiredToDownload": "Une connexion Wi-Fi est nécessaire pour continuer le téléchargement.", diff --git a/src/bower_components/emby-webcomponents/strings/gsw.json b/src/bower_components/emby-webcomponents/strings/gsw.json index d883b20325..37c3694ad5 100644 --- a/src/bower_components/emby-webcomponents/strings/gsw.json +++ b/src/bower_components/emby-webcomponents/strings/gsw.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "New", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Abbreche", "ButtonGotIt": "Got It", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metadata Settings", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "New Recording", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "You Said...", "Help": "Help", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Item saved.", @@ -674,9 +672,6 @@ "ViewAlbum": "View album", "ViewArtist": "View artist", "VoiceInput": "Voice Input", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "Mittwoch", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/he.json b/src/bower_components/emby-webcomponents/strings/he.json index cc9687d000..a7529722b4 100644 --- a/src/bower_components/emby-webcomponents/strings/he.json +++ b/src/bower_components/emby-webcomponents/strings/he.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "כמה שיותר", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "חדש", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "בטל", "ButtonGotIt": "הבנתי", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "הגדרות מטא נתונים", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "הקלטה חדשה", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "אתה אמרת...", "Help": "עזרה", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "הורד תור", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "אם מנעת גישה קולית לאפליקציה שתצטרך להגדיר מחדש לפני שתנסה שוב.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "הפריט נשמר.", @@ -674,9 +672,6 @@ "ViewAlbum": "צפה באלבום", "ViewArtist": "צפה באמן", "VoiceInput": "קלט קולי", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "רביעי", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/hr.json b/src/bower_components/emby-webcomponents/strings/hr.json index d937a114c8..b9d30e828c 100644 --- a/src/bower_components/emby-webcomponents/strings/hr.json +++ b/src/bower_components/emby-webcomponents/strings/hr.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "Što više je moguće", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "Novo", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Odustani", "ButtonGotIt": "Shvaćam", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Postavke meta-podataka", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "Nova snimka", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "Rekao si...", "Help": "Pomoć", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Preuzimanje na čekanju", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "Ako ste zabranili glasovni pristup aplikaciji morate ponovo podesiti prije ponovnog pokušaja.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Stavka je snimljena.", @@ -674,9 +672,6 @@ "ViewAlbum": "Pogledaj album", "ViewArtist": "Pogledaj umjetnika", "VoiceInput": "Ulazni glas", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "Srijeda", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/hu.json b/src/bower_components/emby-webcomponents/strings/hu.json index a8155814b2..c4ea5df5c0 100644 --- a/src/bower_components/emby-webcomponents/strings/hu.json +++ b/src/bower_components/emby-webcomponents/strings/hu.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Növekvő", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "A kiszolgáló felébresztése folyamatban. Kérlek várj...", "AttributeNew": "Új", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Könyvek", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Mégsem", "ButtonGotIt": "Értettem", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metaadat Beállítások", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "Jelenlegi eszköz", - "HeaderMyDownloads": "Letöltések", "HeaderMyMedia": "Médiatáram", "HeaderMyMediaSmall": "Médiatáram (kicsi)", "HeaderNewRecording": "Új Felvétel", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Videó típusa:", "HeaderWaitingForWifi": "Wifi-re vár", - "HeaderWakeServer": "Kiszolgáló felébresztés", "HeaderYouSaid": "You Said...", "Help": "Segítség", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Item saved.", @@ -674,9 +672,6 @@ "ViewAlbum": "View album", "ViewArtist": "View artist", "VoiceInput": "Voice Input", - "WakeServer": "Kiszolgáló felébresztés", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Megtekintett", "Wednesday": "Szerda", "WifiRequiredToDownload": "Wifi kapcsolat szükséges a letöltés folytatásához.", diff --git a/src/bower_components/emby-webcomponents/strings/id.json b/src/bower_components/emby-webcomponents/strings/id.json index 63bb57ab91..9dc97a0b36 100644 --- a/src/bower_components/emby-webcomponents/strings/id.json +++ b/src/bower_components/emby-webcomponents/strings/id.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "New", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Cancel", "ButtonGotIt": "Got It", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metadata Settings", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "New Recording", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "You Said...", "Help": "Help", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Item saved.", @@ -674,9 +672,6 @@ "ViewAlbum": "View album", "ViewArtist": "View artist", "VoiceInput": "Voice Input", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "Wednesday", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/it.json b/src/bower_components/emby-webcomponents/strings/it.json index 2e4e8823a3..8842e91080 100644 --- a/src/bower_components/emby-webcomponents/strings/it.json +++ b/src/bower_components/emby-webcomponents/strings/it.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "Tutto il possibile", "Ascending": "Crescente", "AspectRatio": "Rapporto d'aspetto", - "AttemptingWakeServer": "Tentando di svegliare il server. Per piacere aspetta...", "AttributeNew": "Nuovo", "AudioBitDepthNotSupported": "La profondità bit audio non è supportata", "AudioBitrateNotSupported": "Bitrate audio non supportato", @@ -53,7 +52,6 @@ "Books": "Libri", "Box": "Box", "BoxRear": "Box (retro)", - "Browse": "Esplora", "BurnSubtitlesHelp": "Determina se il server deve applicare i sottotitoli quando si converte i video in base al formato dei sottotitoli. Evitando di applicare i sottotitoli migliorerà le prestazioni del server. Selezionare Auto per applicare formati basati sull'immagine (ad esempio VOBSUB, PGS, SUB / IDX, ecc.) così come alcuni sottotitoli ASS / SSA", "ButtonCancel": "Annulla", "ButtonGotIt": "Ho capito", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Impostazioni Metadati", "HeaderMusicQuality": "Qualità Musica", "HeaderMyDevice": "Il Mio Dispositivo", - "HeaderMyDownloads": "I Miei Download", "HeaderMyMedia": "I miei media", "HeaderMyMediaSmall": "I miei media (piccolo)", "HeaderNewRecording": "Nuova Registrazione", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Qualità Video", "HeaderVideoType": "Tipo di Video", "HeaderWaitingForWifi": "In attesa di Wifi", - "HeaderWakeServer": "Sveglia il server", "HeaderYouSaid": "Hai detto...", "Help": "Aiuto", "Hide": "Nascondi", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Scaricamento programmato.", "MessageFileReadError": "Si è verificato un errore durante la lettura del file. Si prega di riprovare.", "MessageIfYouBlockedVoice": "Se hai negato l'accesso vocale all'app dovrai riconfigurarlo prima di riprovare di nuovo.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "Un'email è stata inviata a {0} invitandolo a registrarsi a Jellyfin", "MessageInvitationSentToUser": "Una e-mail è stata inviata a {0}, invitandoli ad accettare l'invito di condivisione.", "MessageItemSaved": "Elemento salvato.", @@ -674,9 +672,6 @@ "ViewAlbum": "Visualizza album", "ViewArtist": "Visualizza artista", "VoiceInput": "Comandi Vocali", - "WakeServer": "Sveglia il server", - "WakeServerError": "I pacchetti Wake On LAN sono stati inviati al computer server, ma non siamo in grado di connettersi al server Jellyfin. Potrebbe essere necessario un po 'più di tempo per riattivare la macchina, oppure il server Jellyfin potrebbe non essere attivo sulla macchina.", - "WakeServerSuccess": "Successo!", "Watched": "Visto", "Wednesday": "Mercoledì", "WifiRequiredToDownload": "Una connessione Wifi è richiesta per continuare il download", diff --git a/src/bower_components/emby-webcomponents/strings/kk.json b/src/bower_components/emby-webcomponents/strings/kk.json index 2f45becfc9..0c21b9e8c9 100644 --- a/src/bower_components/emby-webcomponents/strings/kk.json +++ b/src/bower_components/emby-webcomponents/strings/kk.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "Мүмкіндігінше көп", "Ascending": "Артуы бойынша", "AspectRatio": "Пішімдік арақатынасы", - "AttemptingWakeServer": "Серверді ояту әрекеті жасалуда. Күте тұрыңыз...", "AttributeNew": "Жаңа", "AudioBitDepthNotSupported": "Дыбыстың биттік тереңдігі үшін қолдау көрсетілмейді", "AudioBitrateNotSupported": "Дыбыс қарқыны үшін қолдау көрсетілмейді", @@ -53,7 +52,6 @@ "Books": "Кітаптар", "Box": "Қорап", "BoxRear": "Қорап арты", - "Browse": "Шарлау", "BurnSubtitlesHelp": "Субтитрлер пішіміне байланысты бейнені түрлендірген кезде сервер субтитрлерді жазыуын анықтайды. Субтитрлер жазуды қашқақтау сервердің өнімділігін жақсартады. Суретке негізделген пішімдерді (мысалы, VOBSUB, PGS, SUB/IDX ж.т.б.), сондай-ақ кейбір ASS/SSA субтитрлерін жазу үшін Автоматтыны таңдаңыз", "ButtonCancel": "Болдырмау", "ButtonGotIt": "Түсінікті", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Метадеректер параметрлері", "HeaderMusicQuality": "Музыка сапасы", "HeaderMyDevice": "Менің құрылғым", - "HeaderMyDownloads": "Менің жүктеулерім", "HeaderMyMedia": "Менің тасығышдеректерім", "HeaderMyMediaSmall": "Менің тасығышдеректерім (ықшам)", "HeaderNewRecording": "Жаңа жазба", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Бейне сапасы", "HeaderVideoType": "Бейне түрі", "HeaderWaitingForWifi": "WiFi үшін күтуде", - "HeaderWakeServer": "Серверді ояту", "HeaderYouSaid": "Сіз айтқаныңыз...", "Help": "Интернеттегі анықтамаға", "Hide": "Жасыру", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Жүктеп алу кезекте.", "MessageFileReadError": "Файл оқу кезінде қате орын алды. Әрекетті кейін қайталаңыз.", "MessageIfYouBlockedVoice": "Егер қолданбаға дауыстық қатынаудан бас тартсаңыз, қайта әрекеттенуіңізден алдынан қайта теңшеуіңіз қажет болады.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "Jellyfin үшін тіркелу шақыруыңыз, э-пошта {0} үшін жіберілді.", "MessageInvitationSentToUser": "Оларға ортақтасу шақыруыңызды қабылдау ұсынысымен, э-пошта {0} арнап жіберілді.", "MessageItemSaved": "Тармақ сақталды.", @@ -674,9 +672,6 @@ "ViewAlbum": "Альбомды қарау", "ViewArtist": "Орындаушыны қарау", "VoiceInput": "Дауыстық енгізу", - "WakeServer": "Серверді ояту", - "WakeServerError": "Wake On LAN пакеттері сіздің серверіңізге жіберілді, бірақ сіздің Jellyfin Server үшін қосыла алмаймыз. Құрылғыңызды ояту үшін біраз уақыт қажет болуы мүмкін немесе Jellyfin Server құрылғыда белсенді жұмыс істемеуі мүмкін.", - "WakeServerSuccess": "Сәттілік!", "Watched": "Қаралған", "Wednesday": "сәрсенбі", "WifiRequiredToDownload": "Жүктеп алуды жалғастыру үшін WiFi қосылымы қажет.", diff --git a/src/bower_components/emby-webcomponents/strings/ko.json b/src/bower_components/emby-webcomponents/strings/ko.json index 030586a94f..ce76774d9a 100644 --- a/src/bower_components/emby-webcomponents/strings/ko.json +++ b/src/bower_components/emby-webcomponents/strings/ko.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "신규", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "취소", "ButtonGotIt": "그럴게요.", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "메타데이터 설정", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "내 미디어 (작음)", "HeaderNewRecording": "신규 녹화", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "You Said...", "Help": "도움말", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "항목이 저장되었습니다.", @@ -674,9 +672,6 @@ "ViewAlbum": "앨범 보기", "ViewArtist": "아티스트 보기", "VoiceInput": "Voice Input", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "시청함", "Wednesday": "수요일", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/lt-lt.json b/src/bower_components/emby-webcomponents/strings/lt-lt.json index 64c5fc8f43..895d52f0c8 100644 --- a/src/bower_components/emby-webcomponents/strings/lt-lt.json +++ b/src/bower_components/emby-webcomponents/strings/lt-lt.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "Kiek tik įmanoma", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "Naujas", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Atšaukti", "ButtonGotIt": "Supratau", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metaduomenų nustatymai", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "Naujas įrašas", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "Jūs pasakėte:", "Help": "Padėti", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Siuntimas užsakytas.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "Jei neleidote programėlei naudoti mikrofono, pakeiskite nustatymus ir bandykite dar kartą.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Elementas išsaugotas.", @@ -674,9 +672,6 @@ "ViewAlbum": "Žiūrėti albumą", "ViewArtist": "Žiūrėti atlikėją", "VoiceInput": "Balso komandos", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "Trečiadienis", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/ms.json b/src/bower_components/emby-webcomponents/strings/ms.json index 94b5242f8d..fe299fa836 100644 --- a/src/bower_components/emby-webcomponents/strings/ms.json +++ b/src/bower_components/emby-webcomponents/strings/ms.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "New", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Cancel", "ButtonGotIt": "Got It", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metadata Settings", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "New Recording", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "You Said...", "Help": "Help", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Item saved.", @@ -674,9 +672,6 @@ "ViewAlbum": "View album", "ViewArtist": "View artist", "VoiceInput": "Voice Input", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "Wednesday", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/nb.json b/src/bower_components/emby-webcomponents/strings/nb.json index a80683775b..7dc2a640f2 100644 --- a/src/bower_components/emby-webcomponents/strings/nb.json +++ b/src/bower_components/emby-webcomponents/strings/nb.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "Så mange som mulig", "Ascending": "Ascending", "AspectRatio": "Størrelsesforholdet", - "AttemptingWakeServer": "Prøver å vekke opp server. Vennligst vent,..", "AttributeNew": "Ny", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Bøker", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Avbryt", "ButtonGotIt": "Skjønner", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metadata innstilinger", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "Min enhet", - "HeaderMyDownloads": "Mine nedlastinger", "HeaderMyMedia": "Min Media", "HeaderMyMediaSmall": "Min Media (liten)", "HeaderNewRecording": "Nye opptak:", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Vekk opp server", "HeaderYouSaid": "Du sa...", "Help": "Hjelp", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Nedlasting satt til i kø", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "Hvis du nektet tale tilgang til applikasjonen må du rekonfigurere før du prøver igjen.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Element lagret.", @@ -674,9 +672,6 @@ "ViewAlbum": "Vis album", "ViewArtist": "Vis artist", "VoiceInput": "Stemme input", - "WakeServer": "Vekk opp server", - "WakeServerError": "Wake On LAN-pakker ble sendt til servermaskinen din, men tilkobling til din Jellyfin Server mislyktes. Serveren din kan trenge litt mer tid til å våkne, eller så kjører ikke Jellyfin Server på maskinen.", - "WakeServerSuccess": "Suksess!", "Watched": "Sett", "Wednesday": "Onsdag", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/nl.json b/src/bower_components/emby-webcomponents/strings/nl.json index 5e9dd29b9c..5419b5d2b6 100644 --- a/src/bower_components/emby-webcomponents/strings/nl.json +++ b/src/bower_components/emby-webcomponents/strings/nl.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "Zo veel als mogelijk", "Ascending": "Oplopend", "AspectRatio": "Beeldverhouding", - "AttemptingWakeServer": "Proberen de server te wekken. Een moment geduld...", "AttributeNew": "Nieuw", "AudioBitDepthNotSupported": "Audio bit depth niet ondersteund", "AudioBitrateNotSupported": "Audio bitrate niet ondersteund", @@ -53,7 +52,6 @@ "Books": "Boeken", "Box": "Box", "BoxRear": "Box (achterkant)", - "Browse": "Bladeren", "BurnSubtitlesHelp": "Bepaalt of de server ondertitels moet inbranden wanneer video's op basis van het ondertitel formaat geconverteerd moeten worden. Inbranden van ondertitels hebben een negatief effect op de server performance. Selecteer Automatisch om op afbeelding gebaseerde formaten (bijv. VOBSUB, PGS, SUB/IDX etc.) en bepaalde ASS/SSA ondertitels in te branden.", "ButtonCancel": "Annuleren", "ButtonGotIt": "Begrepen", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metagegevens instellingen", "HeaderMusicQuality": "Muziek Kwaliteit", "HeaderMyDevice": "Mijn Apparaat", - "HeaderMyDownloads": "Mijn Downloads", "HeaderMyMedia": "Mijn Media", "HeaderMyMediaSmall": "Mijn Media (klein)", "HeaderNewRecording": "Nieuwe opname", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Kwaliteit", "HeaderVideoType": "Videotype", "HeaderWaitingForWifi": "Wachten op Wifi", - "HeaderWakeServer": "Server Wekken", "HeaderYouSaid": "U zei...", "Help": "Hulp", "Hide": "Verbergen", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download in de wachtrij geplaatst.", "MessageFileReadError": "Er is een fout opgetreden bij het lezen van het bestand. Probeer het opnieuw.", "MessageIfYouBlockedVoice": "Als u spraak toegang uitgeschakeld heeft moet u dit opnieuw configureren voordat u verder gaat.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "Een email is verzonden naar {0} met een uitnodiging om aan te melden bij Jellyfin.", "MessageInvitationSentToUser": "Een email is verzonden naar {0} met een uitnodiging om uw uitnodiging te accepteren.", "MessageItemSaved": "Item opgeslagen.", @@ -674,9 +672,6 @@ "ViewAlbum": "Bekijk album", "ViewArtist": "Bekijk artiest", "VoiceInput": "Spraak invoer", - "WakeServer": "Server wekken", - "WakeServerError": "Er zijn \"Wake On Lan\" pakketten naar de server verzonden, maar verbinding maken is mislukt. Het kan voorkomen dat de server wat meer tijd nodig heeft om op te starten, of misschien is Jellyfin Server niet actief op de machine.", - "WakeServerSuccess": "Succesvol!", "Watched": "Bekeken", "Wednesday": "Woensdag", "WifiRequiredToDownload": "Wifi verbinding is vereist om te downloaden.", diff --git a/src/bower_components/emby-webcomponents/strings/pl.json b/src/bower_components/emby-webcomponents/strings/pl.json index 79d40c530a..f031762a05 100644 --- a/src/bower_components/emby-webcomponents/strings/pl.json +++ b/src/bower_components/emby-webcomponents/strings/pl.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "Tak wiele jak to możliwe", "Ascending": "Rosnąco", "AspectRatio": "Proporcje obrazu", - "AttemptingWakeServer": "Trwa próba wybudzenia serwera. Proszę czekać...", "AttributeNew": "Nowy", "AudioBitDepthNotSupported": "Nieobsługiwana głębia bitowa dźwięku", "AudioBitrateNotSupported": "Nieobsługiwana przepływność dźwięku", @@ -53,7 +52,6 @@ "Books": "Książki", "Box": "Pudełko", "BoxRear": "Pudełko (tył)", - "Browse": "Przeglądaj", "BurnSubtitlesHelp": "Określa czy serwer powinien wypalać napisy podczas konwersji wideo, w zależności od formatu napisów. Unikanie wypalania napisów poprawia wydajność serwera. Wybierz Automatycznie, w celu wypalania zarówno napisów w formatach graficznych (np. VOBSUB, PGS, SUB/IDX, itp.), jak i pewnych napisów ASS/SSA.", "ButtonCancel": "Anuluj", "ButtonGotIt": "Rozumiem", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Ustawienia metadanych", "HeaderMusicQuality": "Jakość muzyki", "HeaderMyDevice": "Moje urządzenie", - "HeaderMyDownloads": "Moje pobrania", "HeaderMyMedia": "Moje media", "HeaderMyMediaSmall": "Moje media (małe)", "HeaderNewRecording": "Nowe nagranie", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Jakość wideo", "HeaderVideoType": "Typ wideo", "HeaderWaitingForWifi": "Oczekiwanie na sieć WiFi", - "HeaderWakeServer": "Wybudzaj serwer", "HeaderYouSaid": "Powiedziałeś...", "Help": "Pomoc", "Hide": "Ukryj", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Dodano do kolejki pobierania.", "MessageFileReadError": "Podczas wczytywania plików wystąpił błąd. Spróbuj ponownie później.", "MessageIfYouBlockedVoice": "Jeśli odmówisz aplikacji dostępu głosowego, będziesz musiał zmienić konfigurację przed ponownym urządzeniem.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "Wiadomość pocztowa, z prośbą o rejestrację konta Jellyfin, została wysłana do {0}.", "MessageInvitationSentToUser": "Wiadomość pocztowa, z prośbą o akceptację zaproszenia współużytkowania, została wysłana do {0}.", "MessageItemSaved": "Obiekt zapisany.", @@ -674,9 +672,6 @@ "ViewAlbum": "Podgląd albumu", "ViewArtist": "Podgląd wykonawcy", "VoiceInput": "Wejście głosowe", - "WakeServer": "Wybudzaj serwer", - "WakeServerError": "Wysłano pakiery Wake On LAN do maszyny serwera, ale połączenie z serwer Jellyfin zakończyło się niepowodzeniem. Twoja maszyna potrzebuje więcej czasu do wybudzenia lub serwer Jellyfin może nie działać aktywnie na tej maszynie.", - "WakeServerSuccess": "Powodzenie!", "Watched": "Obejrzany", "Wednesday": "Środa", "WifiRequiredToDownload": "Połączenie WiFi jest wymagane, aby kontynuować pobieranie.", diff --git a/src/bower_components/emby-webcomponents/strings/pt-br.json b/src/bower_components/emby-webcomponents/strings/pt-br.json index 25a47b1648..42c0916ff2 100644 --- a/src/bower_components/emby-webcomponents/strings/pt-br.json +++ b/src/bower_components/emby-webcomponents/strings/pt-br.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "Quantos forem possíveis", "Ascending": "Ascendente", "AspectRatio": "Proporção da imagem", - "AttemptingWakeServer": "Tentando despertar o servidor. Por favor, aguarde...", "AttributeNew": "Novo", "AudioBitDepthNotSupported": "Profundidade de bit de áudio não suportada", "AudioBitrateNotSupported": "Taxa de áudio não suportada", @@ -53,7 +52,6 @@ "Books": "Livros", "Box": "Caixa", "BoxRear": "Caixa (traseira)", - "Browse": "Navegar", "BurnSubtitlesHelp": "Determina se o servidor deveria gravar as legendas no vídeo ao convertê-lo, dependendo do formato da legenda. Evitar a gravação da legenda irá melhorar a performance do servidor. Selecione Auto para gravar a imagem baseado nos formatos (ex. VOBSUB, PGS, SUB/IDX, etc.) assim como algumas legendas ASS/SSA.", "ButtonCancel": "Cancelar", "ButtonGotIt": "Feito", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Ajustes dos Metadados", "HeaderMusicQuality": "Qualidade da Música:", "HeaderMyDevice": "Meu Dispositivo", - "HeaderMyDownloads": "Meus Downloads", "HeaderMyMedia": "Minha Mídia", "HeaderMyMediaSmall": "Minha Mídia (pequeno)", "HeaderNewRecording": "Nova Gravação", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Qualidade do Vídeo", "HeaderVideoType": "Tipo de Vídeo", "HeaderWaitingForWifi": "Esperando por Wifi", - "HeaderWakeServer": "Despertar Servidor", "HeaderYouSaid": "Você Disse...", "Help": "Ajuda", "Hide": "Ocultar", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download enfileirado.", "MessageFileReadError": "Ocorreu um erro ao ler o arquivo. Por favor, tente novamente.", "MessageIfYouBlockedVoice": "Se você negou o acesso de voz ao app, você necessitará reconfigurar antes de tentar novamente.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "Um email foi enviado para {0}, convidando-os a se registrarem no Jellyfin.", "MessageInvitationSentToUser": "Um email foi enviado para {0}, convidando-os para aceitar seu convite.", "MessageItemSaved": "Item salvo.", @@ -674,9 +672,6 @@ "ViewAlbum": "Ver álbum", "ViewArtist": "Ver artista", "VoiceInput": "Entrada de voz", - "WakeServer": "Acordar servidor", - "WakeServerError": "Pacotes de rede para despertar foram enviados para seu servidor, mas não foi possível conectar ao seu Servidor Jellyfin. Sua máquina pode necessitar um pouco mais de tempo para despertar, ou o Servidor Jellyfin pode não estar rodando na máquina.", - "WakeServerSuccess": "Deu certo!", "Watched": "Assistido(s)", "Wednesday": "Quarta-feira", "WifiRequiredToDownload": "É necessária uma conexão Wifi para continuar a transferir.", diff --git a/src/bower_components/emby-webcomponents/strings/pt-pt.json b/src/bower_components/emby-webcomponents/strings/pt-pt.json index e2db9bbfba..980ddbe503 100644 --- a/src/bower_components/emby-webcomponents/strings/pt-pt.json +++ b/src/bower_components/emby-webcomponents/strings/pt-pt.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "Novo", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Cancelar", "ButtonGotIt": "Got It", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Ajustes dos Metadados", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "Nova Gravação", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "You Said...", "Help": "Ajuda", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Item salvo.", @@ -674,9 +672,6 @@ "ViewAlbum": "View album", "ViewArtist": "View artist", "VoiceInput": "Voice Input", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "Quarta", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/ro.json b/src/bower_components/emby-webcomponents/strings/ro.json index 21b12ec4e6..d84b9c7325 100644 --- a/src/bower_components/emby-webcomponents/strings/ro.json +++ b/src/bower_components/emby-webcomponents/strings/ro.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "New", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Anuleaza", "ButtonGotIt": "Got It", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metadata Settings", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "New Recording", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "You Said...", "Help": "Ajutor", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Item saved.", @@ -674,9 +672,6 @@ "ViewAlbum": "View album", "ViewArtist": "View artist", "VoiceInput": "Voice Input", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "Miercuri", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/ru.json b/src/bower_components/emby-webcomponents/strings/ru.json index 2dd6c9e803..37575ec9c6 100644 --- a/src/bower_components/emby-webcomponents/strings/ru.json +++ b/src/bower_components/emby-webcomponents/strings/ru.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "Как можно больше", "Ascending": "По возрастанию", "AspectRatio": "Соот-ие сторон", - "AttemptingWakeServer": "Идёт попытка разбудить ваш сервер. Ждите...", "AttributeNew": "Новинка", "AudioBitDepthNotSupported": "Разрядность аудио не поддерживается", "AudioBitrateNotSupported": "Потоковая скорость аудио не поддерживается", @@ -53,7 +52,6 @@ "Books": "Книги", "Box": "Коробка", "BoxRear": "Спинка коробки", - "Browse": "Навигация", "BurnSubtitlesHelp": "Определяется, должен ли сервер внедрять субтитры при преобразовании видео в зависимости от формата субтитров. Избегание внедрения субтитров улучшит производительность сервера. Выберите «Авто» для записи основанных на графике форматов (например, VOBSUB, PGS, SUB/IDX и т.п.), а также некоторых субтитров ASS/SSA.", "ButtonCancel": "Отменить", "ButtonGotIt": "Понятно", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Параметры метаданных", "HeaderMusicQuality": "Качество музыки", "HeaderMyDevice": "Моё устройство", - "HeaderMyDownloads": "Мои загрузки", "HeaderMyMedia": "Мои медиаданные", "HeaderMyMediaSmall": "Мои медиаданные (компактно)", "HeaderNewRecording": "Новая запись", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Качество видео", "HeaderVideoType": "Тип видео", "HeaderWaitingForWifi": "В ожидании WiFi", - "HeaderWakeServer": "Пробуждение сервера", "HeaderYouSaid": "Вы сказали...", "Help": "Справка...", "Hide": "Скрыть", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Загрузка в очереди.", "MessageFileReadError": "Произошла ошибка при считывании файла. Повторите попытку позже.", "MessageIfYouBlockedVoice": "Если отказано в голосовом доступе к приложению, перед новой попыткой вам необходимо переконфигурирование.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "Письмо была отправлена к {0}, с предложением зарегистрироваться в Jellyfin.", "MessageInvitationSentToUser": "Письмо было отправлено к {0}, с предложением принять ваше приглашение на совместный доступ.", "MessageItemSaved": "Элемент сохранён.", @@ -674,9 +672,6 @@ "ViewAlbum": "Посмотреть альбом", "ViewArtist": "Посмотреть исполнителя", "VoiceInput": "Голосовой ввод", - "WakeServer": "Разбудить сервер", - "WakeServerError": "Пакеты Wake On LAN были отправлены на вашу серверную машину, однако, мы не смогли соединиться с Jellyfin Server. Возможно, вашей машине потребуется немного больше времени для пробуждения, или Jellyfin Server не может активно работать на данной машине.", - "WakeServerSuccess": "Успешно!", "Watched": "Просмотрено", "Wednesday": "среда", "WifiRequiredToDownload": "WiFi-соединение требуется для продолжения загрузки.", diff --git a/src/bower_components/emby-webcomponents/strings/sk.json b/src/bower_components/emby-webcomponents/strings/sk.json index 8004ee2e93..803c1d5250 100644 --- a/src/bower_components/emby-webcomponents/strings/sk.json +++ b/src/bower_components/emby-webcomponents/strings/sk.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "Najviac ako je možné", "Ascending": "Vzostupne", "AspectRatio": "Pomer strán", - "AttemptingWakeServer": "Pokúšam sa zobudiť server. Prosím počkajte...", "AttributeNew": "Nové", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Knihy", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Zrušiť", "ButtonGotIt": "Rozumiem", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Nastavenia metadát", "HeaderMusicQuality": "Kvalita hudby", "HeaderMyDevice": "Moje zariadenie", - "HeaderMyDownloads": "Moje sťahovania", "HeaderMyMedia": "Moje média", "HeaderMyMediaSmall": "Moje médiá (malé)", "HeaderNewRecording": "New Recording", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Kvalita videa", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Čakám na WiFi", - "HeaderWakeServer": "Zobudiť server", "HeaderYouSaid": "You Said...", "Help": "Pomoc", "Hide": "Skryť", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Položka uložená.", @@ -674,9 +672,6 @@ "ViewAlbum": "Zobraziť album", "ViewArtist": "Zobraziť umelca", "VoiceInput": "Hlasový vstup", - "WakeServer": "Zobudiť server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Vyšlo to!", "Watched": "Watched", "Wednesday": "Streda", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/sl-si.json b/src/bower_components/emby-webcomponents/strings/sl-si.json index fb0e32900b..5b96b3ed82 100644 --- a/src/bower_components/emby-webcomponents/strings/sl-si.json +++ b/src/bower_components/emby-webcomponents/strings/sl-si.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "New", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Cancel", "ButtonGotIt": "Got It", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metadata Settings", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "New Recording", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "You Said...", "Help": "Help", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Item saved.", @@ -674,9 +672,6 @@ "ViewAlbum": "View album", "ViewArtist": "View artist", "VoiceInput": "Voice Input", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "Wednesday", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/sv.json b/src/bower_components/emby-webcomponents/strings/sv.json index a465d4826d..ee158a65fb 100644 --- a/src/bower_components/emby-webcomponents/strings/sv.json +++ b/src/bower_components/emby-webcomponents/strings/sv.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "Så många som möjligt", "Ascending": "Ascending", "AspectRatio": "Bildförhållande", - "AttemptingWakeServer": "Försöker väcka servern. Vänligen vänta....", "AttributeNew": "Ny", "AudioBitDepthNotSupported": "Ljudets bitdjup stöds inte", "AudioBitrateNotSupported": "Ljudbithastigheten stöds inte", @@ -53,7 +52,6 @@ "Books": "Böcker", "Box": "Omslag", "BoxRear": "Omslag (baksida)", - "Browse": "Bläddra", "BurnSubtitlesHelp": "Avgör ifall servern ska \"bränna in\" undertexterna under videokonverteringen, beroende på undertextsformatet. Att undvika inbränning av undertexter kommer att förbättra prestandan på servern. Välj Auto för att bränna image-baserade formats (ex. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA undertexter.", "ButtonCancel": "Avbryt", "ButtonGotIt": "Ok", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metadatainställningar", "HeaderMusicQuality": "Musikkvalitet:", "HeaderMyDevice": "Min enhet", - "HeaderMyDownloads": "Mina nedladdningar", "HeaderMyMedia": "Min Media", "HeaderMyMediaSmall": "Min Media (liten)", "HeaderNewRecording": "Ny inspelning", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Videokvalitet", "HeaderVideoType": "Videotyp", "HeaderWaitingForWifi": "Väntar på Wifi", - "HeaderWakeServer": "Väck Server", "HeaderYouSaid": "Du sa...", "Help": "Hjälp", "Hide": "Dölj", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Nedladdning köad.", "MessageFileReadError": "Ett fel uppstod när filen lästes in. Var god försök igen.", "MessageIfYouBlockedVoice": "Om du nekade tillgång för röståtkomst till appen så behöver du konfigurerara om innan du försöker igen.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "Ett mail har skickats till {0} med en inbjudan att registrera sig med Jellyfin.", "MessageInvitationSentToUser": "Ett mail har skickats till {0}, med en bekfräftelse för att acceptera din delade inbjudan.", "MessageItemSaved": "Objektet har sparats.", @@ -674,9 +672,6 @@ "ViewAlbum": "Bläddra album", "ViewArtist": "Bläddra artist", "VoiceInput": "Röstinspelning", - "WakeServer": "Väck server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Lyckades!", "Watched": "Sedd", "Wednesday": "Onsdag", "WifiRequiredToDownload": "En Wifi-anslutning krävs för att fortsätta nedladdningen.", diff --git a/src/bower_components/emby-webcomponents/strings/tr.json b/src/bower_components/emby-webcomponents/strings/tr.json index 5a4ff9a00c..f224047293 100644 --- a/src/bower_components/emby-webcomponents/strings/tr.json +++ b/src/bower_components/emby-webcomponents/strings/tr.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "Yeni", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "İptal", "ButtonGotIt": "Got It", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metadata Settings", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "New Recording", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "You Said...", "Help": "Help", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Item saved.", @@ -674,9 +672,6 @@ "ViewAlbum": "View album", "ViewArtist": "View artist", "VoiceInput": "Voice Input", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "Çarşamba", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/uk.json b/src/bower_components/emby-webcomponents/strings/uk.json index dc598bb6bd..5e64cf97fe 100644 --- a/src/bower_components/emby-webcomponents/strings/uk.json +++ b/src/bower_components/emby-webcomponents/strings/uk.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "New", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Скасувати", "ButtonGotIt": "Got It", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metadata Settings", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "New Recording", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "You Said...", "Help": "Help", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Item saved.", @@ -674,9 +672,6 @@ "ViewAlbum": "View album", "ViewArtist": "View artist", "VoiceInput": "Voice Input", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "Wednesday", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/vi.json b/src/bower_components/emby-webcomponents/strings/vi.json index c695aed76f..2db6e383b8 100644 --- a/src/bower_components/emby-webcomponents/strings/vi.json +++ b/src/bower_components/emby-webcomponents/strings/vi.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "New", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "Thoát", "ButtonGotIt": "Got It", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metadata Settings", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "New Recording", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "You Said...", "Help": "Help", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Item saved.", @@ -674,9 +672,6 @@ "ViewAlbum": "View album", "ViewArtist": "View artist", "VoiceInput": "Voice Input", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "Wednesday", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/zh-cn.json b/src/bower_components/emby-webcomponents/strings/zh-cn.json index aae1569228..fe7efc1949 100644 --- a/src/bower_components/emby-webcomponents/strings/zh-cn.json +++ b/src/bower_components/emby-webcomponents/strings/zh-cn.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "尽可能多", "Ascending": "升序", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "尝试唤醒服务器中,请耐心等待...", "AttributeNew": "新增", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "音频比特率不受支持", @@ -53,7 +52,6 @@ "Books": "书籍", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "浏览", "BurnSubtitlesHelp": "根据字幕格式确定服务器在转换视频时是否应烧录字幕。避免烧录字幕会提高服务器性能。选择“自动”以烧录基于图像的字幕格式(如 VOBSUB, PGS, SUB/IDX 等)和一些复杂的 ASS/SSA 字幕。", "ButtonCancel": "取消", "ButtonGotIt": "知道了", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "元数据设置", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "我的设备", - "HeaderMyDownloads": "我的下载", "HeaderMyMedia": "我的媒体", "HeaderMyMediaSmall": "我的媒体 (小)", "HeaderNewRecording": "新录制", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "视频质量", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "等待 Wifi 连接", - "HeaderWakeServer": "唤醒服务器", "HeaderYouSaid": "您说了...", "Help": "帮助", "Hide": "隐藏", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "下载已列队。", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "项目已保存。", @@ -674,9 +672,6 @@ "ViewAlbum": "查看专辑", "ViewArtist": "查看艺术家", "VoiceInput": "语音输入", - "WakeServer": "唤醒服务器", - "WakeServerError": "Wake On LAN 数据包已经发送到你的服务器所在机器上,但我们不能连接到你的 Jellyfin 服务器。你的机器可能还需要一些时间才能被唤醒,或者\n Jellyfin 服务器没有正确的在机器上启动。", - "WakeServerSuccess": "成功!", "Watched": "已观看", "Wednesday": "星期三", "WifiRequiredToDownload": "需要连接 Wifi 才能继续下载。", diff --git a/src/bower_components/emby-webcomponents/strings/zh-hk.json b/src/bower_components/emby-webcomponents/strings/zh-hk.json index c5b3c0dddb..8cd9950b82 100644 --- a/src/bower_components/emby-webcomponents/strings/zh-hk.json +++ b/src/bower_components/emby-webcomponents/strings/zh-hk.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "New", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "取消", "ButtonGotIt": "Got It", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metadata Settings", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "New Recording", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "You Said...", "Help": "幫助", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "Download queued.", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Item saved.", @@ -674,9 +672,6 @@ "ViewAlbum": "View album", "ViewArtist": "View artist", "VoiceInput": "Voice Input", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "星期三", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/strings/zh-tw.json b/src/bower_components/emby-webcomponents/strings/zh-tw.json index f526048dfc..41a83e0908 100644 --- a/src/bower_components/emby-webcomponents/strings/zh-tw.json +++ b/src/bower_components/emby-webcomponents/strings/zh-tw.json @@ -31,7 +31,6 @@ "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", "AspectRatio": "Aspect ratio", - "AttemptingWakeServer": "Attempting to wake server. Please wait...", "AttributeNew": "New", "AudioBitDepthNotSupported": "Audio bit depth not supported", "AudioBitrateNotSupported": "Audio bitrate not supported", @@ -53,7 +52,6 @@ "Books": "Books", "Box": "Box", "BoxRear": "Box (rear)", - "Browse": "Browse", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitles format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (e.g. VOBSUB, PGS, SUB/IDX, etc.) as well as certain ASS/SSA subtitles", "ButtonCancel": "取消", "ButtonGotIt": "我知道了", @@ -248,7 +246,6 @@ "HeaderMetadataSettings": "Metadata Settings", "HeaderMusicQuality": "Music Quality", "HeaderMyDevice": "My Device", - "HeaderMyDownloads": "My Downloads", "HeaderMyMedia": "My Media", "HeaderMyMediaSmall": "My Media (small)", "HeaderNewRecording": "新錄製", @@ -283,7 +280,6 @@ "HeaderVideoQuality": "Video Quality", "HeaderVideoType": "Video Type", "HeaderWaitingForWifi": "Waiting for Wifi", - "HeaderWakeServer": "Wake Server", "HeaderYouSaid": "您是指...", "Help": "說明", "Hide": "Hide", @@ -438,6 +434,8 @@ "MessageDownloadQueued": "需要下載", "MessageFileReadError": "There was an error reading the file. Please try again.", "MessageIfYouBlockedVoice": "如果您拒絕程式使用語音辨識,您將需要在重試之前再次設定", + "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", + "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Jellyfin.", "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", "MessageItemSaved": "Item saved.", @@ -674,9 +672,6 @@ "ViewAlbum": "View album", "ViewArtist": "View artist", "VoiceInput": "Voice Input", - "WakeServer": "Wake server", - "WakeServerError": "Wake On LAN packets were sent to your server machine, but we're unable to connect to your Jellyfin Server. Your machine may need a little more time to wake, or Jellyfin Server may not be actively running on the machine.", - "WakeServerSuccess": "Success!", "Watched": "Watched", "Wednesday": "星期三", "WifiRequiredToDownload": "A Wifi connection is required to continue downloading.", diff --git a/src/bower_components/emby-webcomponents/subtitleeditor/subtitleeditor.css b/src/bower_components/emby-webcomponents/subtitleeditor/subtitleeditor.css index f6963482d3..c805f54a2c 100644 --- a/src/bower_components/emby-webcomponents/subtitleeditor/subtitleeditor.css +++ b/src/bower_components/emby-webcomponents/subtitleeditor/subtitleeditor.css @@ -1,3 +1,3 @@ -.originalSubtitleFileLabel { - margin-right: 1em +.originalSubtitleFileLabel { + margin-right: 1em; } \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/subtitleeditor/subtitleeditor.js b/src/bower_components/emby-webcomponents/subtitleeditor/subtitleeditor.js index 65b1a74b6b..4f5047989d 100644 --- a/src/bower_components/emby-webcomponents/subtitleeditor/subtitleeditor.js +++ b/src/bower_components/emby-webcomponents/subtitleeditor/subtitleeditor.js @@ -1,187 +1,526 @@ -define(["dialogHelper", "require", "layoutManager", "globalize", "userSettings", "connectionManager", "loading", "focusManager", "dom", "apphost", "emby-select", "listViewStyle", "paper-icon-button-light", "css!./../formdialog", "material-icons", "css!./subtitleeditor", "emby-button", "flexStyles"], function(dialogHelper, require, layoutManager, globalize, userSettings, connectionManager, loading, focusManager, dom, appHost) { - "use strict"; +define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings', 'connectionManager', 'loading', 'focusManager', 'dom', 'apphost', 'emby-select', 'listViewStyle', 'paper-icon-button-light', 'css!./../formdialog', 'material-icons', 'css!./subtitleeditor', 'emby-button', 'flexStyles'], function (dialogHelper, require, layoutManager, globalize, userSettings, connectionManager, loading, focusManager, dom, appHost) { + 'use strict'; + + var currentItem; + var hasChanges; + + function showLocalSubtitles(context, index) { + + loading.show(); + + var subtitleContent = context.querySelector('.subtitleContent'); + subtitleContent.innerHTML = ''; + + var apiClient = connectionManager.getApiClient(currentItem.ServerId); + var url = 'Videos/' + currentItem.Id + '/Subtitles/' + index; + + apiClient.ajax({ + + type: 'GET', + url: url + + }).then(function (result) { + + subtitleContent.innerHTML = result; + + loading.hide(); + }); + } + + function showRemoteSubtitles(context, id) { + + loading.show(); + + var url = 'Providers/Subtitles/Subtitles/' + id; + + ApiClient.get(ApiClient.getUrl(url)).then(function (result) { + + // show result + + loading.hide(); + }); + } function downloadRemoteSubtitles(context, id) { - var url = "Items/" + currentItem.Id + "/RemoteSearch/Subtitles/" + id, - apiClient = connectionManager.getApiClient(currentItem.ServerId); + + var url = 'Items/' + currentItem.Id + '/RemoteSearch/Subtitles/' + id; + + var apiClient = connectionManager.getApiClient(currentItem.ServerId); apiClient.ajax({ + type: "POST", url: apiClient.getUrl(url) - }).then(function() { - hasChanges = !0, require(["toast"], function(toast) { - toast(globalize.translate("sharedcomponents#MessageDownloadQueued")) - }), focusManager.autoFocus(context) - }) + + }).then(function () { + + hasChanges = true; + + require(['toast'], function (toast) { + toast(globalize.translate('sharedcomponents#MessageDownloadQueued')); + }); + + focusManager.autoFocus(context); + }); } function deleteLocalSubtitle(context, index) { - var msg = globalize.translate("sharedcomponents#MessageAreYouSureDeleteSubtitles"); - require(["confirm"], function(confirm) { + + var msg = globalize.translate('sharedcomponents#MessageAreYouSureDeleteSubtitles'); + + require(['confirm'], function (confirm) { + confirm({ - title: globalize.translate("sharedcomponents#ConfirmDeletion"), + + title: globalize.translate('sharedcomponents#ConfirmDeletion'), text: msg, - confirmText: globalize.translate("sharedcomponents#Delete"), - primary: "cancel" - }).then(function() { + confirmText: globalize.translate('sharedcomponents#Delete'), + primary: 'cancel' + + }).then(function () { + loading.show(); - var itemId = currentItem.Id, - url = "Videos/" + itemId + "/Subtitles/" + index, - apiClient = connectionManager.getApiClient(currentItem.ServerId); + + var itemId = currentItem.Id; + var url = 'Videos/' + itemId + '/Subtitles/' + index; + + var apiClient = connectionManager.getApiClient(currentItem.ServerId); + apiClient.ajax({ + type: "DELETE", url: apiClient.getUrl(url) - }).then(function() { - hasChanges = !0, reload(context, apiClient, itemId) - }) - }) - }) + + }).then(function () { + + hasChanges = true; + reload(context, apiClient, itemId); + + }); + }); + }); } function fillSubtitleList(context, item) { - var streams = item.MediaStreams || [], - subs = streams.filter(function(s) { - return "Subtitle" === s.Type - }), - html = ""; - subs.length && (html += "

    " + globalize.translate("sharedcomponents#MySubtitles") + "

    ", html += "
    ", html += subs.map(function(s) { - var itemHtml = "", - tagName = layoutManager.tv ? "button" : "div", - className = layoutManager.tv && s.Path ? "listItem listItem-border btnDelete" : "listItem listItem-border"; - return layoutManager.tv && (className += " listItem-focusscale listItem-button"), className += " listItem-noborder", itemHtml += "<" + tagName + ' class="' + className + '" data-index="' + s.Index + '">', itemHtml += 'closed_caption', itemHtml += '
    ', itemHtml += "
    ", itemHtml += s.DisplayTitle || "", itemHtml += "
    ", s.Path && (itemHtml += '
    ' + s.Path + "
    "), itemHtml += "", itemHtml += "
    ", layoutManager.tv || s.Path && (itemHtml += ''), itemHtml += "" - }).join(""), html += "
    "); - var elem = context.querySelector(".subtitleList"); - subs.length ? elem.classList.remove("hide") : elem.classList.add("hide"), elem.innerHTML = html + + var streams = item.MediaStreams || []; + + var subs = streams.filter(function (s) { + + return s.Type === 'Subtitle'; + }); + + var html = ''; + + if (subs.length) { + + html += '

    ' + globalize.translate('sharedcomponents#MySubtitles') + '

    '; + + html += '
    '; + + html += subs.map(function (s) { + + var itemHtml = ''; + + var tagName = layoutManager.tv ? 'button' : 'div'; + var className = layoutManager.tv && s.Path ? 'listItem listItem-border btnDelete' : 'listItem listItem-border'; + + if (layoutManager.tv) { + className += ' listItem-focusscale listItem-button'; + } + + className += ' listItem-noborder'; + + itemHtml += '<' + tagName + ' class="' + className + '" data-index="' + s.Index + '">'; + + itemHtml += 'closed_caption'; + + itemHtml += '
    '; + + itemHtml += '
    '; + itemHtml += s.DisplayTitle || ''; + itemHtml += '
    '; + + if (s.Path) { + itemHtml += '
    ' + (s.Path) + '
    '; + } + + itemHtml += ''; + itemHtml += '
    '; + + if (!layoutManager.tv) { + if (s.Path) { + itemHtml += ''; + } + } + + itemHtml += ''; + + return itemHtml; + + }).join(''); + + html += '
    '; + } + + var elem = context.querySelector('.subtitleList'); + + if (subs.length) { + elem.classList.remove('hide'); + } else { + elem.classList.add('hide'); + } + elem.innerHTML = html; + + //('.btnViewSubtitles', elem).on('click', function () { + + // var index = this.getAttribute('data-index'); + + // showLocalSubtitles(context, index); + + //}); } function fillLanguages(context, apiClient, languages) { - var selectLanguage = context.querySelector("#selectLanguage"); - selectLanguage.innerHTML = languages.map(function(l) { - return '" + + var selectLanguage = context.querySelector('#selectLanguage'); + + selectLanguage.innerHTML = languages.map(function (l) { + + return ''; }); - var lastLanguage = userSettings.get("subtitleeditor-language"); - lastLanguage ? selectLanguage.value = lastLanguage : apiClient.getCurrentUser().then(function(user) { - var lang = user.Configuration.SubtitleLanguagePreference; - lang && (selectLanguage.value = lang) - }) + + var lastLanguage = userSettings.get('subtitleeditor-language'); + if (lastLanguage) { + selectLanguage.value = lastLanguage; + } + else { + + apiClient.getCurrentUser().then(function (user) { + + var lang = user.Configuration.SubtitleLanguagePreference; + + if (lang) { + selectLanguage.value = lang; + } + }); + } } function renderSearchResults(context, results) { - var lastProvider = "", - html = ""; - if (!results.length) return context.querySelector(".noSearchResults").classList.remove("hide"), context.querySelector(".subtitleResults").innerHTML = "", void loading.hide(); - context.querySelector(".noSearchResults").classList.add("hide"); - for (var i = 0, length = results.length; i < length; i++) { - var result = results[i], - provider = result.ProviderName; - provider !== lastProvider && (i > 0 && (html += ""), html += "

    " + provider + "

    ", layoutManager.tv, html += "
    ", lastProvider = provider); - var tagName = layoutManager.tv ? "button" : "div", - className = layoutManager.tv ? "listItem listItem-border btnOptions" : "listItem listItem-border"; - layoutManager.tv && (className += " listItem-focusscale listItem-button"), html += "<" + tagName + ' class="' + className + '" data-subid="' + result.Id + '">', html += 'closed_caption'; - html += '
    ', html += "
    " + result.Name + "
    ", html += '
    ', result.Format && (html += '' + globalize.translate("sharedcomponents#FormatValue", result.Format) + ""), null != result.DownloadCount && (html += "" + globalize.translate("sharedcomponents#DownloadsValue", result.DownloadCount) + ""), html += "
    ", result.Comment && (html += '
    ' + result.Comment + "
    "), result.IsHashMatch && (html += '
    ' + globalize.translate("sharedcomponents#PerfectMatch") + "
    "), html += "
    ", layoutManager.tv || (html += ''), html += "" + + var lastProvider = ''; + var html = ''; + + if (!results.length) { + + context.querySelector('.noSearchResults').classList.remove('hide'); + context.querySelector('.subtitleResults').innerHTML = ''; + loading.hide(); + return; } - results.length && (html += "
    "), context.querySelector(".subtitleResults").innerHTML = html, loading.hide() + + context.querySelector('.noSearchResults').classList.add('hide'); + + for (var i = 0, length = results.length; i < length; i++) { + + var result = results[i]; + + var provider = result.ProviderName; + + if (provider !== lastProvider) { + + if (i > 0) { + html += ''; + } + html += '

    ' + provider + '

    '; + if (layoutManager.tv) { + html += '
    '; + } else { + html += '
    '; + } + lastProvider = provider; + } + + var tagName = layoutManager.tv ? 'button' : 'div'; + var className = layoutManager.tv ? 'listItem listItem-border btnOptions' : 'listItem listItem-border'; + if (layoutManager.tv) { + className += ' listItem-focusscale listItem-button'; + } + + html += '<' + tagName + ' class="' + className + '" data-subid="' + result.Id + '">'; + + html += 'closed_caption'; + + var bodyClass = result.Comment || result.IsHashMatch ? 'three-line' : 'two-line'; + + html += ''; + + if (!layoutManager.tv) { + html += ''; + } + + html += ''; + } + + if (results.length) { + html += '
    '; + } + + var elem = context.querySelector('.subtitleResults'); + elem.innerHTML = html; + + //('.btnViewSubtitle', elem).on('click', function () { + + // var id = this.getAttribute('data-subid'); + // showRemoteSubtitles(context, id); + //}); + + loading.hide(); } function searchForSubtitles(context, language) { - userSettings.set("subtitleeditor-language", language), loading.show(); - var apiClient = connectionManager.getApiClient(currentItem.ServerId), - url = apiClient.getUrl("Items/" + currentItem.Id + "/RemoteSearch/Subtitles/" + language); - apiClient.getJSON(url).then(function(results) { - renderSearchResults(context, results) - }) + + userSettings.set('subtitleeditor-language', language); + + loading.show(); + + var apiClient = connectionManager.getApiClient(currentItem.ServerId); + var url = apiClient.getUrl('Items/' + currentItem.Id + '/RemoteSearch/Subtitles/' + language); + + apiClient.getJSON(url).then(function (results) { + + renderSearchResults(context, results); + }); } function reload(context, apiClient, itemId) { + + context.querySelector('.noSearchResults').classList.add('hide'); + function onGetItem(item) { - currentItem = item, fillSubtitleList(context, item); - var file = item.Path || "", - index = Math.max(file.lastIndexOf("/"), file.lastIndexOf("\\")); - index > -1 && (file = file.substring(index + 1)), file ? (context.querySelector(".pathValue").innerHTML = file, context.querySelector(".originalFile").classList.remove("hide")) : (context.querySelector(".pathValue").innerHTML = "", context.querySelector(".originalFile").classList.add("hide")), loading.hide() + + currentItem = item; + + fillSubtitleList(context, item); + var file = item.Path || ''; + var index = Math.max(file.lastIndexOf('/'), file.lastIndexOf('\\')); + if (index > -1) { + file = file.substring(index + 1); + } + + if (file) { + context.querySelector('.pathValue').innerHTML = file; + context.querySelector('.originalFile').classList.remove('hide'); + } else { + context.querySelector('.pathValue').innerHTML = ''; + context.querySelector('.originalFile').classList.add('hide'); + } + + loading.hide(); + } + + if (typeof itemId === 'string') { + apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(onGetItem); + } + else { + onGetItem(itemId); } - context.querySelector(".noSearchResults").classList.add("hide"), "string" == typeof itemId ? apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(onGetItem) : onGetItem(itemId) } function onSearchSubmit(e) { - var form = this, - lang = form.querySelector("#selectLanguage", form).value; - return searchForSubtitles(dom.parentWithClass(form, "formDialogContent"), lang), e.preventDefault(), !1 + var form = this; + + var lang = form.querySelector('#selectLanguage', form).value; + + searchForSubtitles(dom.parentWithClass(form, 'formDialogContent'), lang); + + e.preventDefault(); + return false; } function onSubtitleListClick(e) { - var btnDelete = dom.parentWithClass(e.target, "btnDelete"); + + var btnDelete = dom.parentWithClass(e.target, 'btnDelete'); if (btnDelete) { - var index = btnDelete.getAttribute("data-index"); - deleteLocalSubtitle(dom.parentWithClass(btnDelete, "subtitleEditorDialog"), index) + var index = btnDelete.getAttribute('data-index'); + var context = dom.parentWithClass(btnDelete, 'subtitleEditorDialog'); + deleteLocalSubtitle(context, index); } } function onSubtitleResultsClick(e) { - var subtitleId, context, btnOptions = dom.parentWithClass(e.target, "btnOptions"); - btnOptions && (subtitleId = btnOptions.getAttribute("data-subid"), context = dom.parentWithClass(btnOptions, "subtitleEditorDialog"), showDownloadOptions(btnOptions, context, subtitleId)); - var btnDownload = dom.parentWithClass(e.target, "btnDownload"); - btnDownload && (subtitleId = btnDownload.getAttribute("data-subid"), context = dom.parentWithClass(btnDownload, "subtitleEditorDialog"), downloadRemoteSubtitles(context, subtitleId)) + + var btnOptions = dom.parentWithClass(e.target, 'btnOptions'); + var subtitleId; + var context; + + if (btnOptions) { + subtitleId = btnOptions.getAttribute('data-subid'); + context = dom.parentWithClass(btnOptions, 'subtitleEditorDialog'); + showDownloadOptions(btnOptions, context, subtitleId); + } + + var btnDownload = dom.parentWithClass(e.target, 'btnDownload'); + if (btnDownload) { + subtitleId = btnDownload.getAttribute('data-subid'); + context = dom.parentWithClass(btnDownload, 'subtitleEditorDialog'); + downloadRemoteSubtitles(context, subtitleId); + } } function showDownloadOptions(button, context, subtitleId) { + var items = []; + items.push({ - name: Globalize.translate("sharedcomponents#Download"), - id: "download" - }), require(["actionsheet"], function(actionsheet) { + name: Globalize.translate('sharedcomponents#Download'), + id: 'download' + }); + + require(['actionsheet'], function (actionsheet) { + actionsheet.show({ items: items, positionTo: button - }).then(function(id) { + + }).then(function (id) { + switch (id) { - case "download": - downloadRemoteSubtitles(context, subtitleId) + + case 'download': + downloadRemoteSubtitles(context, subtitleId); + break; + default: + break; } - }) - }) + }); + + }); } function centerFocus(elem, horiz, on) { - require(["scrollHelper"], function(scrollHelper) { - var fn = on ? "on" : "off"; - scrollHelper.centerFocus[fn](elem, horiz) - }) + require(['scrollHelper'], function (scrollHelper) { + var fn = on ? 'on' : 'off'; + scrollHelper.centerFocus[fn](elem, horiz); + }); } function showEditorInternal(itemId, serverId, template) { - hasChanges = !1; + + hasChanges = false; + var apiClient = connectionManager.getApiClient(serverId); - return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function(item) { + return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) { + var dialogOptions = { - removeOnClose: !0, - scrollY: !1 + removeOnClose: true, + scrollY: false }; - layoutManager.tv ? dialogOptions.size = "fullscreen" : dialogOptions.size = "small"; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'small'; + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"), dlg.classList.add("subtitleEditorDialog"), dlg.innerHTML = globalize.translateDocument(template, "sharedcomponents"), dlg.querySelector(".originalSubtitleFileLabel").innerHTML = globalize.translate("sharedcomponents#File"), dlg.querySelector(".subtitleSearchForm").addEventListener("submit", onSearchSubmit); - var btnSubmit = dlg.querySelector(".btnSubmit"); - layoutManager.tv ? (centerFocus(dlg.querySelector(".formDialogContent"), !1, !0), dlg.querySelector(".btnSearchSubtitles").classList.add("hide")) : btnSubmit.classList.add("hide"); - var editorContent = dlg.querySelector(".formDialogContent"); - return dlg.querySelector(".subtitleList").addEventListener("click", onSubtitleListClick), dlg.querySelector(".subtitleResults").addEventListener("click", onSubtitleResultsClick), apiClient.getCultures().then(function(languages) { - fillLanguages(editorContent, apiClient, languages) - }), dlg.querySelector(".btnCancel").addEventListener("click", function() { - dialogHelper.close(dlg) - }), new Promise(function(resolve, reject) { - dlg.addEventListener("close", function() { - layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !1), hasChanges ? resolve() : reject() - }), dialogHelper.open(dlg), reload(editorContent, apiClient, item) - }) - }) + + dlg.classList.add('formDialog'); + dlg.classList.add('subtitleEditorDialog'); + + dlg.innerHTML = globalize.translateDocument(template, 'sharedcomponents'); + + dlg.querySelector('.originalSubtitleFileLabel').innerHTML = globalize.translate('sharedcomponents#File'); + + dlg.querySelector('.subtitleSearchForm').addEventListener('submit', onSearchSubmit); + + var btnSubmit = dlg.querySelector('.btnSubmit'); + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, true); + dlg.querySelector('.btnSearchSubtitles').classList.add('hide'); + } else { + btnSubmit.classList.add('hide'); + } + + var editorContent = dlg.querySelector('.formDialogContent'); + + dlg.querySelector('.subtitleList').addEventListener('click', onSubtitleListClick); + dlg.querySelector('.subtitleResults').addEventListener('click', onSubtitleResultsClick); + + apiClient.getCultures().then(function (languages) { + + fillLanguages(editorContent, apiClient, languages); + }); + + dlg.querySelector('.btnCancel').addEventListener('click', function () { + + dialogHelper.close(dlg); + }); + + return new Promise(function (resolve, reject) { + + dlg.addEventListener('close', function () { + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, false); + } + + if (hasChanges) { + resolve(); + } else { + reject(); + } + }); + + dialogHelper.open(dlg); + + reload(editorContent, apiClient, item); + }); + }); } function showEditor(itemId, serverId) { - return loading.show(), new Promise(function(resolve, reject) { - require(["text!./subtitleeditor.template.html"], function(template) { - showEditorInternal(itemId, serverId, template).then(resolve, reject) - }) - }) + + loading.show(); + + return new Promise(function (resolve, reject) { + + require(['text!./subtitleeditor.template.html'], function (template) { + + showEditorInternal(itemId, serverId, template).then(resolve, reject); + }); + }); + } - var currentItem, hasChanges; + return { show: showEditor - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/subtitlesettings/subtitleappearancehelper.js b/src/bower_components/emby-webcomponents/subtitlesettings/subtitleappearancehelper.js index cfd79fd9fa..588f491a67 100644 --- a/src/bower_components/emby-webcomponents/subtitlesettings/subtitleappearancehelper.js +++ b/src/bower_components/emby-webcomponents/subtitlesettings/subtitleappearancehelper.js @@ -1,211 +1,160 @@ -define([], function() { +define([], function () { "use strict"; function getTextStyles(settings, isCue) { + var list = []; - if (isCue) switch (settings.textSize || "") { - case "smaller": - list.push({ - name: "font-size", - value: ".5em" - }); - break; - case "small": - list.push({ - name: "font-size", - value: ".7em" - }); - break; - case "large": - list.push({ - name: "font-size", - value: "1.3em" - }); - break; - case "larger": - list.push({ - name: "font-size", - value: "1.72em" - }); - break; - case "extralarge": - list.push({ - name: "font-size", - value: "2em" - }); - break; - default: - case "medium": - } else switch (settings.textSize || "") { - case "smaller": - list.push({ - name: "font-size", - value: ".8em" - }); - break; - case "small": - list.push({ - name: "font-size", - value: "inherit" - }); - break; - case "larger": - list.push({ - name: "font-size", - value: "2em" - }); - break; - case "extralarge": - list.push({ - name: "font-size", - value: "2.2em" - }); - break; - case "large": - list.push({ - name: "font-size", - value: "1.72em" - }); - break; - default: - case "medium": - list.push({ - name: "font-size", - value: "1.36em" - }) + + if (isCue) { + switch (settings.textSize || '') { + + case 'smaller': + list.push({ name: 'font-size', value: '.5em' }); + break; + case 'small': + list.push({ name: 'font-size', value: '.7em' }); + break; + case 'large': + list.push({ name: 'font-size', value: '1.3em' }); + break; + case 'larger': + list.push({ name: 'font-size', value: '1.72em' }); + break; + case 'extralarge': + list.push({ name: 'font-size', value: '2em' }); + break; + default: + case 'medium': + break; + } + } else { + switch (settings.textSize || '') { + + case 'smaller': + list.push({ name: 'font-size', value: '.8em' }); + break; + case 'small': + list.push({ name: 'font-size', value: 'inherit' }); + break; + case 'larger': + list.push({ name: 'font-size', value: '2em' }); + break; + case 'extralarge': + list.push({ name: 'font-size', value: '2.2em' }); + break; + case 'large': + list.push({ name: 'font-size', value: '1.72em' }); + break; + default: + case 'medium': + list.push({ name: 'font-size', value: '1.36em' }); + break; + } } - switch (settings.dropShadow || "") { - case "raised": - list.push({ - name: "text-shadow", - value: "-1px -1px white, 0px -1px white, -1px 0px white, 1px 1px black, 0px 1px black, 1px 0px black" - }); + + switch (settings.dropShadow || '') { + + case 'raised': + list.push({ name: 'text-shadow', value: '-1px -1px white, 0px -1px white, -1px 0px white, 1px 1px black, 0px 1px black, 1px 0px black' }); break; - case "depressed": - list.push({ - name: "text-shadow", - value: "1px 1px white, 0px 1px white, 1px 0px white, -1px -1px black, 0px -1px black, -1px 0px black" - }); + case 'depressed': + list.push({ name: 'text-shadow', value: '1px 1px white, 0px 1px white, 1px 0px white, -1px -1px black, 0px -1px black, -1px 0px black' }); break; - case "uniform": - list.push({ - name: "text-shadow", - value: "-1px 0px #000000, 0px 1px #000000, 1px 0px #000000, 0px -1px #000000" - }); + case 'uniform': + list.push({ name: 'text-shadow', value: '-1px 0px #000000, 0px 1px #000000, 1px 0px #000000, 0px -1px #000000' }); break; - case "none": - list.push({ - name: "text-shadow", - value: "none" - }); + case 'none': + list.push({ name: 'text-shadow', value: 'none' }); break; default: - case "dropshadow": - list.push({ - name: "text-shadow", - value: "#000000 0px 0px 7px" - }) + case 'dropshadow': + list.push({ name: 'text-shadow', value: '#000000 0px 0px 7px' }); + break; } - var background = settings.textBackground || "transparent"; - background && list.push({ - name: "background-color", - value: background - }); - var textColor = settings.textColor || "#ffffff"; - switch (textColor && list.push({ - name: "color", - value: textColor - }), settings.font || "") { - case "typewriter": - list.push({ - name: "font-family", - value: '"Courier New",monospace' - }), list.push({ - name: "font-variant", - value: "none" - }); + + var background = settings.textBackground || 'transparent'; + if (background) { + list.push({ name: 'background-color', value: background }); + } + + var textColor = settings.textColor || '#ffffff'; + if (textColor) { + list.push({ name: 'color', value: textColor }); + } + + switch (settings.font || '') { + + case 'typewriter': + list.push({ name: 'font-family', value: '"Courier New",monospace' }); + list.push({ name: 'font-variant', value: 'none' }); break; - case "print": - list.push({ - name: "font-family", - value: "Georgia,Times New Roman,Arial,Helvetica,serif" - }), list.push({ - name: "font-variant", - value: "none" - }); + case 'print': + list.push({ name: 'font-family', value: 'Georgia,Times New Roman,Arial,Helvetica,serif' }); + list.push({ name: 'font-variant', value: 'none' }); break; - case "console": - list.push({ - name: "font-family", - value: "Consolas,Lucida Console,Menlo,Monaco,monospace" - }), list.push({ - name: "font-variant", - value: "none" - }); + case 'console': + list.push({ name: 'font-family', value: 'Consolas,Lucida Console,Menlo,Monaco,monospace' }); + list.push({ name: 'font-variant', value: 'none' }); break; - case "cursive": - list.push({ - name: "font-family", - value: "Lucida Handwriting,Brush Script MT,Segoe Script,cursive,Quintessential,system-ui,-apple-system,BlinkMacSystemFont,sans-serif" - }), list.push({ - name: "font-variant", - value: "none" - }); + case 'cursive': + list.push({ name: 'font-family', value: 'Lucida Handwriting,Brush Script MT,Segoe Script,cursive,Quintessential,system-ui,-apple-system,BlinkMacSystemFont,sans-serif' }); + list.push({ name: 'font-variant', value: 'none' }); break; - case "casual": - list.push({ - name: "font-family", - value: "Gabriola,Segoe Print,Comic Sans MS,Chalkboard,Short Stack,system-ui,-apple-system,BlinkMacSystemFont,sans-serif" - }), list.push({ - name: "font-variant", - value: "none" - }); + case 'casual': + list.push({ name: 'font-family', value: 'Gabriola,Segoe Print,Comic Sans MS,Chalkboard,Short Stack,system-ui,-apple-system,BlinkMacSystemFont,sans-serif' }); + list.push({ name: 'font-variant', value: 'none' }); break; - case "smallcaps": - list.push({ - name: "font-family", - value: "Copperplate Gothic,Copperplate Gothic Bold,Copperplate,system-ui,-apple-system,BlinkMacSystemFont,sans-serif" - }), list.push({ - name: "font-variant", - value: "small-caps" - }); + case 'smallcaps': + list.push({ name: 'font-family', value: 'Copperplate Gothic,Copperplate Gothic Bold,Copperplate,system-ui,-apple-system,BlinkMacSystemFont,sans-serif' }); + list.push({ name: 'font-variant', value: 'small-caps' }); break; default: - list.push({ - name: "font-family", - value: "inherit" - }), list.push({ - name: "font-variant", - value: "none" - }) + list.push({ name: 'font-family', value: 'inherit' }); + list.push({ name: 'font-variant', value: 'none' }); + break; } - return list + + return list; } function getWindowStyles(settings) { - return [] + + return []; } function getStyles(settings, isCue) { + return { text: getTextStyles(settings, isCue), window: getWindowStyles(settings) - } + }; } function applyStyleList(styles, elem) { + + for (var i = 0, length = styles.length; i < length; i++) { + var style = styles[i]; - elem.style[style.name] = style.value + + elem.style[style.name] = style.value; } } function applyStyles(elements, appearanceSettings) { + var styles = getStyles(appearanceSettings); - elements.text && applyStyleList(styles.text, elements.text), elements.window && applyStyleList(styles.window, elements.window) + + if (elements.text) { + applyStyleList(styles.text, elements.text); + } + if (elements.window) { + applyStyleList(styles.window, elements.window); + } } + return { getStyles: getStyles, applyStyles: applyStyles - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/subtitlesettings/subtitlesettings.js b/src/bower_components/emby-webcomponents/subtitlesettings/subtitlesettings.js index 092c806620..00bed79522 100644 --- a/src/bower_components/emby-webcomponents/subtitlesettings/subtitlesettings.js +++ b/src/bower_components/emby-webcomponents/subtitlesettings/subtitlesettings.js @@ -1,100 +1,222 @@ -define(["require", "globalize", "appSettings", "apphost", "focusManager", "loading", "connectionManager", "subtitleAppearanceHelper", "dom", "events", "listViewStyle", "emby-select", "emby-input", "emby-checkbox", "flexStyles"], function(require, globalize, appSettings, appHost, focusManager, loading, connectionManager, subtitleAppearanceHelper, dom, events) { +define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loading', 'connectionManager', 'subtitleAppearanceHelper', 'dom', 'events', 'listViewStyle', 'emby-select', 'emby-input', 'emby-checkbox', 'flexStyles'], function (require, globalize, appSettings, appHost, focusManager, loading, connectionManager, subtitleAppearanceHelper, dom, events) { "use strict"; function populateLanguages(select, languages) { + var html = ""; - html += ""; + + html += ""; + for (var i = 0, length = languages.length; i < length; i++) { + var culture = languages[i]; - html += "" + + html += ""; } - select.innerHTML = html + + select.innerHTML = html; } function getSubtitleAppearanceObject(context) { + var appearanceSettings = {}; - return appearanceSettings.textSize = context.querySelector("#selectTextSize").value, appearanceSettings.dropShadow = context.querySelector("#selectDropShadow").value, appearanceSettings.font = context.querySelector("#selectFont").value, appearanceSettings.textBackground = context.querySelector("#inputTextBackground").value, appearanceSettings.textColor = context.querySelector("#inputTextColor").value, appearanceSettings + + appearanceSettings.textSize = context.querySelector('#selectTextSize').value; + appearanceSettings.dropShadow = context.querySelector('#selectDropShadow').value; + appearanceSettings.font = context.querySelector('#selectFont').value; + appearanceSettings.textBackground = context.querySelector('#inputTextBackground').value; + appearanceSettings.textColor = context.querySelector('#inputTextColor').value; + + return appearanceSettings; } function loadForm(context, user, userSettings, appearanceSettings, apiClient) { - apiClient.getCultures().then(function(allCultures) { - appHost.supports("subtitleburnsettings") && user.Policy.EnableVideoPlaybackTranscoding && context.querySelector(".fldBurnIn").classList.remove("hide"); - var selectSubtitleLanguage = context.querySelector("#selectSubtitleLanguage"); - populateLanguages(selectSubtitleLanguage, allCultures), selectSubtitleLanguage.value = user.Configuration.SubtitleLanguagePreference || "", context.querySelector("#selectSubtitlePlaybackMode").value = user.Configuration.SubtitleMode || "", context.querySelector("#selectSubtitlePlaybackMode").dispatchEvent(new CustomEvent("change", {})), context.querySelector("#selectTextSize").value = appearanceSettings.textSize || "", context.querySelector("#selectDropShadow").value = appearanceSettings.dropShadow || "", context.querySelector("#inputTextBackground").value = appearanceSettings.textBackground || "transparent", context.querySelector("#inputTextColor").value = appearanceSettings.textColor || "#ffffff", context.querySelector("#selectFont").value = appearanceSettings.font || "", context.querySelector("#selectSubtitleBurnIn").value = appSettings.get("subtitleburnin") || "", onAppearanceFieldChange({ - target: context.querySelector("#selectTextSize") - }), loading.hide() - }) + + apiClient.getCultures().then(function (allCultures) { + + if (appHost.supports('subtitleburnsettings') && user.Policy.EnableVideoPlaybackTranscoding) { + context.querySelector('.fldBurnIn').classList.remove('hide'); + } + + var selectSubtitleLanguage = context.querySelector('#selectSubtitleLanguage'); + + populateLanguages(selectSubtitleLanguage, allCultures); + + selectSubtitleLanguage.value = user.Configuration.SubtitleLanguagePreference || ""; + context.querySelector('#selectSubtitlePlaybackMode').value = user.Configuration.SubtitleMode || ""; + + context.querySelector('#selectSubtitlePlaybackMode').dispatchEvent(new CustomEvent('change', {})); + + context.querySelector('#selectTextSize').value = appearanceSettings.textSize || ''; + context.querySelector('#selectDropShadow').value = appearanceSettings.dropShadow || ''; + context.querySelector('#inputTextBackground').value = appearanceSettings.textBackground || 'transparent'; + context.querySelector('#inputTextColor').value = appearanceSettings.textColor || '#ffffff'; + context.querySelector('#selectFont').value = appearanceSettings.font || ''; + + context.querySelector('#selectSubtitleBurnIn').value = appSettings.get('subtitleburnin') || ''; + + onAppearanceFieldChange({ + target: context.querySelector('#selectTextSize') + }); + + loading.hide(); + }); } function saveUser(context, user, userSettingsInstance, appearanceKey, apiClient) { + var appearanceSettings = userSettingsInstance.getSubtitleAppearanceSettings(appearanceKey); - return appearanceSettings = Object.assign(appearanceSettings, getSubtitleAppearanceObject(context)), userSettingsInstance.setSubtitleAppearanceSettings(appearanceSettings, appearanceKey), user.Configuration.SubtitleLanguagePreference = context.querySelector("#selectSubtitleLanguage").value, user.Configuration.SubtitleMode = context.querySelector("#selectSubtitlePlaybackMode").value, apiClient.updateUserConfiguration(user.Id, user.Configuration) + appearanceSettings = Object.assign(appearanceSettings, getSubtitleAppearanceObject(context)); + + userSettingsInstance.setSubtitleAppearanceSettings(appearanceSettings, appearanceKey); + + user.Configuration.SubtitleLanguagePreference = context.querySelector('#selectSubtitleLanguage').value; + user.Configuration.SubtitleMode = context.querySelector('#selectSubtitlePlaybackMode').value; + + return apiClient.updateUserConfiguration(user.Id, user.Configuration); } function save(instance, context, userId, userSettings, apiClient, enableSaveConfirmation) { - loading.show(), appSettings.set("subtitleburnin", context.querySelector("#selectSubtitleBurnIn").value), apiClient.getUser(userId).then(function(user) { - saveUser(context, user, userSettings, instance.appearanceKey, apiClient).then(function() { - loading.hide(), enableSaveConfirmation && require(["toast"], function(toast) { - toast(globalize.translate("sharedcomponents#SettingsSaved")) - }), events.trigger(instance, "saved") - }, function() { - loading.hide() - }) - }) + + loading.show(); + + appSettings.set('subtitleburnin', context.querySelector('#selectSubtitleBurnIn').value); + + apiClient.getUser(userId).then(function (user) { + + saveUser(context, user, userSettings, instance.appearanceKey, apiClient).then(function () { + + loading.hide(); + if (enableSaveConfirmation) { + require(['toast'], function (toast) { + toast(globalize.translate('sharedcomponents#SettingsSaved')); + }); + } + + events.trigger(instance, 'saved'); + + }, function () { + loading.hide(); + }); + }); } function onSubmit(e) { - var self = this, - apiClient = connectionManager.getApiClient(self.options.serverId), - userId = self.options.userId, - userSettings = self.options.userSettings; - return userSettings.setUserInfo(userId, apiClient).then(function() { + + var self = this; + var apiClient = connectionManager.getApiClient(self.options.serverId); + var userId = self.options.userId; + var userSettings = self.options.userSettings; + + userSettings.setUserInfo(userId, apiClient).then(function () { + var enableSaveConfirmation = self.options.enableSaveConfirmation; - save(self, self.options.element, userId, userSettings, apiClient, enableSaveConfirmation) - }), e && e.preventDefault(), !1 + save(self, self.options.element, userId, userSettings, apiClient, enableSaveConfirmation); + }); + + // Disable default form submission + if (e) { + e.preventDefault(); + } + return false; } function onSubtitleModeChange(e) { - for (var view = dom.parentWithClass(e.target, "subtitlesettings"), subtitlesHelp = view.querySelectorAll(".subtitlesHelp"), i = 0, length = subtitlesHelp.length; i < length; i++) subtitlesHelp[i].classList.add("hide"); - view.querySelector(".subtitles" + this.value + "Help").classList.remove("hide") + + var view = dom.parentWithClass(e.target, 'subtitlesettings'); + + var subtitlesHelp = view.querySelectorAll('.subtitlesHelp'); + for (var i = 0, length = subtitlesHelp.length; i < length; i++) { + subtitlesHelp[i].classList.add('hide'); + } + view.querySelector('.subtitles' + this.value + 'Help').classList.remove('hide'); } function onAppearanceFieldChange(e) { - var view = dom.parentWithClass(e.target, "subtitlesettings"), - appearanceSettings = getSubtitleAppearanceObject(view), - elements = { - window: view.querySelector(".subtitleappearance-preview-window"), - text: view.querySelector(".subtitleappearance-preview-text") - }; - subtitleAppearanceHelper.applyStyles(elements, appearanceSettings) + + var view = dom.parentWithClass(e.target, 'subtitlesettings'); + + var appearanceSettings = getSubtitleAppearanceObject(view); + + var elements = { + window: view.querySelector('.subtitleappearance-preview-window'), + text: view.querySelector('.subtitleappearance-preview-text') + }; + + subtitleAppearanceHelper.applyStyles(elements, appearanceSettings); } function embed(options, self) { - require(["text!./subtitlesettings.template.html"], function(template) { - options.element.classList.add("subtitlesettings"), options.element.innerHTML = globalize.translateDocument(template, "sharedcomponents"), options.element.querySelector("form").addEventListener("submit", onSubmit.bind(self)), options.element.querySelector("#selectSubtitlePlaybackMode").addEventListener("change", onSubtitleModeChange), options.element.querySelector("#selectTextSize").addEventListener("change", onAppearanceFieldChange), options.element.querySelector("#selectDropShadow").addEventListener("change", onAppearanceFieldChange), options.element.querySelector("#selectFont").addEventListener("change", onAppearanceFieldChange), options.element.querySelector("#inputTextColor").addEventListener("change", onAppearanceFieldChange), options.element.querySelector("#inputTextBackground").addEventListener("change", onAppearanceFieldChange), options.enableSaveButton && options.element.querySelector(".btnSave").classList.remove("hide"), appHost.supports("subtitleappearancesettings") && options.element.querySelector(".subtitleAppearanceSection").classList.remove("hide"), self.loadData(), options.autoFocus && focusManager.autoFocus(options.element) - }) + + require(['text!./subtitlesettings.template.html'], function (template) { + + options.element.classList.add('subtitlesettings'); + options.element.innerHTML = globalize.translateDocument(template, 'sharedcomponents'); + + options.element.querySelector('form').addEventListener('submit', onSubmit.bind(self)); + + options.element.querySelector('#selectSubtitlePlaybackMode').addEventListener('change', onSubtitleModeChange); + options.element.querySelector('#selectTextSize').addEventListener('change', onAppearanceFieldChange); + options.element.querySelector('#selectDropShadow').addEventListener('change', onAppearanceFieldChange); + options.element.querySelector('#selectFont').addEventListener('change', onAppearanceFieldChange); + options.element.querySelector('#inputTextColor').addEventListener('change', onAppearanceFieldChange); + options.element.querySelector('#inputTextBackground').addEventListener('change', onAppearanceFieldChange); + + if (options.enableSaveButton) { + options.element.querySelector('.btnSave').classList.remove('hide'); + } + + if (appHost.supports('subtitleappearancesettings')) { + options.element.querySelector('.subtitleAppearanceSection').classList.remove('hide'); + } + + self.loadData(); + + if (options.autoFocus) { + focusManager.autoFocus(options.element); + } + }); } function SubtitleSettings(options) { - this.options = options, embed(options, this) + + this.options = options; + + embed(options, this); } - return SubtitleSettings.prototype.loadData = function() { - var self = this, - context = self.options.element; + + SubtitleSettings.prototype.loadData = function () { + + var self = this; + var context = self.options.element; + loading.show(); - var userId = self.options.userId, - apiClient = connectionManager.getApiClient(self.options.serverId), - userSettings = self.options.userSettings; - apiClient.getUser(userId).then(function(user) { - userSettings.setUserInfo(userId, apiClient).then(function() { - self.dataLoaded = !0; + + var userId = self.options.userId; + var apiClient = connectionManager.getApiClient(self.options.serverId); + var userSettings = self.options.userSettings; + + apiClient.getUser(userId).then(function (user) { + + userSettings.setUserInfo(userId, apiClient).then(function () { + + self.dataLoaded = true; + var appearanceSettings = userSettings.getSubtitleAppearanceSettings(self.options.appearanceKey); - loadForm(context, user, userSettings, appearanceSettings, apiClient) - }) - }) - }, SubtitleSettings.prototype.submit = function() { - onSubmit.call(this) - }, SubtitleSettings.prototype.destroy = function() { - this.options = null - }, SubtitleSettings + + loadForm(context, user, userSettings, appearanceSettings, apiClient); + }); + }); + }; + + SubtitleSettings.prototype.submit = function () { + onSubmit.call(this); + }; + + SubtitleSettings.prototype.destroy = function () { + + this.options = null; + }; + + return SubtitleSettings; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/sync/emby-downloadbutton.js b/src/bower_components/emby-webcomponents/sync/emby-downloadbutton.js index 9c1c37da08..bc91e0333b 100644 --- a/src/bower_components/emby-webcomponents/sync/emby-downloadbutton.js +++ b/src/bower_components/emby-webcomponents/sync/emby-downloadbutton.js @@ -1,71 +1,188 @@ -define(["connectionManager", "serverNotifications", "events", "globalize", "emby-button"], function(connectionManager, serverNotifications, events, globalize, EmbyButtonPrototype) { - "use strict"; +define(['connectionManager', 'serverNotifications', 'events', 'globalize', 'emby-button'], function (connectionManager, serverNotifications, events, globalize, EmbyButtonPrototype) { + 'use strict'; function onClick(e) { - var button = this, - id = button.getAttribute("data-id"), - serverId = button.getAttribute("data-serverid"), - apiClient = connectionManager.getApiClient(serverId); - button.classList.contains("downloadbutton-on") ? require(["confirm"], function(confirm) { - confirm({ - text: globalize.translate("sharedcomponents#ConfirmRemoveDownload"), - confirmText: globalize.translate("sharedcomponents#RemoveDownload"), - cancelText: globalize.translate("sharedcomponents#KeepDownload"), - primary: "cancel" - }).then(function() { - apiClient.cancelSyncItems([id]), button.dispatchEvent(new CustomEvent("download-cancel", { - cancelable: !1 - })) - }) - }) : require(["syncDialog"], function(syncDialog) { - syncDialog.showMenu({ - items: [id], - mode: "download", - serverId: serverId - }).then(function() { - button.dispatchEvent(new CustomEvent("download", { - cancelable: !1 - })) - }) - }) + + var button = this; + var id = button.getAttribute('data-id'); + var serverId = button.getAttribute('data-serverid'); + var apiClient = connectionManager.getApiClient(serverId); + + if (!button.classList.contains('downloadbutton-on')) { + + require(['syncDialog'], function (syncDialog) { + syncDialog.showMenu({ + + items: [id], + mode: 'download', + serverId: serverId + + }).then(function () { + + button.dispatchEvent(new CustomEvent('download', { + cancelable: false + })); + + }); + }); + + } else { + + require(['confirm'], function (confirm) { + + confirm({ + + text: globalize.translate('sharedcomponents#ConfirmRemoveDownload'), + confirmText: globalize.translate('sharedcomponents#RemoveDownload'), + cancelText: globalize.translate('sharedcomponents#KeepDownload'), + primary: 'cancel' + + }).then(function () { + apiClient.cancelSyncItems([id]); + + button.dispatchEvent(new CustomEvent('download-cancel', { + cancelable: false + })); + }); + }); + } } function updateSyncStatus(button, syncPercent) { + var icon = button.iconElement; - icon || (button.iconElement = button.querySelector("i"), icon = button.iconElement), null != syncPercent ? (button.classList.add("downloadbutton-on"), icon && icon.classList.add("downloadbutton-icon-on")) : (button.classList.remove("downloadbutton-on"), icon && icon.classList.remove("downloadbutton-icon-on")), (syncPercent || 0) >= 100 ? (button.classList.add("downloadbutton-complete"), icon && icon.classList.add("downloadbutton-icon-complete")) : (button.classList.remove("downloadbutton-complete"), icon && icon.classList.remove("downloadbutton-icon-complete")); + if (!icon) { + button.iconElement = button.querySelector('i'); + icon = button.iconElement; + } + + if (syncPercent != null) { + button.classList.add('downloadbutton-on'); + + if (icon) { + icon.classList.add('downloadbutton-icon-on'); + } + + } else { + button.classList.remove('downloadbutton-on'); + + if (icon) { + icon.classList.remove('downloadbutton-icon-on'); + } + } + + if ((syncPercent || 0) >= 100) { + button.classList.add('downloadbutton-complete'); + + if (icon) { + icon.classList.add('downloadbutton-icon-complete'); + } + } else { + button.classList.remove('downloadbutton-complete'); + + if (icon) { + icon.classList.remove('downloadbutton-icon-complete'); + } + } + var text; - text = (syncPercent || 0) >= 100 ? globalize.translate("sharedcomponents#Downloaded") : null != syncPercent ? globalize.translate("sharedcomponents#Downloading") : globalize.translate("sharedcomponents#Download"); - var textElement = button.querySelector(".emby-downloadbutton-downloadtext"); - textElement && (textElement.innerHTML = text), button.title = text + if ((syncPercent || 0) >= 100) { + text = globalize.translate('sharedcomponents#Downloaded'); + } else if (syncPercent != null) { + text = globalize.translate('sharedcomponents#Downloading'); + } else { + text = globalize.translate('sharedcomponents#Download'); + } + + var textElement = button.querySelector('.emby-downloadbutton-downloadtext'); + if (textElement) { + textElement.innerHTML = text; + } + + button.title = text; } function clearEvents(button) { - button.removeEventListener("click", onClick) + + button.removeEventListener('click', onClick); } function bindEvents(button) { - clearEvents(button), button.addEventListener("click", onClick) + + clearEvents(button); + + button.addEventListener('click', onClick); } - function fetchAndUpdate(button, item) { - connectionManager.getApiClient(item.ServerId).getSyncStatus(item.Id).then(function(result) { - updateSyncStatus(button, result.Progress) - }, function() {}) - } var EmbyDownloadButtonPrototype = Object.create(EmbyButtonPrototype); - EmbyDownloadButtonPrototype.createdCallback = function() { - EmbyButtonPrototype.createdCallback && EmbyButtonPrototype.createdCallback.call(this) - }, EmbyDownloadButtonPrototype.attachedCallback = function() { - EmbyButtonPrototype.attachedCallback && EmbyButtonPrototype.attachedCallback.call(this); - var itemId = this.getAttribute("data-id"), - serverId = this.getAttribute("data-serverid"); - itemId && serverId && bindEvents(this) - }, EmbyDownloadButtonPrototype.detachedCallback = function() { - EmbyButtonPrototype.detachedCallback && EmbyButtonPrototype.detachedCallback.call(this), clearEvents(this), this.iconElement = null - }, EmbyDownloadButtonPrototype.setItem = function(item) { - item ? (this.setAttribute("data-id", item.Id), this.setAttribute("data-serverid", item.ServerId), fetchAndUpdate(this, item), bindEvents(this)) : (this.removeAttribute("data-id"), this.removeAttribute("data-serverid"), clearEvents(this)) - }, document.registerElement("emby-downloadbutton", { + + EmbyDownloadButtonPrototype.createdCallback = function () { + + // base method + if (EmbyButtonPrototype.createdCallback) { + EmbyButtonPrototype.createdCallback.call(this); + } + }; + + EmbyDownloadButtonPrototype.attachedCallback = function () { + + // base method + if (EmbyButtonPrototype.attachedCallback) { + EmbyButtonPrototype.attachedCallback.call(this); + } + + var itemId = this.getAttribute('data-id'); + var serverId = this.getAttribute('data-serverid'); + if (itemId && serverId) { + + bindEvents(this); + } + }; + + EmbyDownloadButtonPrototype.detachedCallback = function () { + + // base method + if (EmbyButtonPrototype.detachedCallback) { + EmbyButtonPrototype.detachedCallback.call(this); + } + + clearEvents(this); + + this.iconElement = null; + }; + + function fetchAndUpdate(button, item) { + + connectionManager.getApiClient(item.ServerId).getSyncStatus(item.Id).then(function (result) { + + updateSyncStatus(button, result.Progress); + + }, function () { + + }); + } + + EmbyDownloadButtonPrototype.setItem = function (item) { + + if (item) { + + this.setAttribute('data-id', item.Id); + this.setAttribute('data-serverid', item.ServerId); + + fetchAndUpdate(this, item); + + bindEvents(this); + + } else { + + this.removeAttribute('data-id'); + this.removeAttribute('data-serverid'); + clearEvents(this); + } + }; + + document.registerElement('emby-downloadbutton', { prototype: EmbyDownloadButtonPrototype, - extends: "button" - }) + extends: 'button' + }); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/sync/sync.js b/src/bower_components/emby-webcomponents/sync/sync.js index a50370da8c..c57fc5255a 100644 --- a/src/bower_components/emby-webcomponents/sync/sync.js +++ b/src/bower_components/emby-webcomponents/sync/sync.js @@ -1,295 +1,736 @@ -define(["apphost", "globalize", "connectionManager", "layoutManager", "focusManager", "scrollHelper", "appSettings", "registrationServices", "dialogHelper", "paper-icon-button-light", "formDialogStyle"], function(appHost, globalize, connectionManager, layoutManager, focusManager, scrollHelper, appSettings, registrationServices, dialogHelper) { - "use strict"; +define(['apphost', 'globalize', 'connectionManager', 'layoutManager', 'focusManager', 'scrollHelper', 'appSettings', 'registrationServices', 'dialogHelper', 'paper-icon-button-light', 'formDialogStyle'], function (appHost, globalize, connectionManager, layoutManager, focusManager, scrollHelper, appSettings, registrationServices, dialogHelper) { + 'use strict'; + + var currentDialogOptions; function submitJob(dlg, apiClient, userId, syncOptions, form) { - if (!userId) throw new Error("userId cannot be null"); - if (!syncOptions) throw new Error("syncOptions cannot be null"); - if (!form) throw new Error("form cannot be null"); - var selectSyncTarget = form.querySelector("#selectSyncTarget"), - target = selectSyncTarget ? selectSyncTarget.value : null; - if (!target) return require(["toast"], function(toast) { - toast(globalize.translate("sharedcomponents#PleaseSelectDeviceToSyncTo")) - }), !1; + + if (!userId) { + throw new Error('userId cannot be null'); + } + + if (!syncOptions) { + throw new Error('syncOptions cannot be null'); + } + + if (!form) { + throw new Error('form cannot be null'); + } + + var selectSyncTarget = form.querySelector('#selectSyncTarget'); + var target = selectSyncTarget ? selectSyncTarget.value : null; + + if (!target) { + + require(['toast'], function (toast) { + toast(globalize.translate('sharedcomponents#PleaseSelectDeviceToSyncTo')); + }); + return false; + } + var options = { + userId: userId, TargetId: target, + ParentId: syncOptions.ParentId, Category: syncOptions.Category }; - return setJobValues(options, form), syncOptions.items && syncOptions.items.length && (options.ItemIds = (syncOptions.items || []).map(function(i) { - return i.Id || i - }).join(",")), apiClient.ajax({ + + setJobValues(options, form); + + if (syncOptions.items && syncOptions.items.length) { + options.ItemIds = (syncOptions.items || []).map(function (i) { + return i.Id || i; + }).join(','); + } + + apiClient.ajax({ + type: "POST", url: apiClient.getUrl("Sync/Jobs"), data: JSON.stringify(options), contentType: "application/json", - dataType: "json" - }).then(function() { - dialogHelper.close(dlg), require(["toast"], function(toast) { - showSubmissionToast(target, apiClient), "download" === syncOptions.mode && syncNow() - }) - }), !0 + dataType: 'json' + + }).then(function () { + + dialogHelper.close(dlg); + require(['toast'], function (toast) { + + showSubmissionToast(target, apiClient); + + if (syncOptions.mode === 'download') { + syncNow(); + } + }); + }); + + return true; } + function showSubmissionToast(targetId, apiClient) { - require(["toast"], function(toast) { - toast(targetId === apiClient.deviceId() ? globalize.translate("sharedcomponents#DownloadingDots") : globalize.translate("sharedcomponents#SyncingDots")) - }) + + require(['toast'], function (toast) { + + var msg = targetId === apiClient.deviceId() ? + globalize.translate('sharedcomponents#DownloadingDots') : + globalize.translate('sharedcomponents#SyncingDots'); + + toast(msg); + }); } function syncNow() { - require(["localsync"], function(localSync) { - localSync.sync() - }) + require(['localsync'], function (localSync) { + localSync.sync(); + }); } function submitQuickSyncJob(apiClient, userId, targetId, syncOptions) { - if (!userId) throw new Error("userId cannot be null"); - if (!syncOptions) throw new Error("syncOptions cannot be null"); - if (!targetId) throw new Error("targetId cannot be null"); + + if (!userId) { + throw new Error('userId cannot be null'); + } + + if (!syncOptions) { + throw new Error('syncOptions cannot be null'); + } + + if (!targetId) { + throw new Error('targetId cannot be null'); + } + var options = { + userId: userId, TargetId: targetId, + ParentId: syncOptions.ParentId, Category: syncOptions.Category, Quality: syncOptions.Quality, Bitrate: syncOptions.Bitrate }; - return syncOptions.items && syncOptions.items.length && (options.ItemIds = (syncOptions.items || []).map(function(i) { - return i.Id || i - }).join(",")), apiClient.ajax({ + + if (syncOptions.items && syncOptions.items.length) { + options.ItemIds = (syncOptions.items || []).map(function (i) { + return i.Id || i; + }).join(','); + } + + return apiClient.ajax({ + type: "POST", url: apiClient.getUrl("Sync/Jobs"), data: JSON.stringify(options), contentType: "application/json", - dataType: "json" - }).then(function() { - require(["toast"], function(toast) { - showSubmissionToast(targetId, apiClient), "download" === syncOptions.mode && syncNow() - }) - }) + dataType: 'json' + + }).then(function () { + + require(['toast'], function (toast) { + + showSubmissionToast(targetId, apiClient); + + if (syncOptions.mode === 'download') { + syncNow(); + } + }); + }); } function setJobValues(job, form) { - var txtBitrate = form.querySelector("#txtBitrate"), - bitrate = txtBitrate ? txtBitrate.value : null; - bitrate && (bitrate = 1e6 * parseFloat(bitrate)), job.Bitrate = bitrate; - var selectQuality = form.querySelector("#selectQuality"); - selectQuality && (job.Quality = selectQuality.value, appSettings.set("sync-lastquality", job.Quality || "")); - var selectProfile = form.querySelector("#selectProfile"); - selectProfile && (job.Profile = selectProfile.value); - var txtItemLimit = form.querySelector("#txtItemLimit"); - txtItemLimit && (job.ItemLimit = txtItemLimit.value || null); - var chkSyncNewContent = form.querySelector("#chkSyncNewContent"); - chkSyncNewContent && (job.SyncNewContent = chkSyncNewContent.checked); - var chkUnwatchedOnly = form.querySelector("#chkUnwatchedOnly"); - chkUnwatchedOnly && (job.UnwatchedOnly = chkUnwatchedOnly.checked) + + var txtBitrate = form.querySelector('#txtBitrate'); + var bitrate = txtBitrate ? txtBitrate.value : null; + + if (bitrate) { + bitrate = parseFloat(bitrate) * 1000000; + } + job.Bitrate = bitrate; + + var selectQuality = form.querySelector('#selectQuality'); + if (selectQuality) { + job.Quality = selectQuality.value; + + appSettings.set('sync-lastquality', job.Quality || ''); + } + + var selectProfile = form.querySelector('#selectProfile'); + if (selectProfile) { + job.Profile = selectProfile.value; + } + + var txtItemLimit = form.querySelector('#txtItemLimit'); + if (txtItemLimit) { + job.ItemLimit = txtItemLimit.value || null; + } + + var chkSyncNewContent = form.querySelector('#chkSyncNewContent'); + if (chkSyncNewContent) { + job.SyncNewContent = chkSyncNewContent.checked; + } + + var chkUnwatchedOnly = form.querySelector('#chkUnwatchedOnly'); + if (chkUnwatchedOnly) { + job.UnwatchedOnly = chkUnwatchedOnly.checked; + } } function renderForm(options) { - return new Promise(function(resolve, reject) { - require(["emby-checkbox", "emby-input", "emby-select"], function() { - renderFormInternal(options, connectionManager.deviceId(), resolve) - }) - }) + + return new Promise(function (resolve, reject) { + + require(['emby-checkbox', 'emby-input', 'emby-select'], function () { + + renderFormInternal(options, connectionManager.deviceId(), resolve); + }); + }); } function renderFormInternal(options, defaultTargetId, resolve) { - var elem = options.elem, - dialogOptions = options.dialogOptions, - targets = dialogOptions.Targets, - html = "", - mode = options.mode, - targetContainerClass = "download" === mode ? " hide" : "", - syncTargetLabel = "convert" === mode ? globalize.translate("sharedcomponents#LabelConvertTo") : globalize.translate("sharedcomponents#LabelSyncTo"); - options.readOnlySyncTarget ? (html += '
    ', html += '', html += "
    ") : (html += '
    ', html += '", targets.length || (html += '
    ' + globalize.translate("sharedcomponents#LabelSyncNoTargetsHelp") + "
    "), appHost.supports("externallinks") && (html += '"), html += "
    "), html += '
    ', html += '", html += '
    ', html += "
    ", html += '
    ', html += '", html += '
    ', html += "
    ", html += '
    ', html += '', html += "
    ", -1 !== dialogOptions.Options.indexOf("UnwatchedOnly") && (html += '
    ', html += "", html += "convert" === mode ? '
    ' + globalize.translate("sharedcomponents#ConvertUnwatchedVideosOnlyHelp") + "
    " : '
    ' + globalize.translate("sharedcomponents#SyncUnwatchedVideosOnlyHelp") + "
    ", html += "
    "), -1 !== dialogOptions.Options.indexOf("SyncNewContent") && (html += '
    ', html += "", html += "convert" === mode ? '
    ' + globalize.translate("sharedcomponents#AutomaticallyConvertNewContentHelp") + "
    " : '
    ' + globalize.translate("sharedcomponents#AutomaticallySyncNewContentHelp") + "
    ", html += "
    "), -1 !== dialogOptions.Options.indexOf("ItemLimit") && (html += '
    ', html += '', html += "convert" === mode ? '
    ' + globalize.translate("sharedcomponents#ConvertItemLimitHelp") + "
    " : '
    ' + globalize.translate("sharedcomponents#DownloadItemLimitHelp") + "
    ", html += "
    "), elem.innerHTML = html; - var selectSyncTarget = elem.querySelector("#selectSyncTarget"); - selectSyncTarget && (selectSyncTarget.addEventListener("change", function() { - loadQualityOptions(elem, this.value, options.dialogOptionsFn).then(resolve) - }), selectSyncTarget.dispatchEvent(new CustomEvent("change", { - bubbles: !0 - }))); - var selectProfile = elem.querySelector("#selectProfile"); - selectProfile && (selectProfile.addEventListener("change", function() { - onProfileChange(elem, this.value) - }), dialogOptions.ProfileOptions.length && selectProfile.dispatchEvent(new CustomEvent("change", { - bubbles: !0 - }))); - var selectQuality = elem.querySelector("#selectQuality"); - selectQuality && (selectQuality.addEventListener("change", function() { - onQualityChange(elem, this.value) - }), selectQuality.dispatchEvent(new CustomEvent("change", { - bubbles: !0 - }))), setTimeout(function() { - focusManager.autoFocus(elem) - }, 100) + + var elem = options.elem; + var dialogOptions = options.dialogOptions; + + var targets = dialogOptions.Targets; + + var html = ''; + + var mode = options.mode; + var targetContainerClass = mode === 'download' ? ' hide' : ''; + + var syncTargetLabel = mode === 'convert' ? globalize.translate('sharedcomponents#LabelConvertTo') : globalize.translate('sharedcomponents#LabelSyncTo'); + + if (options.readOnlySyncTarget) { + html += '
    '; + html += ''; + html += '
    '; + } else { + html += '
    '; + html += ''; + if (!targets.length) { + html += '
    ' + globalize.translate('sharedcomponents#LabelSyncNoTargetsHelp') + '
    '; + } + + if (appHost.supports('externallinks')) { + html += ''; + } + html += '
    '; + } + + html += '
    '; + html += ''; + html += '
    '; + html += '
    '; + + html += '
    '; + html += ''; + html += '
    '; + html += '
    '; + + html += '
    '; + html += ''; + html += '
    '; + + if (dialogOptions.Options.indexOf('UnwatchedOnly') !== -1) { + html += '
    '; + html += ''; + + if (mode === 'convert') { + html += '
    ' + globalize.translate('sharedcomponents#ConvertUnwatchedVideosOnlyHelp') + '
    '; + } else { + html += '
    ' + globalize.translate('sharedcomponents#SyncUnwatchedVideosOnlyHelp') + '
    '; + } + + html += '
    '; + } + + if (dialogOptions.Options.indexOf('SyncNewContent') !== -1) { + html += '
    '; + html += ''; + + if (mode === 'convert') { + html += '
    ' + globalize.translate('sharedcomponents#AutomaticallyConvertNewContentHelp') + '
    '; + } else { + html += '
    ' + globalize.translate('sharedcomponents#AutomaticallySyncNewContentHelp') + '
    '; + } + html += '
    '; + } + + if (dialogOptions.Options.indexOf('ItemLimit') !== -1) { + html += '
    '; + html += ''; + + if (mode === 'convert') { + html += '
    ' + globalize.translate('sharedcomponents#ConvertItemLimitHelp') + '
    '; + } else { + html += '
    ' + globalize.translate('sharedcomponents#DownloadItemLimitHelp') + '
    '; + } + + html += '
    '; + } + + //html += '
    '; + //html += ''; + + elem.innerHTML = html; + + var selectSyncTarget = elem.querySelector('#selectSyncTarget'); + if (selectSyncTarget) { + selectSyncTarget.addEventListener('change', function () { + loadQualityOptions(elem, this.value, options.dialogOptionsFn).then(resolve); + }); + selectSyncTarget.dispatchEvent(new CustomEvent('change', { + bubbles: true + })); + } + + var selectProfile = elem.querySelector('#selectProfile'); + if (selectProfile) { + selectProfile.addEventListener('change', function () { + onProfileChange(elem, this.value); + }); + + if (dialogOptions.ProfileOptions.length) { + selectProfile.dispatchEvent(new CustomEvent('change', { + bubbles: true + })); + } + } + + var selectQuality = elem.querySelector('#selectQuality'); + if (selectQuality) { + selectQuality.addEventListener('change', function () { + onQualityChange(elem, this.value); + }); + selectQuality.dispatchEvent(new CustomEvent('change', { + bubbles: true + })); + } + + // This isn't ideal, but allow time for the change handlers above to run + setTimeout(function () { + focusManager.autoFocus(elem); + }, 100); } function showWifiMessage() { - require(["dialog", "appRouter"], function(dialog, appRouter) { + + require(['dialog', 'appRouter'], function (dialog, appRouter) { + var options = { - title: globalize.translate("sharedcomponents#HeaderWaitingForWifi"), - text: globalize.translate("sharedcomponents#WifiRequiredToDownload") - }, - items = []; + + title: globalize.translate('sharedcomponents#HeaderWaitingForWifi'), + text: globalize.translate('sharedcomponents#WifiRequiredToDownload') + }; + + var items = []; + items.push({ - name: options.confirmText || globalize.translate("sharedcomponents#ButtonOk"), - id: "ok", - type: "submit" - }), items.push({ - name: options.cancelText || globalize.translate("sharedcomponents#HeaderDownloadSettings"), - id: "downloadsettings", - type: "cancel" - }), options.buttons = items, dialog(options).then(function(result) { - return "ok" === result ? Promise.resolve() : "downloadsettings" === result ? (appRouter.show(appRouter.getRouteUrl("downloadsettings")), Promise.resolve()) : Promise.reject() - }) - }) + name: options.confirmText || globalize.translate('sharedcomponents#ButtonOk'), + id: 'ok', + type: 'submit' + }); + + items.push({ + name: options.cancelText || globalize.translate('sharedcomponents#HeaderDownloadSettings'), + id: 'downloadsettings', + type: 'cancel' + }); + + options.buttons = items; + + dialog(options).then(function (result) { + + if (result === 'ok') { + return Promise.resolve(); + } + if (result === 'downloadsettings') { + appRouter.show(appRouter.getRouteUrl('downloadsettings')); + return Promise.resolve(); + } + + return Promise.reject(); + }); + }); } function validateNetwork() { - switch (navigator.connection ? navigator.connection.type : null) { - case "cellular": - case "bluetooth": - return showWifiMessage(), !1; + + var network = navigator.connection ? navigator.connection.type : null; + + switch (network) { + + case 'cellular': + case 'bluetooth': + showWifiMessage(); + return false; default: - return !0 + return true; } } function showSyncMenu(options) { - return "download" === options.mode && appSettings.syncOnlyOnWifi() && !validateNetwork() ? Promise.reject() : registrationServices.validateFeature("sync").then(function() { - return showSyncMenuInternal(options) - }) + + if (options.mode === 'download' && appSettings.syncOnlyOnWifi() && !validateNetwork()) { + return Promise.reject(); + } + + return registrationServices.validateFeature('sync').then(function () { + return showSyncMenuInternal(options); + }); } function enableAutoSync(options) { - if ("download" !== options.mode) return !1; + + if (options.mode !== 'download') { + return false; + } + var firstItem = (options.items || [])[0] || {}; - return "Audio" === firstItem.Type || ("MusicAlbum" === firstItem.Type || ("MusicArtist" === firstItem.Type || ("MusicGenre" === firstItem.Type || "Playlist" === firstItem.Type && "Audio" === firstItem.MediaType))) + + if (firstItem.Type === 'Audio') { + return true; + } + if (firstItem.Type === 'MusicAlbum') { + return true; + } + if (firstItem.Type === 'MusicArtist') { + return true; + } + if (firstItem.Type === 'MusicGenre') { + return true; + } + if (firstItem.Type === 'Playlist' && firstItem.MediaType === 'Audio') { + return true; + } + + return false; } function showSyncMenuInternal(options) { - var apiClient = connectionManager.getApiClient(options.serverId), - userId = apiClient.getCurrentUserId(); - if (enableAutoSync(options)) return submitQuickSyncJob(apiClient, userId, apiClient.deviceId(), { - items: options.items, - Quality: "custom", - Bitrate: appSettings.maxStaticMusicBitrate() - }); + + var apiClient = connectionManager.getApiClient(options.serverId); + var userId = apiClient.getCurrentUserId(); + + if (enableAutoSync(options)) { + + return submitQuickSyncJob(apiClient, userId, apiClient.deviceId(), { + items: options.items, + Quality: 'custom', + Bitrate: appSettings.maxStaticMusicBitrate() + }); + } + var dialogOptionsFn = getTargetDialogOptionsFn(apiClient, { UserId: userId, - ItemIds: (options.items || []).map(function(i) { - return i.Id || i - }).join(","), + ItemIds: (options.items || []).map(function (i) { + return i.Id || i; + }).join(','), + ParentId: options.ParentId, Category: options.Category, - IncludeProviders: "convert" === options.mode ? "ConvertSyncProvider" : null, - ExcludeProviders: "convert" === options.mode ? null : "ConvertSyncProvider" + IncludeProviders: options.mode === 'convert' ? 'ConvertSyncProvider' : null, + ExcludeProviders: options.mode === 'convert' ? null : 'ConvertSyncProvider' }); - return dialogOptionsFn().then(function(dialogOptions) { + + return dialogOptionsFn().then(function (dialogOptions) { + currentDialogOptions = dialogOptions; + var dlgElementOptions = { - removeOnClose: !0, - scrollY: !1, - autoFocus: !1 + removeOnClose: true, + scrollY: false, + autoFocus: false }; - layoutManager.tv ? dlgElementOptions.size = "fullscreen" : dlgElementOptions.size = "small"; + + if (layoutManager.tv) { + dlgElementOptions.size = 'fullscreen'; + } else { + dlgElementOptions.size = 'small'; + } + var dlg = dialogHelper.createDialog(dlgElementOptions); - dlg.classList.add("formDialog"); - var html = ""; - html += '
    ', html += '', html += '

    '; - var syncButtonLabel = "download" === options.mode ? globalize.translate("sharedcomponents#Download") : "convert" === options.mode ? globalize.translate("sharedcomponents#Convert") : globalize.translate("sharedcomponents#Sync"); - html += syncButtonLabel, html += "

    ", appHost.supports("externallinks") && (html += 'info' + globalize.translate("sharedcomponents#Help") + ""), html += "
    ", html += '
    ', html += '
    ', html += '
    ', html += '
    ', html += '
    ', html += '", html += "
    ", html += "
    ", html += "
    ", html += "
    ", dlg.innerHTML = html; - var submitted = !1; - dlg.querySelector("form").addEventListener("submit", function(e) { - return submitted = submitJob(dlg, apiClient, userId, options, this), e.preventDefault(), !1 - }), dlg.querySelector(".btnCancel").addEventListener("click", function() { - dialogHelper.close(dlg) - }), layoutManager.tv && scrollHelper.centerFocus.on(dlg.querySelector(".formDialogContent"), !1); + + dlg.classList.add('formDialog'); + + var html = ''; + html += '
    '; + html += ''; + html += '

    '; + + var syncButtonLabel = options.mode === 'download' ? + globalize.translate('sharedcomponents#Download') : + (options.mode === 'convert' ? globalize.translate('sharedcomponents#Convert') : globalize.translate('sharedcomponents#Sync')); + + html += syncButtonLabel; + html += '

    '; + + if (appHost.supports('externallinks')) { + html += 'info' + globalize.translate('sharedcomponents#Help') + ''; + } + + html += '
    '; + + html += '
    '; + html += '
    '; + + html += '
    '; + + html += '
    '; + + html += '
    '; + + html += ''; + html += '
    '; + + html += '
    '; + + html += '
    '; + html += '
    '; + + dlg.innerHTML = html; + + var submitted = false; + + dlg.querySelector('form').addEventListener('submit', function (e) { + + submitted = submitJob(dlg, apiClient, userId, options, this); + + e.preventDefault(); + return false; + }); + + dlg.querySelector('.btnCancel').addEventListener('click', function () { + dialogHelper.close(dlg); + }); + + if (layoutManager.tv) { + scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false); + } + var promise = dialogHelper.open(dlg); - return renderForm({ - elem: dlg.querySelector(".formFields"), + + renderForm({ + elem: dlg.querySelector('.formFields'), dialogOptions: dialogOptions, dialogOptionsFn: dialogOptionsFn, mode: options.mode - }), promise.then(function() { - return layoutManager.tv && scrollHelper.centerFocus.off(dlg.querySelector(".formDialogContent"), !1), submitted ? Promise.resolve() : Promise.reject() - }) - }) + }); + + return promise.then(function () { + if (layoutManager.tv) { + scrollHelper.centerFocus.off(dlg.querySelector('.formDialogContent'), false); + } + + if (submitted) { + return Promise.resolve(); + } + return Promise.reject(); + }); + }); } function getTargetDialogOptionsFn(apiClient, query) { - return function(targetId) { - return query.TargetId = targetId, apiClient.getJSON(apiClient.getUrl("Sync/Options", query)) - } + + return function (targetId) { + + query.TargetId = targetId; + return apiClient.getJSON(apiClient.getUrl('Sync/Options', query)); + }; } function setQualityFieldVisible(form, visible) { - var fldQuality = form.querySelector(".fldQuality"), - selectQuality = form.querySelector("#selectQuality"); - visible ? (fldQuality && fldQuality.classList.remove("hide"), selectQuality && selectQuality.removeAttribute("required")) : (fldQuality && fldQuality.classList.add("hide"), selectQuality && selectQuality.removeAttribute("required")) + + var fldQuality = form.querySelector('.fldQuality'); + var selectQuality = form.querySelector('#selectQuality'); + + if (visible) { + if (fldQuality) { + fldQuality.classList.remove('hide'); + } + if (selectQuality) { + //selectQuality.setAttribute('required', 'required'); + + // This is a hack due to what appears to be a edge bug but it shoudln't matter as the list always has selectable items + selectQuality.removeAttribute('required'); + } + } else { + if (fldQuality) { + fldQuality.classList.add('hide'); + } + if (selectQuality) { + selectQuality.removeAttribute('required'); + } + } } function onProfileChange(form, profileId) { - var options = currentDialogOptions || {}, - profileOptions = options.ProfileOptions || []; - if (profileOptions.length) { - var option = profileOptions.filter(function(o) { - return o.Id === profileId - })[0], - qualityOptions = options.QualityOptions || []; - option ? (form.querySelector(".profileDescription").innerHTML = option.Description || "", setQualityFieldVisible(form, qualityOptions.length > 0 && option.EnableQualityOptions && -1 !== options.Options.indexOf("Quality"))) : (form.querySelector(".profileDescription").innerHTML = "", setQualityFieldVisible(form, qualityOptions.length > 0 && -1 !== options.Options.indexOf("Quality"))) + + var options = currentDialogOptions || {}; + + var profileOptions = options.ProfileOptions || []; + + if (!profileOptions.length) { + return; + } + + var option = profileOptions.filter(function (o) { + return o.Id === profileId; + })[0]; + + var qualityOptions = options.QualityOptions || []; + + if (option) { + form.querySelector('.profileDescription').innerHTML = option.Description || ''; + setQualityFieldVisible(form, qualityOptions.length > 0 && option.EnableQualityOptions && options.Options.indexOf('Quality') !== -1); + } else { + form.querySelector('.profileDescription').innerHTML = ''; + setQualityFieldVisible(form, qualityOptions.length > 0 && options.Options.indexOf('Quality') !== -1); } } function onQualityChange(form, qualityId) { - var options = currentDialogOptions || {}, - option = (options.QualityOptions || []).filter(function(o) { - return o.Id === qualityId - })[0], - qualityDescription = form.querySelector(".qualityDescription"); - qualityDescription.innerHTML = option ? option.Description || "" : ""; - var fldBitrate = form.querySelector(".fldBitrate"), - txtBitrate = form.querySelector("#txtBitrate"); - "custom" === qualityId ? (fldBitrate && fldBitrate.classList.remove("hide"), txtBitrate && txtBitrate.setAttribute("required", "required")) : (fldBitrate && fldBitrate.classList.add("hide"), txtBitrate && txtBitrate.removeAttribute("required")) + + var options = currentDialogOptions || {}; + var option = (options.QualityOptions || []).filter(function (o) { + return o.Id === qualityId; + })[0]; + + var qualityDescription = form.querySelector('.qualityDescription'); + + if (option) { + qualityDescription.innerHTML = option.Description || ''; + } else { + qualityDescription.innerHTML = ''; + } + + var fldBitrate = form.querySelector('.fldBitrate'); + var txtBitrate = form.querySelector('#txtBitrate'); + + if (qualityId === 'custom') { + + if (fldBitrate) { + fldBitrate.classList.remove('hide'); + } + if (txtBitrate) { + txtBitrate.setAttribute('required', 'required'); + } + } else { + if (fldBitrate) { + fldBitrate.classList.add('hide'); + } + if (txtBitrate) { + txtBitrate.removeAttribute('required'); + } + } } function renderTargetDialogOptions(form, options) { + currentDialogOptions = options; - var fldProfile = form.querySelector(".fldProfile"), - selectProfile = form.querySelector("#selectProfile"); - options.ProfileOptions.length && -1 !== options.Options.indexOf("Profile") ? (fldProfile && fldProfile.classList.remove("hide"), selectProfile && selectProfile.setAttribute("required", "required")) : (fldProfile && fldProfile.classList.add("hide"), selectProfile && selectProfile.removeAttribute("required")), setQualityFieldVisible(form, options.QualityOptions.length > 0), selectProfile && (selectProfile.innerHTML = options.ProfileOptions.map(function(o) { - var selectedAttribute = o.IsDefault ? ' selected="selected"' : ""; - return '" - }).join(""), selectProfile.dispatchEvent(new CustomEvent("change", { - bubbles: !0 - }))); - var selectQuality = form.querySelector("#selectQuality"); + + var fldProfile = form.querySelector('.fldProfile'); + var selectProfile = form.querySelector('#selectProfile'); + + if (options.ProfileOptions.length && options.Options.indexOf('Profile') !== -1) { + if (fldProfile) { + fldProfile.classList.remove('hide'); + } + if (selectProfile) { + selectProfile.setAttribute('required', 'required'); + } + } else { + if (fldProfile) { + fldProfile.classList.add('hide'); + } + if (selectProfile) { + selectProfile.removeAttribute('required'); + } + } + + setQualityFieldVisible(form, options.QualityOptions.length > 0); + + if (selectProfile) { + selectProfile.innerHTML = options.ProfileOptions.map(function (o) { + + var selectedAttribute = o.IsDefault ? ' selected="selected"' : ''; + return ''; + + }).join(''); + + selectProfile.dispatchEvent(new CustomEvent('change', { + bubbles: true + })); + } + + var selectQuality = form.querySelector('#selectQuality'); if (selectQuality) { - selectQuality.innerHTML = options.QualityOptions.map(function(o) { - var selectedAttribute = o.IsDefault ? ' selected="selected"' : ""; - return '" - }).join(""); - var lastQuality = appSettings.get("sync-lastquality"); - lastQuality && options.QualityOptions.filter(function(i) { - return i.Id === lastQuality - }).length && (selectQuality.value = lastQuality), selectQuality.dispatchEvent(new CustomEvent("change", { - bubbles: !0 - })) + selectQuality.innerHTML = options.QualityOptions.map(function (o) { + + var selectedAttribute = o.IsDefault ? ' selected="selected"' : ''; + return ''; + + }).join(''); + + var lastQuality = appSettings.get('sync-lastquality'); + if (lastQuality && options.QualityOptions.filter(function (i) { + + return i.Id === lastQuality; + + }).length) { + selectQuality.value = lastQuality; + } + + selectQuality.dispatchEvent(new CustomEvent('change', { + bubbles: true + })); } } function loadQualityOptions(form, targetId, dialogOptionsFn) { - return dialogOptionsFn(targetId).then(function(options) { - return renderTargetDialogOptions(form, options) - }) + + return dialogOptionsFn(targetId).then(function (options) { + + return renderTargetDialogOptions(form, options); + }); } - var currentDialogOptions; + return { + showMenu: showSyncMenu, renderForm: renderForm, setJobValues: setJobValues - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/sync/syncjobeditor.js b/src/bower_components/emby-webcomponents/sync/syncjobeditor.js index fb8e60d054..7a23490ce2 100644 --- a/src/bower_components/emby-webcomponents/sync/syncjobeditor.js +++ b/src/bower_components/emby-webcomponents/sync/syncjobeditor.js @@ -1,221 +1,504 @@ -define(["connectionManager", "serverNotifications", "events", "datetime", "dom", "imageLoader", "loading", "globalize", "apphost", "layoutManager", "scrollHelper", "dialogHelper", "listViewStyle", "paper-icon-button-light", "emby-button", "formDialogStyle", "emby-linkbutton"], function(connectionManager, serverNotifications, events, datetime, dom, imageLoader, loading, globalize, appHost, layoutManager, scrollHelper, dialogHelper) { - "use strict"; +define(['connectionManager', 'serverNotifications', 'events', 'datetime', 'dom', 'imageLoader', 'loading', 'globalize', 'apphost', 'layoutManager', 'scrollHelper', 'dialogHelper', 'listViewStyle', 'paper-icon-button-light', 'emby-button', 'formDialogStyle', 'emby-linkbutton'], function (connectionManager, serverNotifications, events, datetime, dom, imageLoader, loading, globalize, appHost, layoutManager, scrollHelper, dialogHelper) { + 'use strict'; function syncNow() { - require(["localsync"], function(localSync) { - localSync.sync() - }) + require(['localsync'], function (localSync) { + localSync.sync(); + }); } function renderJob(context, job, dialogOptions) { - require(["syncDialog"], function(syncDialog) { + + require(['syncDialog'], function (syncDialog) { syncDialog.renderForm({ - elem: context.querySelector(".syncJobFormContent"), + elem: context.querySelector('.syncJobFormContent'), dialogOptions: dialogOptions, dialogOptionsFn: getTargetDialogOptionsFn(dialogOptions), - readOnlySyncTarget: !0 - }).then(function() { - fillJobValues(context, job, dialogOptions) - }) - }) + readOnlySyncTarget: true + }).then(function () { + fillJobValues(context, job, dialogOptions); + }); + }); } function getTargetDialogOptionsFn(dialogOptions) { - return function(targetId) { - return Promise.resolve(dialogOptions) - } + + return function (targetId) { + + return Promise.resolve(dialogOptions); + }; } function getJobItemHtml(jobItem, apiClient, index) { - var nextAction, html = "", - status = jobItem.Status; - "Failed" === status ? nextAction = "retry" : "Cancelled" === status ? nextAction = "retry" : "Queued" === status || "Transferring" === status || "Converting" === status || "ReadyToTransfer" === status ? nextAction = "cancel" : "Synced" !== status || jobItem.IsMarkedForRemoval || (nextAction = "remove"); - var listItemClass = "listItem listItem-border"; - layoutManager.tv && nextAction && (listItemClass += " btnJobItemMenu"), layoutManager.tv && (listItemClass += " listItem-button"); - var tagName = layoutManager.tv ? "button" : "div"; - html += "<" + tagName + ' type="button" class="' + listItemClass + '" data-itemid="' + jobItem.Id + '" data-status="' + jobItem.Status + '" data-action="' + nextAction + '">'; + + var html = ''; + var status = jobItem.Status; + + var nextAction; + + if (status === 'Failed') { + nextAction = 'retry'; + } + else if (status === 'Cancelled') { + nextAction = 'retry'; + } + else if (status === 'Queued' || status === 'Transferring' || status === 'Converting' || status === 'ReadyToTransfer') { + nextAction = 'cancel'; + } + else if (status === 'Synced' && !jobItem.IsMarkedForRemoval) { + nextAction = 'remove'; + } + + var listItemClass = 'listItem listItem-border'; + if (layoutManager.tv && nextAction) { + listItemClass += ' btnJobItemMenu'; + } + + if (layoutManager.tv) { + listItemClass += ' listItem-button'; + } + + var tagName = layoutManager.tv ? 'button' : 'div'; + html += '<' + tagName + ' type="button" class="' + listItemClass + '" data-itemid="' + jobItem.Id + '" data-status="' + jobItem.Status + '" data-action="' + nextAction + '">'; + var imgUrl; - jobItem.PrimaryImageItemId && (imgUrl = apiClient.getImageUrl(jobItem.PrimaryImageItemId, { - type: "Primary", - width: 80, - tag: jobItem.PrimaryImageTag, - minScale: 1.5 - })), html += imgUrl ? '
    " : 'sync', html += '
    ', html += '

    ', html += jobItem.ItemName, html += "

    ", "Failed" === jobItem.Status ? html += '
    ' : html += '
    ', html += globalize.translate("sharedcomponents#SyncJobItemStatus" + jobItem.Status), "Synced" === jobItem.Status && jobItem.IsMarkedForRemoval && (html += "
    ", html += globalize.translate("sharedcomponents#RemovingFromDevice")), html += "
    ", html += '
    ', html += '
    ', html += "
    ", html += "
    "; - return layoutManager.tv || ("retry" === nextAction ? html += '' : "cancel" === nextAction ? html += '' : "remove" === nextAction && (html += '')), html += "" + + if (jobItem.PrimaryImageItemId) { + + imgUrl = apiClient.getImageUrl(jobItem.PrimaryImageItemId, { + type: "Primary", + width: 80, + tag: jobItem.PrimaryImageTag, + minScale: 1.5 + }); + } + + if (imgUrl) { + html += '
    '; + } + else { + html += 'sync'; + } + + html += '
    '; + + html += '

    '; + html += jobItem.ItemName; + html += '

    '; + + if (jobItem.Status === 'Failed') { + html += '
    '; + } else { + html += '
    '; + } + html += globalize.translate('sharedcomponents#SyncJobItemStatus' + jobItem.Status); + if (jobItem.Status === 'Synced' && jobItem.IsMarkedForRemoval) { + html += '
    '; + html += globalize.translate('sharedcomponents#RemovingFromDevice'); + } + html += '
    '; + + html += '
    '; + html += '
    '; + html += '
    '; + + html += '
    '; + + var moreIcon = ''; + + if (!layoutManager.tv) { + + if (nextAction === 'retry') { + html += ''; + } + else if (nextAction === 'cancel') { + html += ''; + } + else if (nextAction === 'remove') { + html += ''; + } + } + + html += ''; + return html; } function renderJobItems(context, items, apiClient) { - var html = ""; - html += "

    " + globalize.translate("sharedcomponents#Items") + "

    ", html += '
    '; + + var html = ''; + + html += '

    ' + globalize.translate('sharedcomponents#Items') + '

    '; + + html += '
    '; + var index = 0; - html += items.map(function(i) { - return getJobItemHtml(i, apiClient, index++) - }).join(""), html += "
    "; - var elem = context.querySelector(".jobItems"); - elem.innerHTML = html, imageLoader.lazyChildren(elem) + html += items.map(function (i) { + + return getJobItemHtml(i, apiClient, index++); + + }).join(''); + + html += '
    '; + + var elem = context.querySelector('.jobItems'); + elem.innerHTML = html; + imageLoader.lazyChildren(elem); } function parentWithClass(elem, className) { - for (; !elem.classList || !elem.classList.contains(className);) - if (!(elem = elem.parentNode)) return null; - return elem + + while (!elem.classList || !elem.classList.contains(className)) { + elem = elem.parentNode; + + if (!elem) { + return null; + } + } + + return elem; } function showJobItemMenu(elem, jobId, apiClient) { - var action = elem.getAttribute("data-action"), - context = parentWithClass(elem, "formDialog"), - listItem = parentWithClass(elem, "listItem"), - jobItemId = listItem.getAttribute("data-itemid"); - "retry" === action ? retryJobItem(context, jobId, jobItemId, apiClient) : "cancel" !== action && "remove" !== action || cancelJobItem(context, jobId, jobItemId, apiClient) + + var action = elem.getAttribute('data-action'); + var context = parentWithClass(elem, 'formDialog'); + var listItem = parentWithClass(elem, 'listItem'); + var jobItemId = listItem.getAttribute('data-itemid'); + + var menuItems = []; + + if (action === 'retry') { + retryJobItem(context, jobId, jobItemId, apiClient); + } + else if (action === 'cancel' || action === 'remove') { + cancelJobItem(context, jobId, jobItemId, apiClient); + } } function cancelJobItem(context, jobId, jobItemId, apiClient) { - showRemoveConfirm(function() { - loading.show(), apiClient.ajax({ + + showRemoveConfirm(function () { + loading.show(); + + apiClient.ajax({ + type: "DELETE", - url: apiClient.getUrl("Sync/JobItems/" + jobItemId) - }).then(function() { - appHost.supports("sync") && syncNow(), loadJob(context, jobId, apiClient) - }) - }) + url: apiClient.getUrl('Sync/JobItems/' + jobItemId) + + }).then(function () { + + // TODO this should check editor options.mode === 'download' + if (appHost.supports('sync')) { + syncNow(); + } + + loadJob(context, jobId, apiClient); + }); + }); } function retryJobItem(context, jobId, jobItemId, apiClient) { - showRetryConfirm(function() { + + showRetryConfirm(function () { apiClient.ajax({ + type: "POST", - url: apiClient.getUrl("Sync/JobItems/" + jobItemId + "/Enable") - }).then(function() { - appHost.supports("sync") && syncNow(), loadJob(context, jobId, apiClient) - }) - }) + url: apiClient.getUrl('Sync/JobItems/' + jobItemId + '/Enable') + + }).then(function () { + + // TODO this should check editor options.mode === 'download' + if (appHost.supports('sync')) { + syncNow(); + } + + loadJob(context, jobId, apiClient); + }); + }); } function showRetryConfirm(callback) { - require(["confirm"], function(confirm) { + + // TODO Implement this as a retry dialog + require(['confirm'], function (confirm) { + confirm({ - text: globalize.translate("sharedcomponents#ConfirmRemoveDownload"), - confirmText: globalize.translate("sharedcomponents#RemoveDownload"), - cancelText: globalize.translate("sharedcomponents#KeepDownload"), - primary: "cancel" - }).then(callback) - }) + + text: globalize.translate('sharedcomponents#ConfirmRemoveDownload'), + confirmText: globalize.translate('sharedcomponents#RemoveDownload'), + cancelText: globalize.translate('sharedcomponents#KeepDownload'), + primary: 'cancel' + + }).then(callback); + }); } function showRemoveConfirm(callback) { - require(["confirm"], function(confirm) { + + require(['confirm'], function (confirm) { + confirm({ - text: globalize.translate("sharedcomponents#ConfirmRemoveDownload"), - confirmText: globalize.translate("sharedcomponents#RemoveDownload"), - cancelText: globalize.translate("sharedcomponents#KeepDownload"), - primary: "cancel" - }).then(callback) - }) + + text: globalize.translate('sharedcomponents#ConfirmRemoveDownload'), + confirmText: globalize.translate('sharedcomponents#RemoveDownload'), + cancelText: globalize.translate('sharedcomponents#KeepDownload'), + primary: 'cancel' + + }).then(callback); + }); + } + + function showConfirm(text, callback) { + + require(['confirm'], function (confirm) { + + confirm(text).then(callback); + }); } function fillJobValues(context, job, editOptions) { - var selectProfile = context.querySelector("#selectProfile"); - selectProfile && (selectProfile.value = job.Profile || ""); - var selectQuality = context.querySelector("#selectQuality"); - selectQuality && (selectQuality.value = job.Quality || ""); - var chkUnwatchedOnly = context.querySelector("#chkUnwatchedOnly"); - chkUnwatchedOnly && (chkUnwatchedOnly.checked = job.UnwatchedOnly); - var chkSyncNewContent = context.querySelector("#chkSyncNewContent"); - chkSyncNewContent && (chkSyncNewContent.checked = job.SyncNewContent); - var txtItemLimit = context.querySelector("#txtItemLimit"); - txtItemLimit && (txtItemLimit.value = job.ItemLimit); - var txtBitrate = context.querySelector("#txtBitrate"); - job.Bitrate ? txtBitrate.value = job.Bitrate / 1e6 : txtBitrate.value = ""; - var target = editOptions.Targets.filter(function(t) { - return t.Id === job.TargetId - })[0], - targetName = target ? target.Name : "", - selectSyncTarget = context.querySelector("#selectSyncTarget"); - selectSyncTarget && (selectSyncTarget.value = targetName) + + var selectProfile = context.querySelector('#selectProfile'); + if (selectProfile) { + selectProfile.value = job.Profile || ''; + } + + var selectQuality = context.querySelector('#selectQuality'); + if (selectQuality) { + selectQuality.value = job.Quality || ''; + } + + var chkUnwatchedOnly = context.querySelector('#chkUnwatchedOnly'); + if (chkUnwatchedOnly) { + chkUnwatchedOnly.checked = job.UnwatchedOnly; + } + + var chkSyncNewContent = context.querySelector('#chkSyncNewContent'); + if (chkSyncNewContent) { + chkSyncNewContent.checked = job.SyncNewContent; + } + + var txtItemLimit = context.querySelector('#txtItemLimit'); + if (txtItemLimit) { + txtItemLimit.value = job.ItemLimit; + } + + var txtBitrate = context.querySelector('#txtBitrate'); + if (job.Bitrate) { + txtBitrate.value = job.Bitrate / 1000000; + } else { + txtBitrate.value = ''; + } + + var target = editOptions.Targets.filter(function (t) { + return t.Id === job.TargetId; + })[0]; + var targetName = target ? target.Name : ''; + + var selectSyncTarget = context.querySelector('#selectSyncTarget'); + if (selectSyncTarget) { + selectSyncTarget.value = targetName; + } } + var _jobOptions; function loadJob(context, id, apiClient) { - loading.show(), apiClient.getJSON(apiClient.getUrl("Sync/Jobs/" + id)).then(function(job) { - apiClient.getJSON(apiClient.getUrl("Sync/Options", { + + loading.show(); + + apiClient.getJSON(apiClient.getUrl('Sync/Jobs/' + id)).then(function (job) { + + apiClient.getJSON(apiClient.getUrl('Sync/Options', { + UserId: job.UserId, - ItemIds: job.RequestedItemIds && job.RequestedItemIds.length ? job.RequestedItemIds.join("") : null, + ItemIds: (job.RequestedItemIds && job.RequestedItemIds.length ? job.RequestedItemIds.join('') : null), + ParentId: job.ParentId, Category: job.Category, TargetId: job.TargetId - })).then(function(options) { - _jobOptions = options, renderJob(context, job, options), loading.hide() - }) - }), apiClient.getJSON(apiClient.getUrl("Sync/JobItems", { + + })).then(function (options) { + + _jobOptions = options; + renderJob(context, job, options); + loading.hide(); + }); + }); + + apiClient.getJSON(apiClient.getUrl('Sync/JobItems', { + JobId: id, - AddMetadata: !0 - })).then(function(result) { - renderJobItems(context, result.Items, apiClient), loading.hide() - }) + AddMetadata: true + + })).then(function (result) { + + renderJobItems(context, result.Items, apiClient); + loading.hide(); + }); } function loadJobInfo(context, job, jobItems, apiClient) { - renderJobItems(context, jobItems, apiClient), loading.hide() + + //renderJob(page, job, _jobOptions); + renderJobItems(context, jobItems, apiClient); + loading.hide(); } function saveJob(context, id, apiClient) { - loading.show(), apiClient.getJSON(apiClient.getUrl("Sync/Jobs/" + id)).then(function(job) { - require(["syncDialog"], function(syncDialog) { - syncDialog.setJobValues(job, context), apiClient.ajax({ - url: apiClient.getUrl("Sync/Jobs/" + id), - type: "POST", + + loading.show(); + + apiClient.getJSON(apiClient.getUrl('Sync/Jobs/' + id)).then(function (job) { + + require(['syncDialog'], function (syncDialog) { + syncDialog.setJobValues(job, context); + + apiClient.ajax({ + + url: apiClient.getUrl('Sync/Jobs/' + id), + type: 'POST', data: JSON.stringify(job), contentType: "application/json" - }).then(function() { - appHost.supports("sync") && syncNow(), loading.hide(), dialogHelper.close(context) - }) - }) - }) + + }).then(function () { + + // TODO this should check editor options.mode === 'download' + if (appHost.supports('sync')) { + syncNow(); + } + + loading.hide(); + dialogHelper.close(context); + }); + }); + }); + } function startListening(apiClient, jobId) { + var startParams = "0,1500"; - startParams += "," + jobId, apiClient.sendMessage("SyncJobStart", startParams) + + startParams += "," + jobId; + + apiClient.sendMessage("SyncJobStart", startParams); } function stopListening(apiClient) { - apiClient.sendMessage("SyncJobStop", "") + + apiClient.sendMessage("SyncJobStop", ""); } function bindEvents(context, jobId, apiClient) { - context.querySelector(".jobItems").addEventListener("click", function(e) { - var btnJobItemMenu = dom.parentWithClass(e.target, "btnJobItemMenu"); - btnJobItemMenu && showJobItemMenu(btnJobItemMenu, jobId, apiClient) - }) + context.querySelector('.jobItems').addEventListener('click', function (e) { + var btnJobItemMenu = dom.parentWithClass(e.target, 'btnJobItemMenu'); + if (btnJobItemMenu) { + showJobItemMenu(btnJobItemMenu, jobId, apiClient); + } + }); } function showEditor(options) { - function onSyncJobMessage(e, apiClient, msg) { - loadJobInfo(dlg, msg.Job, msg.JobItems, apiClient) + + var apiClient = connectionManager.getApiClient(options.serverId); + var id = options.jobId; + + var dlgElementOptions = { + removeOnClose: true, + scrollY: false, + autoFocus: false + }; + + if (layoutManager.tv) { + dlgElementOptions.size = 'fullscreen'; + } else { + dlgElementOptions.size = 'medium'; } - var apiClient = connectionManager.getApiClient(options.serverId), - id = options.jobId, - dlgElementOptions = { - removeOnClose: !0, - scrollY: !1, - autoFocus: !1 - }; - layoutManager.tv ? dlgElementOptions.size = "fullscreen" : dlgElementOptions.size = "medium"; + var dlg = dialogHelper.createDialog(dlgElementOptions); - dlg.classList.add("formDialog"); - var html = ""; - html += '
    ', html += '', html += '

    ', html += globalize.translate("sharedcomponents#Sync"), html += "

    ", appHost.supports("externallinks") && (html += 'info' + globalize.translate("sharedcomponents#Help") + ""), html += "
    ", html += '
    ', html += '
    ', html += '
    ', html += '
    ', html += '
    ', html += '
    ', html += '", html += "
    ", html += "
    ", html += "
    ", html += "
    ", dlg.innerHTML = html; - dlg.querySelector("form").addEventListener("submit", function(e) { - return saveJob(dlg, id, apiClient), e.preventDefault(), !1 - }), dlg.querySelector(".btnCancel").addEventListener("click", function() { - dialogHelper.close(dlg) - }), layoutManager.tv && scrollHelper.centerFocus.on(dlg.querySelector(".formDialogContent"), !1), loadJob(dlg, id, apiClient), bindEvents(dlg, id, apiClient); + + dlg.classList.add('formDialog'); + + var html = ''; + html += '
    '; + html += ''; + html += '

    '; + html += globalize.translate('sharedcomponents#Sync'); + html += '

    '; + + if (appHost.supports('externallinks')) { + html += 'info' + globalize.translate('sharedcomponents#Help') + ''; + } + + html += '
    '; + + html += '
    '; + html += '
    '; + + html += '
    '; + + html += '
    '; + + html += '
    '; + + html += '
    '; + html += ''; + html += '
    '; + + html += '
    '; + + html += '
    '; + html += '
    '; + + dlg.innerHTML = html; + + var submitted = false; + + dlg.querySelector('form').addEventListener('submit', function (e) { + + saveJob(dlg, id, apiClient); + e.preventDefault(); + return false; + }); + + dlg.querySelector('.btnCancel').addEventListener('click', function () { + dialogHelper.close(dlg); + }); + + if (layoutManager.tv) { + scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false); + } + + function onSyncJobMessage(e, apiClient, msg) { + loadJobInfo(dlg, msg.Job, msg.JobItems, apiClient); + } + + loadJob(dlg, id, apiClient); + bindEvents(dlg, id, apiClient); + var promise = dialogHelper.open(dlg); - return startListening(apiClient, id), events.on(serverNotifications, "SyncJob", onSyncJobMessage), promise.then(function() { - return stopListening(apiClient), events.off(serverNotifications, "SyncJob", onSyncJobMessage), layoutManager.tv && scrollHelper.centerFocus.off(dlg.querySelector(".formDialogContent"), !1), Promise.reject() - }) + + startListening(apiClient, id); + events.on(serverNotifications, "SyncJob", onSyncJobMessage); + + return promise.then(function () { + + stopListening(apiClient); + events.off(serverNotifications, "SyncJob", onSyncJobMessage); + + if (layoutManager.tv) { + scrollHelper.centerFocus.off(dlg.querySelector('.formDialogContent'), false); + } + + if (submitted) { + return Promise.resolve(); + } + return Promise.reject(); + }); } - var _jobOptions; + return { show: showEditor - } + }; + }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/sync/syncjoblist.js b/src/bower_components/emby-webcomponents/sync/syncjoblist.js index aa14d247f9..5deaf6ddb9 100644 --- a/src/bower_components/emby-webcomponents/sync/syncjoblist.js +++ b/src/bower_components/emby-webcomponents/sync/syncjoblist.js @@ -1,209 +1,455 @@ -define(["serverNotifications", "events", "loading", "connectionManager", "imageLoader", "dom", "globalize", "registrationServices", "layoutManager", "listViewStyle"], function(serverNotifications, events, loading, connectionManager, imageLoader, dom, globalize, registrationServices, layoutManager) { - "use strict"; +define(['serverNotifications', 'events', 'loading', 'connectionManager', 'imageLoader', 'dom', 'globalize', 'registrationServices', 'layoutManager', 'listViewStyle'], function (serverNotifications, events, loading, connectionManager, imageLoader, dom, globalize, registrationServices, layoutManager) { + 'use strict'; function onSyncJobCreated(e, apiClient, data) { - fetchData(this) + var listInstance = this; + fetchData(listInstance); } - function onSyncJobUpdated(e, apiClient, data) { - refreshJob(this, data) - } + var listInstance = this; + refreshJob(listInstance, data); + } function onSyncJobCancelled(e, apiClient, data) { - fetchData(this) + var listInstance = this; + fetchData(listInstance); } function refreshList(listInstance, jobs) { for (var i = 0, length = jobs.length; i < length; i++) { - refreshJob(listInstance, jobs[i]) + + var job = jobs[i]; + refreshJob(listInstance, job); } } function syncNow() { - require(["localsync"], function(localSync) { - localSync.sync() - }) + require(['localsync'], function (localSync) { + localSync.sync(); + }); } function cancelJob(listInstance, id) { - require(["confirm"], function(confirm) { + + require(['confirm'], function (confirm) { + + var msg = globalize.translate('sharedcomponents#ConfirmRemoveDownload'); + confirm({ - text: globalize.translate("sharedcomponents#ConfirmRemoveDownload"), - primary: "cancel" - }).then(function() { + + text: msg, + primary: 'cancel' + + }).then(function () { + loading.show(); var apiClient = getApiClient(listInstance); + apiClient.ajax({ - url: apiClient.getUrl("Sync/Jobs/" + id), - type: "DELETE" - }).then(function() { - "download" === listInstance.options.mode && syncNow(), fetchData(listInstance) - }) - }) - }) + + url: apiClient.getUrl('Sync/Jobs/' + id), + type: 'DELETE' + + }).then(function () { + + if (listInstance.options.mode === 'download') { + syncNow(); + } + + fetchData(listInstance); + }); + }); + }); } function refreshJob(listInstance, job) { - var listItem = listInstance.options.element.querySelector(".listItem[data-id='" + job.Id + "']"); - listItem && (listItem.querySelector(".jobStatus").innerHTML = getProgressText(job)) + + var listItem = listInstance.options.element.querySelector('.listItem[data-id=\'' + job.Id + '\']'); + + if (!listItem) { + return; + } + + listItem.querySelector('.jobStatus').innerHTML = getProgressText(job); } function getProgressText(job) { + var status = job.Status; - "Completed" === status && (status = "Synced"); - var html = globalize.translate("sharedcomponents#SyncJobItemStatus" + status); - if ("Transferring" === job.Status || "Converting" === job.Status || "Completed" === job.Status) { - html += " "; - var progress = job.Progress || 0; - progress > 0 && progress < 100 && (progress = progress.toFixed(1)), html += progress + "%" + + if (status === 'Completed') { + status = 'Synced'; } - return html + + var html = globalize.translate('sharedcomponents#SyncJobItemStatus' + status); + + if (job.Status === 'Transferring' || job.Status === 'Converting' || job.Status === 'Completed') { + html += ' '; + + var progress = job.Progress || 0; + if (progress > 0 && progress < 100) { + progress = progress.toFixed(1); + } + html += progress + '%'; + } + + return html; } function getSyncJobHtml(listInstance, job, apiClient) { - var html = "", - tagName = layoutManager.tv ? "button" : "div", - typeAttribute = "button" === tagName ? ' type="button"' : "", - listItemClass = "listItem listItem-border"; - layoutManager.tv && (listItemClass += " listItem-button listItem-focusscale", listItemClass += " btnJobMenu"); - var canEdit = (job.ItemCount || 1) > 1 || "Queued" === job.Status; - html += "<" + tagName + typeAttribute + ' class="' + listItemClass + '" data-canedit="' + canEdit + '" data-id="' + job.Id + '" data-status="' + job.Status + '">'; + + var html = ''; + + var tagName = layoutManager.tv ? 'button' : 'div'; + var typeAttribute = tagName === 'button' ? ' type="button"' : ''; + + var listItemClass = 'listItem listItem-border'; + + if (layoutManager.tv) { + listItemClass += ' listItem-button listItem-focusscale'; + + listItemClass += ' btnJobMenu'; + } + + var canEdit = (job.ItemCount || 1) > 1 || job.Status === 'Queued'; + html += '<' + tagName + typeAttribute + ' class="' + listItemClass + '" data-canedit="' + canEdit + '" data-id="' + job.Id + '" data-status="' + job.Status + '">'; + var imgUrl; - job.PrimaryImageItemId && (imgUrl = apiClient.getImageUrl(job.PrimaryImageItemId, { - type: "Primary", - width: 80, - tag: job.PrimaryImageTag, - minScale: 1.5 - })), imgUrl ? (html += '
    ', html += "
    ") : html += 'file_download'; - var textLines = [], - name = job.Name; - job.ParentName && (name += " - " + job.ParentName), textLines.push(name), 1 === job.ItemCount || textLines.push(globalize.translate("sharedcomponents#ItemCount", job.ItemCount)), html += '
    '; - for (var i = 0, length = textLines.length; i < length; i++) 0 === i ? (html += '

    ', html += textLines[i], html += "

    ") : (html += '
    ', html += textLines[i], html += "
    "); - return html += '
    ', html += getProgressText(job), html += "
    ", html += "
    ", layoutManager.tv || (html += canEdit ? '' : ''), html += "" + + if (job.PrimaryImageItemId) { + + imgUrl = apiClient.getImageUrl(job.PrimaryImageItemId, { + type: "Primary", + width: 80, + tag: job.PrimaryImageTag, + minScale: 1.5 + }); + } + + if (imgUrl) { + html += '
    '; + html += '
    '; + } + else { + html += 'file_download'; + } + + var textLines = []; + + var name = job.Name; + + if (job.ParentName) { + name += ' - ' + job.ParentName; + } + + textLines.push(name); + + if (job.ItemCount === 1) { + //textLines.push(globalize.translate('sharedcomponents#ValueOneItem')); + } else { + textLines.push(globalize.translate('sharedcomponents#ItemCount', job.ItemCount)); + } + + html += '
    '; + + for (var i = 0, length = textLines.length; i < length; i++) { + + if (i === 0) { + html += '

    '; + html += textLines[i]; + html += '

    '; + } else { + html += '
    '; + html += textLines[i]; + html += '
    '; + } + } + + html += '
    '; + html += getProgressText(job); + html += '
    '; + + html += '
    '; + + if (!layoutManager.tv) { + + if (canEdit) { + html += ''; + } else { + html += ''; + } + } + + html += ''; + + return html; } function renderList(listInstance, jobs, apiClient) { - if ((new Date).getTime() - listInstance.lastDataLoad < 6e4) return void refreshList(listInstance, jobs); - listInstance.lastDataLoad = (new Date).getTime(); - for (var html = "", lastTargetName = "", mode = listInstance.options.mode, showTargetName = "download" !== mode, hasOpenSection = !1, i = 0, length = jobs.length; i < length; i++) { + + if ((new Date().getTime() - listInstance.lastDataLoad) < 60000) { + refreshList(listInstance, jobs); + return; + } + + listInstance.lastDataLoad = new Date().getTime(); + + var html = ''; + var lastTargetName = ''; + + var mode = listInstance.options.mode; + var showTargetName = mode !== 'download'; + + var hasOpenSection = false; + + for (var i = 0, length = jobs.length; i < length; i++) { + var job = jobs[i]; if (showTargetName) { - var targetName = job.TargetName || "Unknown"; - targetName !== lastTargetName && (lastTargetName && (html += "
    ", html += "
    ", hasOpenSection = !1), lastTargetName = targetName, html += '
    ', html += '
    ', html += '

    ' + targetName + "

    ", html += "
    ", html += '
    ', hasOpenSection = !0) + var targetName = job.TargetName || 'Unknown'; + + if (targetName !== lastTargetName) { + + if (lastTargetName) { + html += '
    '; + html += '
    '; + hasOpenSection = false; + } + + lastTargetName = targetName; + + html += '
    '; + html += '
    '; + + html += '

    ' + targetName + '

    '; + + html += '
    '; + html += '
    '; + hasOpenSection = true; + } } - html += getSyncJobHtml(listInstance, job, apiClient) + + html += getSyncJobHtml(listInstance, job, apiClient); } - hasOpenSection && (html += "
    ", html += "
    "); - var elem = listInstance.options.element.querySelector(".syncJobListContent"); - html || (html = "download" === mode ? '
    ' + globalize.translate("sharedcomponents#MessageNoDownloadsFound") + "
    " : '
    ' + globalize.translate("sharedcomponents#MessageNoSyncJobsFound") + "
    "), elem.innerHTML = html, imageLoader.lazyChildren(elem) + + if (hasOpenSection) { + html += '
    '; + html += '
    '; + } + + var elem = listInstance.options.element.querySelector('.syncJobListContent'); + + if (!html) { + if (mode === 'download') { + html = '
    ' + globalize.translate('sharedcomponents#MessageNoDownloadsFound') + '
    '; + } else { + html = '
    ' + globalize.translate('sharedcomponents#MessageNoSyncJobsFound') + '
    '; + } + } + + elem.innerHTML = html; + + imageLoader.lazyChildren(elem); } function fetchData(listInstance) { - listInstance.lastDataLoad = 0, loading.show(); - var options = {}, - apiClient = getApiClient(listInstance); - return listInstance.options.userId && (options.UserId = listInstance.options.userId), "download" === listInstance.options.mode && (options.TargetId = apiClient.deviceId()), apiClient.getJSON(apiClient.getUrl("Sync/Jobs", options)).then(function(response) { - renderList(listInstance, response.Items, apiClient), loading.hide() - }) + + listInstance.lastDataLoad = 0; + loading.show(); + + var options = {}; + var apiClient = getApiClient(listInstance); + + if (listInstance.options.userId) { + options.UserId = listInstance.options.userId; + } + + if (listInstance.options.mode === 'download') { + options.TargetId = apiClient.deviceId(); + } + + return apiClient.getJSON(apiClient.getUrl('Sync/Jobs', options)).then(function (response) { + + renderList(listInstance, response.Items, apiClient); + loading.hide(); + }); } function getApiClient(listInstance) { - return connectionManager.getApiClient(listInstance.options.serverId) + return connectionManager.getApiClient(listInstance.options.serverId); } function showJobMenu(listInstance, elem) { - var item = dom.parentWithClass(elem, "listItem"), - jobId = item.getAttribute("data-id"), - menuItems = (item.getAttribute("data-status"), []); - "true" === item.getAttribute("data-canedit") && menuItems.push({ - name: globalize.translate("sharedcomponents#Edit"), - id: "edit" - }); - var txt = globalize.translate("sharedcomponents#RemoveDownload"); + + var item = dom.parentWithClass(elem, 'listItem'); + var jobId = item.getAttribute('data-id'); + var status = item.getAttribute('data-status'); + + var menuItems = []; + + if (item.getAttribute('data-canedit') === 'true') { + menuItems.push({ + name: globalize.translate('sharedcomponents#Edit'), + id: 'edit' + }); + } + + var txt = globalize.translate('sharedcomponents#RemoveDownload'); + menuItems.push({ name: txt, - id: "cancel" - }), require(["actionsheet"], function(actionsheet) { + id: 'cancel' + }); + + require(['actionsheet'], function (actionsheet) { + actionsheet.show({ items: menuItems, positionTo: elem, - callback: function(id) { + callback: function (id) { + switch (id) { - case "delete": - case "cancel": + + case 'delete': cancelJob(listInstance, jobId); break; - case "edit": - showJobEditor(listInstance, elem) + case 'cancel': + cancelJob(listInstance, jobId); + break; + case 'edit': + showJobEditor(listInstance, elem); + break; + default: + break; } } - }) - }) + }); + + }); } function onElementClick(e) { - var listInstance = this, - btnJobMenu = dom.parentWithClass(e.target, "btnJobMenu"); - if (btnJobMenu) return void showJobMenu(listInstance, btnJobMenu); - var btnCancelJob = dom.parentWithClass(e.target, "btnCancelJob"); + + var listInstance = this; + + var btnJobMenu = dom.parentWithClass(e.target, 'btnJobMenu'); + if (btnJobMenu) { + showJobMenu(listInstance, btnJobMenu); + return; + } + + var btnCancelJob = dom.parentWithClass(e.target, 'btnCancelJob'); if (btnCancelJob) { - var listItem = dom.parentWithClass(btnCancelJob, "listItem"); + var listItem = dom.parentWithClass(btnCancelJob, 'listItem'); if (listItem) { - cancelJob(listInstance, listItem.getAttribute("data-id")) + var jobId = listItem.getAttribute('data-id'); + cancelJob(listInstance, jobId); } - } else showJobEditor(listInstance, e.target) + return; + } + + showJobEditor(listInstance, e.target); } function showJobEditor(listInstance, elem) { - var listItem = dom.parentWithClass(elem, "listItem"); - if (listItem && "true" === listItem.getAttribute("data-canedit")) { - var jobId = listItem.getAttribute("data-id"); - require(["syncJobEditor"], function(syncJobEditor) { + + var listItem = dom.parentWithClass(elem, 'listItem'); + if (listItem && listItem.getAttribute('data-canedit') === 'true') { + var jobId = listItem.getAttribute('data-id'); + // edit job + require(['syncJobEditor'], function (syncJobEditor) { syncJobEditor.show({ + serverId: listInstance.options.serverId, jobId: jobId, mode: listInstance.options.mode - }).then(function() { - fetchData(listInstance) - }) - }) + + }).then(function () { + fetchData(listInstance); + }); + }); } } function syncJobList(options) { this.options = options; + var onSyncJobCreatedHandler = onSyncJobCreated.bind(this); - this.onSyncJobCreatedHandler = onSyncJobCreatedHandler, events.on(serverNotifications, "SyncJobCreated", onSyncJobCreatedHandler); + this.onSyncJobCreatedHandler = onSyncJobCreatedHandler; + events.on(serverNotifications, 'SyncJobCreated', onSyncJobCreatedHandler); + var onSyncJobCancelledHandler = onSyncJobCancelled.bind(this); - this.onSyncJobCancelledHandler = onSyncJobCancelledHandler, events.on(serverNotifications, "SyncJobCancelled", onSyncJobCancelledHandler); + this.onSyncJobCancelledHandler = onSyncJobCancelledHandler; + events.on(serverNotifications, 'SyncJobCancelled', onSyncJobCancelledHandler); + var onSyncJobUpdatedHandler = onSyncJobUpdated.bind(this); - this.onSyncJobUpdatedHandler = onSyncJobUpdatedHandler, events.on(serverNotifications, "SyncJobUpdated", onSyncJobUpdatedHandler); + this.onSyncJobUpdatedHandler = onSyncJobUpdatedHandler; + events.on(serverNotifications, 'SyncJobUpdated', onSyncJobUpdatedHandler); + var onClickHandler = onElementClick.bind(this); - options.element.addEventListener("click", onClickHandler), this.onClickHandler = onClickHandler, options.element.innerHTML = '
    ', fetchData(this), initSupporterInfo(options.element, getApiClient(this)) + options.element.addEventListener('click', onClickHandler); + this.onClickHandler = onClickHandler; + + options.element.innerHTML = '
    '; + + fetchData(this); + + initSupporterInfo(options.element, getApiClient(this)); } function showSupporterInfo(context) { + var html = '", html += '", html += "", html += "" + + var html = ''; + + html += '
    '; + html += '
    '; + + html += '
    '; + + html += '

     

    '; + + html += '

    '; + + html += '
    '; + html += '
    '; + + html += '
    '; + + html += '
    '; + + html += ''; + + html += ''; + + // buttons + html += '
    '; + + // main + html += '
    '; + + return html; } function setNextVideoText() { - var instance = this, - elem = instance.options.parent, - secondsRemaining = Math.max(Math.round(getTimeRemainingMs(instance) / 1e3), 0); - console.log("up next seconds remaining: " + secondsRemaining); - var timeText = '' + globalize.translate("sharedcomponents#HeaderSecondsValue", secondsRemaining) + "", - nextVideoText = "Episode" === instance.itemType ? globalize.translate("sharedcomponents#HeaderNextEpisodePlayingInValue", timeText) : globalize.translate("sharedcomponents#HeaderNextVideoPlayingInValue", timeText); - elem.querySelector(".upNextDialog-nextVideoText").innerHTML = nextVideoText + + var instance = this; + + var elem = instance.options.parent; + + var secondsRemaining = Math.max(Math.round(getTimeRemainingMs(instance) / 1000), 0); + + console.log('up next seconds remaining: ' + secondsRemaining); + + var timeText = '' + globalize.translate('sharedcomponents#HeaderSecondsValue', secondsRemaining) + ''; + + var nextVideoText = instance.itemType === 'Episode' ? + globalize.translate('sharedcomponents#HeaderNextEpisodePlayingInValue', timeText) : + globalize.translate('sharedcomponents#HeaderNextVideoPlayingInValue', timeText); + + elem.querySelector('.upNextDialog-nextVideoText').innerHTML = nextVideoText; } function fillItem(item) { - var instance = this, - elem = instance.options.parent; - setPoster(elem.querySelector(".upNextDialog-poster"), item), elem.querySelector(".upNextDialog-overview").innerHTML = item.Overview || "", elem.querySelector(".upNextDialog-mediainfo").innerHTML = mediaInfo.getPrimaryMediaInfoHtml(item, {}); + + var instance = this; + + var elem = instance.options.parent; + + setPoster(elem.querySelector('.upNextDialog-poster'), item); + + elem.querySelector('.upNextDialog-overview').innerHTML = item.Overview || ''; + + elem.querySelector('.upNextDialog-mediainfo').innerHTML = mediaInfo.getPrimaryMediaInfoHtml(item, { + }); + var title = itemHelper.getDisplayName(item); - item.SeriesName && (title = item.SeriesName + " - " + title), elem.querySelector(".upNextDialog-title").innerHTML = title || "", instance.itemType = item.Type, instance.show() + if (item.SeriesName) { + title = item.SeriesName + ' - ' + title; + } + + elem.querySelector('.upNextDialog-title').innerHTML = title || ''; + + instance.itemType = item.Type; + + instance.show(); } function clearCountdownTextTimeout(instance) { - instance._countdownTextTimeout && (clearInterval(instance._countdownTextTimeout), instance._countdownTextTimeout = null) + if (instance._countdownTextTimeout) { + clearInterval(instance._countdownTextTimeout); + instance._countdownTextTimeout = null; + } } function onStartNowClick() { + var options = this.options; + if (options) { + var player = options.player; - this.hide(), playbackManager.nextTrack(player) + + this.hide(); + + playbackManager.nextTrack(player); } } function init(instance, options) { - options.parent.innerHTML = getHtml(), options.parent.classList.add("hide"), options.parent.classList.add("upNextDialog"), options.parent.classList.add("upNextDialog-hidden"), fillItem.call(instance, options.nextItem), options.parent.querySelector(".btnHide").addEventListener("click", instance.hide.bind(instance)), options.parent.querySelector(".btnStartNow").addEventListener("click", onStartNowClick.bind(instance)) + + options.parent.innerHTML = getHtml(); + + options.parent.classList.add('hide'); + options.parent.classList.add('upNextDialog'); + options.parent.classList.add('upNextDialog-hidden'); + + fillItem.call(instance, options.nextItem); + + options.parent.querySelector('.btnHide').addEventListener('click', instance.hide.bind(instance)); + options.parent.querySelector('.btnStartNow').addEventListener('click', onStartNowClick.bind(instance)); } function clearHideAnimationEventListeners(instance, elem) { + var fn = instance._onHideAnimationComplete; - fn && dom.removeEventListener(elem, transitionEndEventName, fn, { - once: !0 - }) + + if (fn) { + dom.removeEventListener(elem, transitionEndEventName, fn, { + once: true + }); + } } function onHideAnimationComplete(e) { - var instance = this, - elem = e.target; - elem.classList.add("hide"), clearHideAnimationEventListeners(instance, elem), events.trigger(instance, "hide") + + var instance = this; + var elem = e.target; + + elem.classList.add('hide'); + + clearHideAnimationEventListeners(instance, elem); + events.trigger(instance, 'hide'); } function hideComingUpNext() { + var instance = this; - if (clearCountdownTextTimeout(this), instance.options) { - var elem = instance.options.parent; - if (elem && (clearHideAnimationEventListeners(this, elem), !elem.classList.contains("upNextDialog-hidden"))) { - elem.offsetWidth, elem.classList.add("upNextDialog-hidden"); - var fn = onHideAnimationComplete.bind(instance); - instance._onHideAnimationComplete = fn, dom.addEventListener(elem, transitionEndEventName, fn, { - once: !0 - }) - } + clearCountdownTextTimeout(this); + + if (!instance.options) { + return; } + + var elem = instance.options.parent; + + if (!elem) { + return; + } + + clearHideAnimationEventListeners(this, elem); + + if (elem.classList.contains('upNextDialog-hidden')) { + return; + } + + // trigger a reflow to force it to animate again + void elem.offsetWidth; + + elem.classList.add('upNextDialog-hidden'); + + var fn = onHideAnimationComplete.bind(instance); + instance._onHideAnimationComplete = fn; + + dom.addEventListener(elem, transitionEndEventName, fn, { + once: true + }); } function getTimeRemainingMs(instance) { + var options = instance.options; if (options) { + var runtimeTicks = playbackManager.duration(options.player); + if (runtimeTicks) { var timeRemainingTicks = runtimeTicks - playbackManager.currentTime(options.player); - return Math.round(timeRemainingTicks / 1e4) + + return Math.round(timeRemainingTicks / 10000); } } - return 0 + + return 0; } function startComingUpNextHideTimer(instance) { - getTimeRemainingMs(instance) <= 0 || (setNextVideoText.call(instance), clearCountdownTextTimeout(instance), instance._countdownTextTimeout = setInterval(setNextVideoText.bind(instance), 400)) + + var timeRemainingMs = getTimeRemainingMs(instance); + + if (timeRemainingMs <= 0) { + return; + } + + setNextVideoText.call(instance); + clearCountdownTextTimeout(instance); + + instance._countdownTextTimeout = setInterval(setNextVideoText.bind(instance), 400); } function UpNextDialog(options) { - this.options = options, init(this, options) + + this.options = options; + + init(this, options); } - var transitionEndEventName = dom.whichTransitionEvent(); - return UpNextDialog.prototype.show = function() { + + UpNextDialog.prototype.show = function () { + var elem = this.options.parent; - clearHideAnimationEventListeners(this, elem), elem.classList.remove("hide"), elem.offsetWidth, elem.classList.remove("upNextDialog-hidden"), layoutManager.tv && setTimeout(function() { - focusManager.focus(elem.querySelector(".btnStartNow")) - }, 50), startComingUpNextHideTimer(this) - }, UpNextDialog.prototype.hide = function() { - hideComingUpNext.call(this) - }, UpNextDialog.prototype.destroy = function() { - hideComingUpNext.call(this), this.options = null, this.itemType = null - }, UpNextDialog + + clearHideAnimationEventListeners(this, elem); + + elem.classList.remove('hide'); + + // trigger a reflow to force it to animate again + void elem.offsetWidth; + + elem.classList.remove('upNextDialog-hidden'); + + if (layoutManager.tv) { + setTimeout(function () { + focusManager.focus(elem.querySelector('.btnStartNow')); + }, 50); + } + + startComingUpNextHideTimer(this); + }; + + UpNextDialog.prototype.hide = function () { + + hideComingUpNext.call(this); + }; + + UpNextDialog.prototype.destroy = function () { + + hideComingUpNext.call(this); + + this.options = null; + this.itemType = null; + }; + + return UpNextDialog; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/userdatabuttons/emby-playstatebutton.js b/src/bower_components/emby-webcomponents/userdatabuttons/emby-playstatebutton.js index d0aa874321..95e9ba8f2e 100644 --- a/src/bower_components/emby-webcomponents/userdatabuttons/emby-playstatebutton.js +++ b/src/bower_components/emby-webcomponents/userdatabuttons/emby-playstatebutton.js @@ -1,64 +1,175 @@ -define(["connectionManager", "serverNotifications", "events", "globalize", "emby-button"], function(connectionManager, serverNotifications, events, globalize, EmbyButtonPrototype) { - "use strict"; +define(['connectionManager', 'serverNotifications', 'events', 'globalize', 'emby-button'], function (connectionManager, serverNotifications, events, globalize, EmbyButtonPrototype) { + 'use strict'; function addNotificationEvent(instance, name, handler) { + var localHandler = handler.bind(instance); - events.on(serverNotifications, name, localHandler), instance[name] = localHandler + events.on(serverNotifications, name, localHandler); + instance[name] = localHandler; } function removeNotificationEvent(instance, name) { + var handler = instance[name]; - handler && (events.off(serverNotifications, name, handler), instance[name] = null) + if (handler) { + events.off(serverNotifications, name, handler); + instance[name] = null; + } } function onClick(e) { - var button = this, - id = button.getAttribute("data-id"), - serverId = button.getAttribute("data-serverid"), - apiClient = connectionManager.getApiClient(serverId); - button.classList.contains("playstatebutton-played") ? (apiClient.markUnplayed(apiClient.getCurrentUserId(), id, new Date), setState(button, !1)) : (apiClient.markPlayed(apiClient.getCurrentUserId(), id, new Date), setState(button, !0)) + + var button = this; + var id = button.getAttribute('data-id'); + var serverId = button.getAttribute('data-serverid'); + var apiClient = connectionManager.getApiClient(serverId); + + if (!button.classList.contains('playstatebutton-played')) { + + apiClient.markPlayed(apiClient.getCurrentUserId(), id, new Date()); + + setState(button, true); + + } else { + + apiClient.markUnplayed(apiClient.getCurrentUserId(), id, new Date()); + + setState(button, false); + } } function onUserDataChanged(e, apiClient, userData) { + var button = this; - userData.ItemId === button.getAttribute("data-id") && setState(button, userData.Played) + + if (userData.ItemId === button.getAttribute('data-id')) { + + setState(button, userData.Played); + } } function setState(button, played, updateAttribute) { + var icon = button.iconElement; - icon || (button.iconElement = button.querySelector("i"), icon = button.iconElement), played ? (button.classList.add("playstatebutton-played"), icon && (icon.classList.add("playstatebutton-icon-played"), icon.classList.remove("playstatebutton-icon-unplayed"))) : (button.classList.remove("playstatebutton-played"), icon && (icon.classList.remove("playstatebutton-icon-played"), icon.classList.add("playstatebutton-icon-unplayed"))), !1 !== updateAttribute && button.setAttribute("data-played", played) + if (!icon) { + button.iconElement = button.querySelector('i'); + icon = button.iconElement; + } + + if (played) { + + button.classList.add('playstatebutton-played'); + + if (icon) { + icon.classList.add('playstatebutton-icon-played'); + icon.classList.remove('playstatebutton-icon-unplayed'); + } + + } else { + + button.classList.remove('playstatebutton-played'); + + if (icon) { + icon.classList.remove('playstatebutton-icon-played'); + icon.classList.add('playstatebutton-icon-unplayed'); + } + } + + if (updateAttribute !== false) { + button.setAttribute('data-played', played); + } } function setTitle(button, itemType) { - button.title = "AudioBook" !== itemType && "AudioPodcast" !== itemType ? globalize.translate("sharedcomponents#Watched") : globalize.translate("sharedcomponents#Played"); - var text = button.querySelector(".button-text"); - text && (text.innerHTML = button.title) + + if (itemType !== 'AudioBook' && itemType !== 'AudioPodcast') { + button.title = globalize.translate('sharedcomponents#Watched'); + } else { + button.title = globalize.translate('sharedcomponents#Played'); + } + + var text = button.querySelector('.button-text'); + if (text) { + text.innerHTML = button.title; + } } function clearEvents(button) { - button.removeEventListener("click", onClick), removeNotificationEvent(button, "UserDataChanged") + + button.removeEventListener('click', onClick); + removeNotificationEvent(button, 'UserDataChanged'); } function bindEvents(button) { - clearEvents(button), button.addEventListener("click", onClick), addNotificationEvent(button, "UserDataChanged", onUserDataChanged) + + clearEvents(button); + + button.addEventListener('click', onClick); + addNotificationEvent(button, 'UserDataChanged', onUserDataChanged); } + var EmbyPlaystateButtonPrototype = Object.create(EmbyButtonPrototype); - EmbyPlaystateButtonPrototype.createdCallback = function() { - EmbyButtonPrototype.createdCallback && EmbyButtonPrototype.createdCallback.call(this) - }, EmbyPlaystateButtonPrototype.attachedCallback = function() { - EmbyButtonPrototype.attachedCallback && EmbyButtonPrototype.attachedCallback.call(this); - var itemId = this.getAttribute("data-id"), - serverId = this.getAttribute("data-serverid"); - itemId && serverId && (setState(this, "true" === this.getAttribute("data-played"), !1), bindEvents(this), setTitle(this, this.getAttribute("data-type"))) - }, EmbyPlaystateButtonPrototype.detachedCallback = function() { - EmbyButtonPrototype.detachedCallback && EmbyButtonPrototype.detachedCallback.call(this), clearEvents(this), this.iconElement = null - }, EmbyPlaystateButtonPrototype.setItem = function(item) { + + EmbyPlaystateButtonPrototype.createdCallback = function () { + + // base method + if (EmbyButtonPrototype.createdCallback) { + EmbyButtonPrototype.createdCallback.call(this); + } + }; + + EmbyPlaystateButtonPrototype.attachedCallback = function () { + + // base method + if (EmbyButtonPrototype.attachedCallback) { + EmbyButtonPrototype.attachedCallback.call(this); + } + + var itemId = this.getAttribute('data-id'); + var serverId = this.getAttribute('data-serverid'); + if (itemId && serverId) { + + setState(this, this.getAttribute('data-played') === 'true', false); + bindEvents(this); + setTitle(this, this.getAttribute('data-type')); + } + }; + + EmbyPlaystateButtonPrototype.detachedCallback = function () { + + // base method + if (EmbyButtonPrototype.detachedCallback) { + EmbyButtonPrototype.detachedCallback.call(this); + } + + clearEvents(this); + this.iconElement = null; + }; + + EmbyPlaystateButtonPrototype.setItem = function (item) { + if (item) { - this.setAttribute("data-id", item.Id), this.setAttribute("data-serverid", item.ServerId); - setState(this, item.UserData && item.UserData.Played), bindEvents(this), setTitle(this, item.Type) - } else this.removeAttribute("data-id"), this.removeAttribute("data-serverid"), this.removeAttribute("data-played"), clearEvents(this) - }, document.registerElement("emby-playstatebutton", { + + this.setAttribute('data-id', item.Id); + this.setAttribute('data-serverid', item.ServerId); + + var played = item.UserData && item.UserData.Played; + setState(this, played); + bindEvents(this); + + setTitle(this, item.Type); + + } else { + + this.removeAttribute('data-id'); + this.removeAttribute('data-serverid'); + this.removeAttribute('data-played'); + clearEvents(this); + } + }; + + document.registerElement('emby-playstatebutton', { prototype: EmbyPlaystateButtonPrototype, - extends: "button" - }) + extends: 'button' + }); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/userdatabuttons/emby-ratingbutton.js b/src/bower_components/emby-webcomponents/userdatabuttons/emby-ratingbutton.js index 2363e9a34e..b50a308177 100644 --- a/src/bower_components/emby-webcomponents/userdatabuttons/emby-ratingbutton.js +++ b/src/bower_components/emby-webcomponents/userdatabuttons/emby-ratingbutton.js @@ -1,78 +1,204 @@ -define(["connectionManager", "serverNotifications", "events", "globalize", "emby-button"], function(connectionManager, serverNotifications, events, globalize, EmbyButtonPrototype) { - "use strict"; +define(['connectionManager', 'serverNotifications', 'events', 'globalize', 'emby-button'], function (connectionManager, serverNotifications, events, globalize, EmbyButtonPrototype) { + 'use strict'; function addNotificationEvent(instance, name, handler) { + var localHandler = handler.bind(instance); - events.on(serverNotifications, name, localHandler), instance[name] = localHandler + events.on(serverNotifications, name, localHandler); + instance[name] = localHandler; } function removeNotificationEvent(instance, name) { + var handler = instance[name]; - handler && (events.off(serverNotifications, name, handler), instance[name] = null) + if (handler) { + events.off(serverNotifications, name, handler); + instance[name] = null; + } } function showPicker(button, apiClient, itemId, likes, isFavorite) { - return apiClient.updateFavoriteStatus(apiClient.getCurrentUserId(), itemId, !isFavorite) + + return apiClient.updateFavoriteStatus(apiClient.getCurrentUserId(), itemId, !isFavorite); } function onClick(e) { - var button = this, - id = button.getAttribute("data-id"), - serverId = button.getAttribute("data-serverid"), - apiClient = connectionManager.getApiClient(serverId), - likes = this.getAttribute("data-likes"), - isFavorite = "true" === this.getAttribute("data-isfavorite"); - likes = "true" === likes || "false" !== likes && null, showPicker(button, apiClient, id, likes, isFavorite).then(function(userData) { - setState(button, userData.Likes, userData.IsFavorite) - }) + + var button = this; + var id = button.getAttribute('data-id'); + var serverId = button.getAttribute('data-serverid'); + var apiClient = connectionManager.getApiClient(serverId); + + var likes = this.getAttribute('data-likes'); + var isFavorite = this.getAttribute('data-isfavorite') === 'true'; + if (likes === 'true') { + likes = true; + } + else if (likes === 'false') { + likes = false; + } else { + likes = null; + } + + showPicker(button, apiClient, id, likes, isFavorite).then(function (userData) { + + setState(button, userData.Likes, userData.IsFavorite); + }); } function onUserDataChanged(e, apiClient, userData) { + var button = this; - userData.ItemId === button.getAttribute("data-id") && setState(button, userData.Likes, userData.IsFavorite) + + if (userData.ItemId === button.getAttribute('data-id')) { + + setState(button, userData.Likes, userData.IsFavorite); + } } function setState(button, likes, isFavorite, updateAttribute) { - var icon = button.querySelector("i"); - isFavorite ? (icon && (icon.innerHTML = "", icon.classList.add("ratingbutton-icon-withrating")), button.classList.add("ratingbutton-withrating")) : (icon && (icon.innerHTML = "", icon.classList.remove("ratingbutton-icon-withrating")), button.classList.remove("ratingbutton-withrating")), !1 !== updateAttribute && (button.setAttribute("data-isfavorite", isFavorite), button.setAttribute("data-likes", null === likes ? "" : likes)) + + var icon = button.querySelector('i'); + + if (isFavorite) { + + if (icon) { + icon.innerHTML = ''; + icon.classList.add('ratingbutton-icon-withrating'); + } + + button.classList.add('ratingbutton-withrating'); + + } else if (likes) { + + if (icon) { + icon.innerHTML = ''; + icon.classList.remove('ratingbutton-icon-withrating'); + //icon.innerHTML = ''; + } + button.classList.remove('ratingbutton-withrating'); + + } else if (likes === false) { + + if (icon) { + icon.innerHTML = ''; + icon.classList.remove('ratingbutton-icon-withrating'); + //icon.innerHTML = ''; + } + button.classList.remove('ratingbutton-withrating'); + + } else { + + if (icon) { + icon.innerHTML = ''; + icon.classList.remove('ratingbutton-icon-withrating'); + //icon.innerHTML = ''; + } + button.classList.remove('ratingbutton-withrating'); + } + + if (updateAttribute !== false) { + button.setAttribute('data-isfavorite', isFavorite); + + button.setAttribute('data-likes', (likes === null ? '' : likes)); + } } function setTitle(button) { - button.title = globalize.translate("sharedcomponents#Favorite"); - var text = button.querySelector(".button-text"); - text && (text.innerHTML = button.title) + button.title = globalize.translate('sharedcomponents#Favorite'); + + var text = button.querySelector('.button-text'); + if (text) { + text.innerHTML = button.title; + } } function clearEvents(button) { - button.removeEventListener("click", onClick), removeNotificationEvent(button, "UserDataChanged") + + button.removeEventListener('click', onClick); + removeNotificationEvent(button, 'UserDataChanged'); } function bindEvents(button) { - clearEvents(button), button.addEventListener("click", onClick), addNotificationEvent(button, "UserDataChanged", onUserDataChanged) + + clearEvents(button); + + button.addEventListener('click', onClick); + addNotificationEvent(button, 'UserDataChanged', onUserDataChanged); } + var EmbyRatingButtonPrototype = Object.create(EmbyButtonPrototype); - EmbyRatingButtonPrototype.createdCallback = function() { - EmbyButtonPrototype.createdCallback && EmbyButtonPrototype.createdCallback.call(this) - }, EmbyRatingButtonPrototype.attachedCallback = function() { - EmbyButtonPrototype.attachedCallback && EmbyButtonPrototype.attachedCallback.call(this); - var itemId = this.getAttribute("data-id"), - serverId = this.getAttribute("data-serverid"); - if (itemId && serverId) { - var likes = this.getAttribute("data-likes"), - isFavorite = "true" === this.getAttribute("data-isfavorite"); - likes = "true" === likes || "false" !== likes && null, setState(this, likes, isFavorite, !1), bindEvents(this) + + EmbyRatingButtonPrototype.createdCallback = function () { + + // base method + if (EmbyButtonPrototype.createdCallback) { + EmbyButtonPrototype.createdCallback.call(this); } - setTitle(this) - }, EmbyRatingButtonPrototype.detachedCallback = function() { - EmbyButtonPrototype.detachedCallback && EmbyButtonPrototype.detachedCallback.call(this), clearEvents(this) - }, EmbyRatingButtonPrototype.setItem = function(item) { + }; + + EmbyRatingButtonPrototype.attachedCallback = function () { + + // base method + if (EmbyButtonPrototype.attachedCallback) { + EmbyButtonPrototype.attachedCallback.call(this); + } + + var itemId = this.getAttribute('data-id'); + var serverId = this.getAttribute('data-serverid'); + if (itemId && serverId) { + + var likes = this.getAttribute('data-likes'); + var isFavorite = this.getAttribute('data-isfavorite') === 'true'; + if (likes === 'true') { + likes = true; + } + else if (likes === 'false') { + likes = false; + } else { + likes = null; + } + + setState(this, likes, isFavorite, false); + bindEvents(this); + } + + setTitle(this); + }; + + EmbyRatingButtonPrototype.detachedCallback = function () { + + // base method + if (EmbyButtonPrototype.detachedCallback) { + EmbyButtonPrototype.detachedCallback.call(this); + } + + clearEvents(this); + }; + + EmbyRatingButtonPrototype.setItem = function (item) { + if (item) { - this.setAttribute("data-id", item.Id), this.setAttribute("data-serverid", item.ServerId); + + this.setAttribute('data-id', item.Id); + this.setAttribute('data-serverid', item.ServerId); + var userData = item.UserData || {}; - setState(this, userData.Likes, userData.IsFavorite), bindEvents(this) - } else this.removeAttribute("data-id"), this.removeAttribute("data-serverid"), this.removeAttribute("data-likes"), this.removeAttribute("data-isfavorite"), clearEvents(this) - }, document.registerElement("emby-ratingbutton", { + setState(this, userData.Likes, userData.IsFavorite); + bindEvents(this); + + } else { + + this.removeAttribute('data-id'); + this.removeAttribute('data-serverid'); + this.removeAttribute('data-likes'); + this.removeAttribute('data-isfavorite'); + clearEvents(this); + } + }; + + document.registerElement('emby-ratingbutton', { prototype: EmbyRatingButtonPrototype, - extends: "button" - }) + extends: 'button' + }); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/userdatabuttons/userdatabuttons.css b/src/bower_components/emby-webcomponents/userdatabuttons/userdatabuttons.css index 253504a2b1..0138631734 100644 --- a/src/bower_components/emby-webcomponents/userdatabuttons/userdatabuttons.css +++ b/src/bower_components/emby-webcomponents/userdatabuttons/userdatabuttons.css @@ -1,3 +1,3 @@ .btnUserDataOn { - color: #c33 !important + color: #cc3333 !important; } \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/userdatabuttons/userdatabuttons.js b/src/bower_components/emby-webcomponents/userdatabuttons/userdatabuttons.js index b1b5631468..7c3fc654f7 100644 --- a/src/bower_components/emby-webcomponents/userdatabuttons/userdatabuttons.js +++ b/src/bower_components/emby-webcomponents/userdatabuttons/userdatabuttons.js @@ -1,110 +1,251 @@ -define(["connectionManager", "globalize", "dom", "itemHelper", "paper-icon-button-light", "material-icons", "emby-button", "css!./userdatabuttons"], function(connectionManager, globalize, dom, itemHelper) { - "use strict"; +define(['connectionManager', 'globalize', 'dom', 'itemHelper', 'paper-icon-button-light', 'material-icons', 'emby-button', 'css!./userdatabuttons'], function (connectionManager, globalize, dom, itemHelper) { + 'use strict'; - function getUserDataButtonHtml(method, itemId, serverId, buttonCssClass, iconCssClass, icon, tooltip, style) { - "fab-mini" === style && (style = "fab", buttonCssClass = buttonCssClass ? buttonCssClass + " mini" : "mini"); - var is = "fab" === style ? "emby-button" : "paper-icon-button-light", - className = "fab" === style ? "autoSize fab" : "autoSize"; - return buttonCssClass && (className += " " + buttonCssClass), iconCssClass ? iconCssClass += " " : iconCssClass = "", iconCssClass += "md-icon", '" - } - - function onContainerClick(e) { - var btnUserData = dom.parentWithClass(e.target, "btnUserData"); - if (btnUserData) { - var method = btnUserData.getAttribute("data-method"); - userDataMethods[method](btnUserData) - } - } - - function fill(options) { - var html = getIconsHtml(options); - "insertAdjacent" === options.fillMode ? options.element.insertAdjacentHTML(options.insertLocation || "beforeend", html) : options.element.innerHTML = html, dom.removeEventListener(options.element, "click", onContainerClick, { - passive: !0 - }), dom.addEventListener(options.element, "click", onContainerClick, { - passive: !0 - }) - } - - function destroy(options) { - options.element.innerHTML = "", dom.removeEventListener(options.element, "click", onContainerClick, { - passive: !0 - }) - } - - function getIconsHtml(options) { - var item = options.item, - includePlayed = options.includePlayed, - cssClass = options.cssClass, - style = options.style, - html = "", - userData = item.UserData || {}, - itemId = item.Id; - if (itemHelper.isLocalItem(item)) return html; - var btnCssClass = "btnUserData"; - cssClass && (btnCssClass += " " + cssClass); - var iconCssClass = options.iconCssClass, - serverId = item.ServerId; - if (!1 !== includePlayed) { - var tooltipPlayed = globalize.translate("sharedcomponents#MarkPlayed"); - itemHelper.canMarkPlayed(item) && (html += userData.Played ? getUserDataButtonHtml("markPlayed", itemId, serverId, btnCssClass + " btnUserDataOn", iconCssClass, "", tooltipPlayed, style) : getUserDataButtonHtml("markPlayed", itemId, serverId, btnCssClass, iconCssClass, "", tooltipPlayed, style)) - } - var tooltipFavorite = globalize.translate("sharedcomponents#Favorite"); - return html += userData.IsFavorite ? getUserDataButtonHtml("markFavorite", itemId, serverId, btnCssClass + " btnUserData btnUserDataOn", iconCssClass, "", tooltipFavorite, style) : getUserDataButtonHtml("markFavorite", itemId, serverId, btnCssClass + " btnUserData", iconCssClass, "", tooltipFavorite, style) - } - - function markFavorite(link) { - var id = link.getAttribute("data-itemid"), - serverId = link.getAttribute("data-serverid"), - markAsFavorite = !link.classList.contains("btnUserDataOn"); - favorite(id, serverId, markAsFavorite), markAsFavorite ? link.classList.add("btnUserDataOn") : link.classList.remove("btnUserDataOn") - } - - function markLike(link) { - var id = link.getAttribute("data-itemid"), - serverId = link.getAttribute("data-serverid"); - link.classList.contains("btnUserDataOn") ? (clearLike(id, serverId), link.classList.remove("btnUserDataOn")) : (likes(id, serverId, !0), link.classList.add("btnUserDataOn")), link.parentNode.querySelector(".btnDislike").classList.remove("btnUserDataOn") - } - - function markDislike(link) { - var id = link.getAttribute("data-itemid"), - serverId = link.getAttribute("data-serverid"); - link.classList.contains("btnUserDataOn") ? (clearLike(id, serverId), link.classList.remove("btnUserDataOn")) : (likes(id, serverId, !1), link.classList.add("btnUserDataOn")), link.parentNode.querySelector(".btnLike").classList.remove("btnUserDataOn") - } - - function markPlayed(link) { - var id = link.getAttribute("data-itemid"), - serverId = link.getAttribute("data-serverid"); - link.classList.contains("btnUserDataOn") ? (played(id, serverId, !1), link.classList.remove("btnUserDataOn")) : (played(id, serverId, !0), link.classList.add("btnUserDataOn")) - } - - function likes(id, serverId, isLiked) { - var apiClient = connectionManager.getApiClient(serverId); - return apiClient.updateUserItemRating(apiClient.getCurrentUserId(), id, isLiked) - } - - function played(id, serverId, isPlayed) { - var apiClient = connectionManager.getApiClient(serverId); - return apiClient[isPlayed ? "markPlayed" : "markUnplayed"](apiClient.getCurrentUserId(), id, new Date) - } - - function favorite(id, serverId, isFavorite) { - var apiClient = connectionManager.getApiClient(serverId); - return apiClient.updateFavoriteStatus(apiClient.getCurrentUserId(), id, isFavorite) - } - - function clearLike(id, serverId) { - var apiClient = connectionManager.getApiClient(serverId); - return apiClient.clearUserItemRating(apiClient.getCurrentUserId(), id) - } var userDataMethods = { markPlayed: markPlayed, markDislike: markDislike, markLike: markLike, markFavorite: markFavorite }; + + function getUserDataButtonHtml(method, itemId, serverId, buttonCssClass, iconCssClass, icon, tooltip, style) { + + if (style === 'fab-mini') { + style = 'fab'; + buttonCssClass = buttonCssClass ? (buttonCssClass + ' mini') : 'mini'; + } + + var is = style === 'fab' ? 'emby-button' : 'paper-icon-button-light'; + var className = style === 'fab' ? 'autoSize fab' : 'autoSize'; + + if (buttonCssClass) { + className += ' ' + buttonCssClass; + } + + if (iconCssClass) { + iconCssClass += ' '; + } else { + iconCssClass = ''; + } + + iconCssClass += 'md-icon'; + + return ''; + } + + function onContainerClick(e) { + + var btnUserData = dom.parentWithClass(e.target, 'btnUserData'); + + if (!btnUserData) { + return; + } + + var method = btnUserData.getAttribute('data-method'); + userDataMethods[method](btnUserData); + } + + function fill(options) { + + var html = getIconsHtml(options); + + if (options.fillMode === 'insertAdjacent') { + options.element.insertAdjacentHTML(options.insertLocation || 'beforeend', html); + } else { + options.element.innerHTML = html; + } + + dom.removeEventListener(options.element, 'click', onContainerClick, { + passive: true + }); + + dom.addEventListener(options.element, 'click', onContainerClick, { + passive: true + }); + } + + function destroy(options) { + + options.element.innerHTML = ''; + + dom.removeEventListener(options.element, 'click', onContainerClick, { + passive: true + }); + } + + function getIconsHtml(options) { + + var item = options.item; + var includePlayed = options.includePlayed; + var cssClass = options.cssClass; + var style = options.style; + + var html = ''; + + var userData = item.UserData || {}; + + var itemId = item.Id; + + if (itemHelper.isLocalItem(item)) { + return html; + } + + var btnCssClass = "btnUserData"; + + if (cssClass) { + btnCssClass += " " + cssClass; + } + + var iconCssClass = options.iconCssClass; + + var serverId = item.ServerId; + + if (includePlayed !== false) { + var tooltipPlayed = globalize.translate('sharedcomponents#MarkPlayed'); + + if (itemHelper.canMarkPlayed(item)) { + if (userData.Played) { + html += getUserDataButtonHtml('markPlayed', itemId, serverId, btnCssClass + ' btnUserDataOn', iconCssClass, '', tooltipPlayed, style); + } else { + html += getUserDataButtonHtml('markPlayed', itemId, serverId, btnCssClass, iconCssClass, '', tooltipPlayed, style); + } + } + } + + //var tooltipLike = globalize.translate('sharedcomponents#Like'); + //var tooltipDislike = globalize.translate('sharedcomponents#Dislike'); + + //if (typeof userData.Likes == "undefined") { + // html += getUserDataButtonHtml('markDislike', itemId, serverId, btnCssClass + ' btnUserData btnDislike', 'thumb-down', tooltipDislike); + // html += getUserDataButtonHtml('markLike', itemId, serverId, btnCssClass + ' btnUserData btnLike', 'thumb-up', tooltipLike); + //} + //else if (userData.Likes) { + // html += getUserDataButtonHtml('markDislike', itemId, serverId, btnCssClass + ' btnUserData btnDislike', 'thumb-down', tooltipDislike); + // html += getUserDataButtonHtml('markLike', itemId, serverId, btnCssClass + ' btnUserData btnLike btnUserDataOn', 'thumb-up', tooltipLike); + //} + //else { + // html += getUserDataButtonHtml('markDislike', itemId, serverId, btnCssClass + ' btnUserData btnDislike btnUserDataOn', 'thumb-down', tooltipDislike); + // html += getUserDataButtonHtml('markLike', itemId, serverId, btnCssClass + ' btnUserData btnLike', 'thumb-up', tooltipLike); + //} + + var tooltipFavorite = globalize.translate('sharedcomponents#Favorite'); + if (userData.IsFavorite) { + + html += getUserDataButtonHtml('markFavorite', itemId, serverId, btnCssClass + ' btnUserData btnUserDataOn', iconCssClass, '', tooltipFavorite, style); + } else { + html += getUserDataButtonHtml('markFavorite', itemId, serverId, btnCssClass + ' btnUserData', iconCssClass, '', tooltipFavorite, style); + } + + return html; + } + + function markFavorite(link) { + + var id = link.getAttribute('data-itemid'); + var serverId = link.getAttribute('data-serverid'); + + var markAsFavorite = !link.classList.contains('btnUserDataOn'); + + favorite(id, serverId, markAsFavorite); + + if (markAsFavorite) { + link.classList.add('btnUserDataOn'); + } else { + link.classList.remove('btnUserDataOn'); + } + } + + function markLike(link) { + + var id = link.getAttribute('data-itemid'); + var serverId = link.getAttribute('data-serverid'); + + if (!link.classList.contains('btnUserDataOn')) { + + likes(id, serverId, true); + + link.classList.add('btnUserDataOn'); + + } else { + + clearLike(id, serverId); + + link.classList.remove('btnUserDataOn'); + } + + link.parentNode.querySelector('.btnDislike').classList.remove('btnUserDataOn'); + } + + function markDislike(link) { + + var id = link.getAttribute('data-itemid'); + var serverId = link.getAttribute('data-serverid'); + + if (!link.classList.contains('btnUserDataOn')) { + + likes(id, serverId, false); + + link.classList.add('btnUserDataOn'); + + } else { + + clearLike(id, serverId); + + link.classList.remove('btnUserDataOn'); + } + + link.parentNode.querySelector('.btnLike').classList.remove('btnUserDataOn'); + } + + function markPlayed(link) { + + var id = link.getAttribute('data-itemid'); + var serverId = link.getAttribute('data-serverid'); + + if (!link.classList.contains('btnUserDataOn')) { + + played(id, serverId, true); + + link.classList.add('btnUserDataOn'); + + } else { + + played(id, serverId, false); + + link.classList.remove('btnUserDataOn'); + } + } + + function likes(id, serverId, isLiked) { + var apiClient = connectionManager.getApiClient(serverId); + return apiClient.updateUserItemRating(apiClient.getCurrentUserId(), id, isLiked); + } + + function played(id, serverId, isPlayed) { + var apiClient = connectionManager.getApiClient(serverId); + + var method = isPlayed ? 'markPlayed' : 'markUnplayed'; + + return apiClient[method](apiClient.getCurrentUserId(), id, new Date()); + } + + function favorite(id, serverId, isFavorite) { + var apiClient = connectionManager.getApiClient(serverId); + + return apiClient.updateFavoriteStatus(apiClient.getCurrentUserId(), id, isFavorite); + } + + function clearLike(id, serverId) { + + var apiClient = connectionManager.getApiClient(serverId); + + return apiClient.clearUserItemRating(apiClient.getCurrentUserId(), id); + } + return { fill: fill, destroy: destroy, getIconsHtml: getIconsHtml - } + }; + }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/usersettings/usersettings.js b/src/bower_components/emby-webcomponents/usersettings/usersettings.js index 0712e46299..6b3dc90d49 100644 --- a/src/bower_components/emby-webcomponents/usersettings/usersettings.js +++ b/src/bower_components/emby-webcomponents/usersettings/usersettings.js @@ -1,4 +1,5 @@ -define(["userSettingsBuilder"], function(userSettingsBuilder) { - "use strict"; - return new userSettingsBuilder +define(['userSettingsBuilder'], function (userSettingsBuilder) { + 'use strict'; + + return new userSettingsBuilder(); }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/usersettings/usersettingsbuilder.js b/src/bower_components/emby-webcomponents/usersettings/usersettingsbuilder.js index 568b1fcaa6..6d0bc58d06 100644 --- a/src/bower_components/emby-webcomponents/usersettings/usersettingsbuilder.js +++ b/src/bower_components/emby-webcomponents/usersettings/usersettingsbuilder.js @@ -1,86 +1,324 @@ -define(["appSettings", "events"], function(appsettings, events) { - "use strict"; +define(['appSettings', 'events'], function (appsettings, events) { + 'use strict'; function onSaveTimeout() { + var self = this; - self.saveTimeout = null, self.currentApiClient.updateDisplayPreferences("usersettings", self.displayPrefs, self.currentUserId, "emby") + self.saveTimeout = null; + self.currentApiClient.updateDisplayPreferences('usersettings', self.displayPrefs, self.currentUserId, 'emby'); } function saveServerPreferences(instance) { - instance.saveTimeout && clearTimeout(instance.saveTimeout), instance.saveTimeout = setTimeout(onSaveTimeout.bind(instance), 50) + if (instance.saveTimeout) { + clearTimeout(instance.saveTimeout); + } + instance.saveTimeout = setTimeout(onSaveTimeout.bind(instance), 50); } - function UserSettings() {} - return UserSettings.prototype.setUserInfo = function(userId, apiClient) { - if (this.saveTimeout && clearTimeout(this.saveTimeout), this.currentUserId = userId, this.currentApiClient = apiClient, !userId) return this.displayPrefs = null, Promise.resolve(); + function UserSettings() { + } + + UserSettings.prototype.setUserInfo = function (userId, apiClient) { + + if (this.saveTimeout) { + clearTimeout(this.saveTimeout); + } + + this.currentUserId = userId; + this.currentApiClient = apiClient; + + if (!userId) { + this.displayPrefs = null; + return Promise.resolve(); + } + var self = this; - return apiClient.getDisplayPreferences("usersettings", userId, "emby").then(function(result) { - result.CustomPrefs = result.CustomPrefs || {}, self.displayPrefs = result - }) - }, UserSettings.prototype.getData = function() { - return this.displayPrefs - }, UserSettings.prototype.importFrom = function(instance) { - this.displayPrefs = instance.getData() - }, UserSettings.prototype.set = function(name, value, enableOnServer) { + + return apiClient.getDisplayPreferences('usersettings', userId, 'emby').then(function (result) { + result.CustomPrefs = result.CustomPrefs || {}; + self.displayPrefs = result; + }); + }; + + UserSettings.prototype.getData = function () { + return this.displayPrefs; + }; + + UserSettings.prototype.importFrom = function (instance) { + this.displayPrefs = instance.getData(); + }; + + UserSettings.prototype.set = function (name, value, enableOnServer) { + var userId = this.currentUserId; - if (!userId) throw new Error("userId cannot be null"); - var currentValue = this.get(name, enableOnServer), - result = appsettings.set(name, value, userId); - return !1 !== enableOnServer && this.displayPrefs && (this.displayPrefs.CustomPrefs[name] = null == value ? value : value.toString(), saveServerPreferences(this)), currentValue !== value && events.trigger(this, "change", [name]), result - }, UserSettings.prototype.get = function(name, enableOnServer) { + if (!userId) { + throw new Error('userId cannot be null'); + } + + var currentValue = this.get(name, enableOnServer); + var result = appsettings.set(name, value, userId); + + if (enableOnServer !== false && this.displayPrefs) { + this.displayPrefs.CustomPrefs[name] = value == null ? value : value.toString(); + saveServerPreferences(this); + } + + if (currentValue !== value) { + events.trigger(this, 'change', [name]); + } + + return result; + }; + + UserSettings.prototype.get = function (name, enableOnServer) { var userId = this.currentUserId; - return userId ? !1 !== enableOnServer && this.displayPrefs ? this.displayPrefs.CustomPrefs[name] : appsettings.get(name, userId) : null - }, UserSettings.prototype.serverConfig = function(config) { + if (!userId) { + // TODO: I'd like to continue to throw this exception but it causes issues with offline use + // Revisit in the future and restore it + return null; + //throw new Error('userId cannot be null'); + } + + if (enableOnServer !== false) { + if (this.displayPrefs) { + return this.displayPrefs.CustomPrefs[name]; + } + } + + return appsettings.get(name, userId); + }; + + UserSettings.prototype.serverConfig = function (config) { + var apiClient = this.currentApiClient; - return config ? apiClient.updateUserConfiguration(this.currentUserId, config) : apiClient.getUser(this.currentUserId).then(function(user) { - return user.Configuration - }) - }, UserSettings.prototype.enableCinemaMode = function(val) { - return null != val ? this.set("enableCinemaMode", val.toString(), !1) : !(val = this.get("enableCinemaMode", !1)) || "false" !== val - }, UserSettings.prototype.enableNextVideoInfoOverlay = function(val) { - return null != val ? this.set("enableNextVideoInfoOverlay", val.toString()) : "false" !== (val = this.get("enableNextVideoInfoOverlay")) - }, UserSettings.prototype.enableThemeSongs = function(val) { - return null != val ? this.set("enableThemeSongs", val.toString(), !1) : "false" !== (val = this.get("enableThemeSongs", !1)) - }, UserSettings.prototype.enableThemeVideos = function(val) { - return null != val ? this.set("enableThemeVideos", val.toString(), !1) : (val = this.get("enableThemeVideos", !1), val ? "false" !== val : UserSettings.defaults.enableThemeVideos) - }, UserSettings.prototype.enableBackdrops = function(val) { - return null != val ? this.set("enableBackdrops", val.toString(), !1) : (val = this.get("enableBackdrops", !1), val ? "false" !== val : UserSettings.defaults.enableBackdrops) - }, UserSettings.prototype.language = function(val) { - return null != val ? this.set("language", val.toString(), !1) : this.get("language", !1) - }, UserSettings.prototype.dateTimeLocale = function(val) { - return null != val ? this.set("datetimelocale", val.toString(), !1) : this.get("datetimelocale", !1) - }, UserSettings.prototype.skipBackLength = function(val) { - return null != val ? this.set("skipBackLength", val.toString()) : parseInt(this.get("skipBackLength") || "10000") - }, UserSettings.prototype.skipForwardLength = function(val) { - return null != val ? this.set("skipForwardLength", val.toString()) : parseInt(this.get("skipForwardLength") || "30000") - }, UserSettings.prototype.dashboardTheme = function(val) { - return null != val ? this.set("dashboardTheme", val) : this.get("dashboardTheme") - }, UserSettings.prototype.skin = function(val) { - return null != val ? this.set("skin", val, !1) : this.get("skin", !1) || UserSettings.defaults.skin - }, UserSettings.prototype.theme = function(val) { - return null != val ? this.set("appTheme", val, !1) : this.get("appTheme", !1) || UserSettings.defaults.theme - }, UserSettings.prototype.enableSeasonalThemes = function(val) { - return null != val ? this.set("enableSeasonalThemes", val, !1) : "false" !== this.get("enableSeasonalThemes", !1) - }, UserSettings.prototype.screensaver = function(val) { - return null != val ? this.set("screensaver", val, !1) : this.get("screensaver", !1) || UserSettings.defaults.screensaver - }, UserSettings.prototype.soundEffects = function(val) { - return null != val ? this.set("soundeffects", val, !1) : this.get("soundeffects", !1) || UserSettings.defaults.soundEffects - }, UserSettings.defaults = { + + if (config) { + + return apiClient.updateUserConfiguration(this.currentUserId, config); + + } else { + + return apiClient.getUser(this.currentUserId).then(function (user) { + + return user.Configuration; + }); + } + }; + + UserSettings.prototype.enableCinemaMode = function (val) { + + if (val != null) { + return this.set('enableCinemaMode', val.toString(), false); + } + + val = this.get('enableCinemaMode', false); + + if (val) { + return val !== 'false'; + } + + return true; + }; + + UserSettings.prototype.enableNextVideoInfoOverlay = function (val) { + + if (val != null) { + return this.set('enableNextVideoInfoOverlay', val.toString()); + } + + val = this.get('enableNextVideoInfoOverlay'); + + return val !== 'false'; + }; + + UserSettings.prototype.enableThemeSongs = function (val) { + + if (val != null) { + return this.set('enableThemeSongs', val.toString(), false); + } + + val = this.get('enableThemeSongs', false); + + return val !== 'false'; + }; + + UserSettings.prototype.enableThemeVideos = function (val) { + + if (val != null) { + return this.set('enableThemeVideos', val.toString(), false); + } + + val = this.get('enableThemeVideos', false); + + if (val) { + return val !== 'false'; + } + + return UserSettings.defaults.enableThemeVideos; + }; + + UserSettings.prototype.enableBackdrops = function (val) { + + if (val != null) { + return this.set('enableBackdrops', val.toString(), false); + } + + val = this.get('enableBackdrops', false); + + if (val) { + return val !== 'false'; + } + + return UserSettings.defaults.enableBackdrops; + }; + + UserSettings.prototype.language = function (val) { + + if (val != null) { + return this.set('language', val.toString(), false); + } + + return this.get('language', false); + }; + + UserSettings.prototype.dateTimeLocale = function (val) { + + if (val != null) { + return this.set('datetimelocale', val.toString(), false); + } + + return this.get('datetimelocale', false); + }; + + UserSettings.prototype.skipBackLength = function (val) { + + if (val != null) { + return this.set('skipBackLength', val.toString()); + } + + return parseInt(this.get('skipBackLength') || '10000'); + }; + + UserSettings.prototype.skipForwardLength = function (val) { + + if (val != null) { + return this.set('skipForwardLength', val.toString()); + } + + return parseInt(this.get('skipForwardLength') || '30000'); + }; + + UserSettings.prototype.dashboardTheme = function (val) { + + if (val != null) { + return this.set('dashboardTheme', val); + } + + return this.get('dashboardTheme'); + }; + + UserSettings.prototype.skin = function (val) { + + if (val != null) { + return this.set('skin', val, false); + } + + return this.get('skin', false) || UserSettings.defaults.skin; + }; + + UserSettings.prototype.theme = function (val) { + + if (val != null) { + return this.set('appTheme', val, false); + } + + return this.get('appTheme', false) || UserSettings.defaults.theme; + }; + + UserSettings.prototype.enableSeasonalThemes = function (val) { + + if (val != null) { + return this.set('enableSeasonalThemes', val, false); + } + + return this.get('enableSeasonalThemes', false) !== 'false'; + }; + + UserSettings.prototype.screensaver = function (val) { + + if (val != null) { + return this.set('screensaver', val, false); + } + + return this.get('screensaver', false) || UserSettings.defaults.screensaver; + }; + + UserSettings.prototype.soundEffects = function (val) { + + if (val != null) { + return this.set('soundeffects', val, false); + } + + return this.get('soundeffects', false) || UserSettings.defaults.soundEffects; + }; + + // apps should set these values + UserSettings.defaults = { theme: null, - enableThemeVideos: !0 - }, UserSettings.prototype.loadQuerySettings = function(key, query) { + enableThemeVideos: true + }; + + UserSettings.prototype.loadQuerySettings = function (key, query) { + var values = this.get(key); - return values ? (values = JSON.parse(values), Object.assign(query, values)) : query - }, UserSettings.prototype.saveQuerySettings = function(key, query) { + + if (values) { + + values = JSON.parse(values); + + return Object.assign(query, values); + } + + return query; + }; + + UserSettings.prototype.saveQuerySettings = function (key, query) { + var values = {}; - return query.SortBy && (values.SortBy = query.SortBy), query.SortOrder && (values.SortOrder = query.SortOrder), this.set(key, JSON.stringify(values)) - }, UserSettings.prototype.getSubtitleAppearanceSettings = function(key) { - return key = key || "localplayersubtitleappearance3", JSON.parse(this.get(key, !1) || "{}") - }, UserSettings.prototype.setSubtitleAppearanceSettings = function(value, key) { - return key = key || "localplayersubtitleappearance3", this.set(key, JSON.stringify(value), !1) - }, UserSettings.prototype.setFilter = function(key, value) { - return this.set(key, value, !0) - }, UserSettings.prototype.getFilter = function(key) { - return this.get(key, !0) - }, UserSettings + + if (query.SortBy) { + values.SortBy = query.SortBy; + } + if (query.SortOrder) { + values.SortOrder = query.SortOrder; + } + + return this.set(key, JSON.stringify(values)); + }; + + UserSettings.prototype.getSubtitleAppearanceSettings = function (key) { + + key = key || 'localplayersubtitleappearance3'; + + return JSON.parse(this.get(key, false) || '{}'); + }; + + UserSettings.prototype.setSubtitleAppearanceSettings = function (value, key) { + + key = key || 'localplayersubtitleappearance3'; + + return this.set(key, JSON.stringify(value), false); + }; + + UserSettings.prototype.setFilter = function (key, value) { + + return this.set(key, value, true); + }; + + UserSettings.prototype.getFilter = function (key) { + + return this.get(key, true); + }; + + return UserSettings; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/viewmanager/viewcontainer-lite.css b/src/bower_components/emby-webcomponents/viewmanager/viewcontainer-lite.css index c8c4b28a10..edfa101976 100644 --- a/src/bower_components/emby-webcomponents/viewmanager/viewcontainer-lite.css +++ b/src/bower_components/emby-webcomponents/viewmanager/viewcontainer-lite.css @@ -4,141 +4,66 @@ left: 0; right: 0; bottom: 0; - contain: layout style size -} - -@-webkit-keyframes view-fadeout { - from { - opacity: 1 - } - - to { - opacity: 0 - } + contain: layout style size; + /* Can't use will-change because it causes the alpha picker to move when the page scrolls*/ + /*will-change: transform;*/ } @keyframes view-fadeout { from { - opacity: 1 + opacity: 1; } to { - opacity: 0 + opacity: 0; } } - -@-webkit-keyframes view-fadein { - from { - opacity: 0 - } - - to { - opacity: 1 - } -} - @keyframes view-fadein { from { - opacity: 0 + opacity: 0; } to { - opacity: 1 - } -} - -@-webkit-keyframes view-slideleft { - from { - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0) - } - - to { - -webkit-transform: none; - transform: none + opacity: 1; } } @keyframes view-slideleft { from { - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0) + transform: translate3d(100%, 0, 0); } to { - -webkit-transform: none; - transform: none - } -} - -@-webkit-keyframes view-slideleft-r { - from { - -webkit-transform: none; - transform: none - } - - to { - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0) + transform: none; } } @keyframes view-slideleft-r { from { - -webkit-transform: none; - transform: none + transform: none; } to { - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0) - } -} - -@-webkit-keyframes view-slideright { - from { - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0) - } - - to { - -webkit-transform: none; - transform: none + transform: translate3d(-100%, 0, 0); } } @keyframes view-slideright { from { - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0) + transform: translate3d(-100%, 0, 0); } to { - -webkit-transform: none; - transform: none - } -} - -@-webkit-keyframes view-slideright-r { - from { - -webkit-transform: none; - transform: none - } - - to { - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0) + transform: none; } } @keyframes view-slideright-r { from { - -webkit-transform: none; - transform: none + transform: none; } to { - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0) + transform: translate3d(100%, 0, 0); } } \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/viewmanager/viewcontainer-lite.js b/src/bower_components/emby-webcomponents/viewmanager/viewcontainer-lite.js index 0e3f492d15..57c8fe926c 100644 --- a/src/bower_components/emby-webcomponents/viewmanager/viewcontainer-lite.js +++ b/src/bower_components/emby-webcomponents/viewmanager/viewcontainer-lite.js @@ -1,148 +1,313 @@ -define(["browser", "dom", "layoutManager", "css!./viewcontainer-lite"], function(browser, dom, layoutManager) { - "use strict"; +define(['browser', 'dom', 'layoutManager', 'css!./viewcontainer-lite'], function (browser, dom, layoutManager) { + 'use strict'; + + var mainAnimatedPages = document.querySelector('.mainAnimatedPages'); + var allPages = []; + var currentUrls = []; + var pageContainerCount = 3; + var selectedPageIndex = -1; function enableAnimation() { - return !browser.tv && browser.supportsCssAnimation() + + // too slow + if (browser.tv) { + return false; + } + + return browser.supportsCssAnimation(); } function findLastView(parent, className) { - for (var nodes = parent.childNodes, i = nodes.length - 1; i >= 0; i--) { - var node = nodes[i], - classList = node.classList; - if (classList && classList.contains(className)) return node + + var nodes = parent.childNodes; + for (var i = nodes.length - 1; i >= 0; i--) { + var node = nodes[i]; + var classList = node.classList; + if (classList && classList.contains(className)) { + return node; + } } } function findViewBefore(elem, className) { - for (var node = elem.previousSibling; node;) { + + var node = elem.previousSibling; + while (node) { var classList = node.classList; - if (classList && classList.contains(className)) return node; - node = node.previousSibling + if (classList && classList.contains(className)) { + return node; + } + + node = node.previousSibling; } } function loadView(options) { - if (!options.cancel) { - cancelActiveAnimations(); - var selected = selectedPageIndex, - previousAnimatable = -1 === selected ? null : allPages[selected], - pageIndex = selected + 1; - pageIndex >= pageContainerCount && (pageIndex = 0); - var viewHtml = options.view, - properties = []; - options.fullscreen && properties.push("fullscreen"); - var view, currentPage = allPages[pageIndex]; - return currentPage ? (triggerDestroy(currentPage), currentPage.insertAdjacentHTML("beforebegin", viewHtml), view = findViewBefore(currentPage, "view"), mainAnimatedPages.removeChild(currentPage)) : (mainAnimatedPages.insertAdjacentHTML("beforeend", viewHtml), view = findLastView(mainAnimatedPages, "view")), view.classList.add("mainAnimatedPage"), properties.length && view.setAttribute("data-properties", properties.join(",")), options.type && view.setAttribute("data-type", options.type), allPages[pageIndex] = view, onBeforeChange && onBeforeChange(view, !1, options), beforeAnimate(allPages, pageIndex, selected), animate(view, previousAnimatable, options.transition, options.isBack).then(function() { - return selectedPageIndex = pageIndex, currentUrls[pageIndex] = options.url, !options.cancel && previousAnimatable && afterAnimate(allPages, pageIndex), view - }) + + if (options.cancel) { + return; } + + cancelActiveAnimations(); + + var selected = selectedPageIndex; + var previousAnimatable = selected === -1 ? null : allPages[selected]; + var pageIndex = selected + 1; + + if (pageIndex >= pageContainerCount) { + pageIndex = 0; + } + + var viewHtml = options.view; + + var properties = []; + if (options.fullscreen) { + properties.push('fullscreen'); + } + + var currentPage = allPages[pageIndex]; + + var view; + + if (currentPage) { + triggerDestroy(currentPage); + currentPage.insertAdjacentHTML('beforebegin', viewHtml); + view = findViewBefore(currentPage, 'view'); + + mainAnimatedPages.removeChild(currentPage); + + } else { + mainAnimatedPages.insertAdjacentHTML('beforeend', viewHtml); + + view = findLastView(mainAnimatedPages, 'view'); + } + + view.classList.add('mainAnimatedPage'); + + if (properties.length) { + view.setAttribute('data-properties', properties.join(',')); + } + + if (options.type) { + view.setAttribute('data-type', options.type); + } + + allPages[pageIndex] = view; + + if (onBeforeChange) { + onBeforeChange(view, false, options); + } + + beforeAnimate(allPages, pageIndex, selected); + + // animate here + return animate(view, previousAnimatable, options.transition, options.isBack).then(function () { + + selectedPageIndex = pageIndex; + currentUrls[pageIndex] = options.url; + if (!options.cancel && previousAnimatable) { + afterAnimate(allPages, pageIndex); + } + + return view; + }); } function beforeAnimate(allPages, newPageIndex, oldPageIndex) { - for (var i = 0, length = allPages.length; i < length; i++) newPageIndex === i || oldPageIndex === i || allPages[i].classList.add("hide") + for (var i = 0, length = allPages.length; i < length; i++) { + if (newPageIndex === i || oldPageIndex === i) { + //allPages[i].classList.remove('hide'); + } else { + allPages[i].classList.add('hide'); + } + } } function afterAnimate(allPages, newPageIndex) { - for (var i = 0, length = allPages.length; i < length; i++) newPageIndex === i || allPages[i].classList.add("hide") + for (var i = 0, length = allPages.length; i < length; i++) { + if (newPageIndex === i) { + //allPages[i].classList.remove('hide'); + } else { + allPages[i].classList.add('hide'); + } + } } function animate(newAnimatedPage, oldAnimatedPage, transition, isBack) { + if (enableAnimation() && oldAnimatedPage) { - if ("slide" === transition) return slide(newAnimatedPage, oldAnimatedPage, transition, isBack); - if ("fade" === transition) return fade(newAnimatedPage, oldAnimatedPage, transition, isBack); - clearAnimation(newAnimatedPage), oldAnimatedPage && clearAnimation(oldAnimatedPage) + if (transition === 'slide') { + return slide(newAnimatedPage, oldAnimatedPage, transition, isBack); + } else if (transition === 'fade') { + return fade(newAnimatedPage, oldAnimatedPage, transition, isBack); + } else { + clearAnimation(newAnimatedPage); + if (oldAnimatedPage) { + clearAnimation(oldAnimatedPage); + } + } } - return Promise.resolve() + + return Promise.resolve(); } function clearAnimation(elem) { - setAnimation(elem, "none") + setAnimation(elem, 'none'); } function slide(newAnimatedPage, oldAnimatedPage, transition, isBack) { - return new Promise(function(resolve, reject) { - var duration = layoutManager.tv ? 450 : 160, - animations = []; - oldAnimatedPage && (isBack ? setAnimation(oldAnimatedPage, "view-slideright-r " + duration + "ms ease-out normal both") : setAnimation(oldAnimatedPage, "view-slideleft-r " + duration + "ms ease-out normal both"), animations.push(oldAnimatedPage)), isBack ? setAnimation(newAnimatedPage, "view-slideright " + duration + "ms ease-out normal both") : setAnimation(newAnimatedPage, "view-slideleft " + duration + "ms ease-out normal both"), animations.push(newAnimatedPage), currentAnimations = animations; - var onAnimationComplete = function() { + + return new Promise(function (resolve, reject) { + + var duration = layoutManager.tv ? 450 : 160; + + var animations = []; + + if (oldAnimatedPage) { + if (isBack) { + setAnimation(oldAnimatedPage, 'view-slideright-r ' + duration + 'ms ease-out normal both'); + } else { + setAnimation(oldAnimatedPage, 'view-slideleft-r ' + duration + 'ms ease-out normal both'); + } + animations.push(oldAnimatedPage); + } + + if (isBack) { + setAnimation(newAnimatedPage, 'view-slideright ' + duration + 'ms ease-out normal both'); + } else { + setAnimation(newAnimatedPage, 'view-slideleft ' + duration + 'ms ease-out normal both'); + } + animations.push(newAnimatedPage); + + currentAnimations = animations; + + var onAnimationComplete = function () { dom.removeEventListener(newAnimatedPage, dom.whichAnimationEvent(), onAnimationComplete, { - once: !0 - }), resolve() + once: true + }); + resolve(); }; + dom.addEventListener(newAnimatedPage, dom.whichAnimationEvent(), onAnimationComplete, { - once: !0 - }) - }) + once: true + }); + }); } function fade(newAnimatedPage, oldAnimatedPage, transition, isBack) { - return new Promise(function(resolve, reject) { - var duration = layoutManager.tv ? 450 : 270, - animations = []; - newAnimatedPage.style.opacity = 0, setAnimation(newAnimatedPage, "view-fadein " + duration + "ms ease-in normal both"), animations.push(newAnimatedPage), oldAnimatedPage && (setAnimation(oldAnimatedPage, "view-fadeout " + duration + "ms ease-out normal both"), animations.push(oldAnimatedPage)), currentAnimations = animations; - var onAnimationComplete = function() { + + return new Promise(function (resolve, reject) { + + var duration = layoutManager.tv ? 450 : 270; + var animations = []; + + newAnimatedPage.style.opacity = 0; + setAnimation(newAnimatedPage, 'view-fadein ' + duration + 'ms ease-in normal both'); + animations.push(newAnimatedPage); + + if (oldAnimatedPage) { + setAnimation(oldAnimatedPage, 'view-fadeout ' + duration + 'ms ease-out normal both'); + animations.push(oldAnimatedPage); + } + + currentAnimations = animations; + + var onAnimationComplete = function () { dom.removeEventListener(newAnimatedPage, dom.whichAnimationEvent(), onAnimationComplete, { - once: !0 - }), resolve() + once: true + }); + resolve(); }; + dom.addEventListener(newAnimatedPage, dom.whichAnimationEvent(), onAnimationComplete, { - once: !0 - }) - }) + once: true + }); + }); } function setAnimation(elem, value) { - requestAnimationFrame(function() { - elem.style.animation = value - }) + + requestAnimationFrame(function () { + elem.style.animation = value; + }); } + var currentAnimations = []; function cancelActiveAnimations() { - for (var animations = currentAnimations, i = 0, length = animations.length; i < length; i++) animations[i].style.animation = "none" + + var animations = currentAnimations; + for (var i = 0, length = animations.length; i < length; i++) { + animations[i].style.animation = 'none'; + } } + var onBeforeChange; function setOnBeforeChange(fn) { - onBeforeChange = fn + onBeforeChange = fn; } function tryRestoreView(options) { - var url = options.url, - index = currentUrls.indexOf(url); - if (-1 !== index) { - var animatable = allPages[index], - view = animatable; + + var url = options.url; + var index = currentUrls.indexOf(url); + + if (index !== -1) { + + var animatable = allPages[index]; + var view = animatable; + if (view) { - if (options.cancel) return; + + if (options.cancel) { + return; + } + cancelActiveAnimations(); - var selected = selectedPageIndex, - previousAnimatable = -1 === selected ? null : allPages[selected]; - return onBeforeChange && onBeforeChange(view, !0, options), beforeAnimate(allPages, index, selected), animatable.classList.remove("hide"), animate(animatable, previousAnimatable, options.transition, options.isBack).then(function() { - return selectedPageIndex = index, !options.cancel && previousAnimatable && afterAnimate(allPages, index), view - }) + + var selected = selectedPageIndex; + var previousAnimatable = selected === -1 ? null : allPages[selected]; + + if (onBeforeChange) { + onBeforeChange(view, true, options); + } + + beforeAnimate(allPages, index, selected); + + animatable.classList.remove('hide'); + + return animate(animatable, previousAnimatable, options.transition, options.isBack).then(function () { + + selectedPageIndex = index; + if (!options.cancel && previousAnimatable) { + afterAnimate(allPages, index); + } + return view; + }); } } - return Promise.reject() + + return Promise.reject(); } function triggerDestroy(view) { - view.dispatchEvent(new CustomEvent("viewdestroy", { - cancelable: !1 - })) + + view.dispatchEvent(new CustomEvent('viewdestroy', { + cancelable: false + })); } function reset() { - allPages = [], currentUrls = [], mainAnimatedPages.innerHTML = "", selectedPageIndex = -1 + + allPages = []; + currentUrls = []; + mainAnimatedPages.innerHTML = ''; + selectedPageIndex = -1; } - var onBeforeChange, mainAnimatedPages = document.querySelector(".mainAnimatedPages"), - allPages = [], - currentUrls = [], - pageContainerCount = 3, - selectedPageIndex = -1, - currentAnimations = []; + return { loadView: loadView, tryRestoreView: tryRestoreView, reset: reset, setOnBeforeChange: setOnBeforeChange - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/viewmanager/viewmanager.js b/src/bower_components/emby-webcomponents/viewmanager/viewmanager.js index 3ab5afa710..606c7e98c1 100644 --- a/src/bower_components/emby-webcomponents/viewmanager/viewmanager.js +++ b/src/bower_components/emby-webcomponents/viewmanager/viewmanager.js @@ -1,82 +1,183 @@ -define(["viewcontainer", "focusManager", "queryString", "layoutManager"], function(viewcontainer, focusManager, queryString, layoutManager) { - "use strict"; +define(['viewcontainer', 'focusManager', 'queryString', 'layoutManager'], function (viewcontainer, focusManager, queryString, layoutManager) { + 'use strict'; + + var currentView; + var dispatchPageEvents; + + viewcontainer.setOnBeforeChange(function (newView, isRestored, options) { + + var lastView = currentView; + if (lastView) { + + var beforeHideResult = dispatchViewEvent(lastView, null, 'viewbeforehide', true); + + if (!beforeHideResult) { + // todo: cancel + } + } + + var eventDetail = getViewEventDetail(newView, options, isRestored); + + if (!newView.initComplete) { + newView.initComplete = true; + + if (options.controllerFactory) { + + // Use controller method + var controller = new options.controllerFactory(newView, eventDetail.detail.params); + } + + if (!options.controllerFactory || dispatchPageEvents) { + dispatchViewEvent(newView, eventDetail, 'viewinit'); + } + } + + dispatchViewEvent(newView, eventDetail, 'viewbeforeshow'); + }); function onViewChange(view, options, isRestore) { + var lastView = currentView; - lastView && dispatchViewEvent(lastView, null, "viewhide"), currentView = view; + if (lastView) { + dispatchViewEvent(lastView, null, 'viewhide'); + } + + currentView = view; + var eventDetail = getViewEventDetail(view, options, isRestore); - isRestore ? layoutManager.mobile || (view.activeElement && document.body.contains(view.activeElement) && focusManager.isCurrentlyFocusable(view.activeElement) ? focusManager.focus(view.activeElement) : focusManager.autoFocus(view)) : !1 !== options.autoFocus && focusManager.autoFocus(view), view.dispatchEvent(new CustomEvent("viewshow", eventDetail)), dispatchPageEvents && view.dispatchEvent(new CustomEvent("pageshow", eventDetail)) + + if (!isRestore) { + if (options.autoFocus !== false) { + focusManager.autoFocus(view); + } + } + else if (!layoutManager.mobile) { + if (view.activeElement && document.body.contains(view.activeElement) && focusManager.isCurrentlyFocusable(view.activeElement)) { + focusManager.focus(view.activeElement); + } else { + focusManager.autoFocus(view); + } + } + + view.dispatchEvent(new CustomEvent('viewshow', eventDetail)); + + if (dispatchPageEvents) { + view.dispatchEvent(new CustomEvent('pageshow', eventDetail)); + } } function getProperties(view) { - var props = view.getAttribute("data-properties"); - return props ? props.split(",") : [] + var props = view.getAttribute('data-properties'); + + if (props) { + return props.split(','); + } + + return []; } function dispatchViewEvent(view, eventInfo, eventName, isCancellable) { - eventInfo || (eventInfo = { - detail: { - type: view.getAttribute("data-type"), - properties: getProperties(view) - }, - bubbles: !0, - cancelable: isCancellable - }), eventInfo.cancelable = isCancellable || !1; + + if (!eventInfo) { + eventInfo = { + detail: { + type: view.getAttribute('data-type'), + properties: getProperties(view) + }, + bubbles: true, + cancelable: isCancellable + }; + } + + eventInfo.cancelable = isCancellable || false; + var eventResult = view.dispatchEvent(new CustomEvent(eventName, eventInfo)); - return dispatchPageEvents && (eventInfo.cancelable = !1, view.dispatchEvent(new CustomEvent(eventName.replace("view", "page"), eventInfo))), eventResult + + if (dispatchPageEvents) { + eventInfo.cancelable = false; + view.dispatchEvent(new CustomEvent(eventName.replace('view', 'page'), eventInfo)); + } + + return eventResult; } function getViewEventDetail(view, options, isRestore) { - var url = options.url, - index = url.indexOf("?"), - params = -1 === index ? {} : queryString.parse(url.substring(index + 1)); + + var url = options.url; + var index = url.indexOf('?'); + var params = index === -1 ? {} : queryString.parse(url.substring(index + 1)); + return { detail: { - type: view.getAttribute("data-type"), + type: view.getAttribute('data-type'), properties: getProperties(view), params: params, isRestored: isRestore, state: options.state, + + // The route options options: options.options || {} }, - bubbles: !0, - cancelable: !1 - } + bubbles: true, + cancelable: false + }; } function resetCachedViews() { - viewcontainer.reset() + // Reset all cached views whenever the skin changes + viewcontainer.reset(); } - function ViewManager() {} - var currentView, dispatchPageEvents; - return viewcontainer.setOnBeforeChange(function(newView, isRestored, options) { + document.addEventListener('skinunload', resetCachedViews); + + function ViewManager() { + } + + ViewManager.prototype.loadView = function (options) { + var lastView = currentView; + + // Record the element that has focus if (lastView) { - dispatchViewEvent(lastView, null, "viewbeforehide", !0) + lastView.activeElement = document.activeElement; } - var eventDetail = getViewEventDetail(newView, options, isRestored); - if (!newView.initComplete) { - if (newView.initComplete = !0, options.controllerFactory) { - new options.controllerFactory(newView, eventDetail.detail.params) - } - options.controllerFactory && !dispatchPageEvents || dispatchViewEvent(newView, eventDetail, "viewinit") + + if (options.cancel) { + return; } - dispatchViewEvent(newView, eventDetail, "viewbeforeshow") - }), document.addEventListener("skinunload", resetCachedViews), ViewManager.prototype.loadView = function(options) { - var lastView = currentView; - lastView && (lastView.activeElement = document.activeElement), options.cancel || viewcontainer.loadView(options).then(function(view) { - onViewChange(view, options) - }) - }, ViewManager.prototype.tryRestoreView = function(options, onViewChanging) { - return options.cancel ? Promise.reject({ - cancelled: !0 - }) : (currentView && (currentView.activeElement = document.activeElement), viewcontainer.tryRestoreView(options).then(function(view) { - onViewChanging(), onViewChange(view, options, !0) - })) - }, ViewManager.prototype.currentView = function() { - return currentView - }, ViewManager.prototype.dispatchPageEvents = function(value) { - dispatchPageEvents = value - }, new ViewManager -}); \ No newline at end of file + + viewcontainer.loadView(options).then(function (view) { + + onViewChange(view, options); + }); + }; + + ViewManager.prototype.tryRestoreView = function (options, onViewChanging) { + + if (options.cancel) { + return Promise.reject({ cancelled: true }); + } + + // Record the element that has focus + if (currentView) { + currentView.activeElement = document.activeElement; + } + + return viewcontainer.tryRestoreView(options).then(function (view) { + + onViewChanging(); + onViewChange(view, options, true); + + }); + }; + + ViewManager.prototype.currentView = function () { + return currentView; + }; + + ViewManager.prototype.dispatchPageEvents = function (value) { + dispatchPageEvents = value; + }; + + return new ViewManager(); +}); diff --git a/src/bower_components/emby-webcomponents/viewsettings/viewsettings.js b/src/bower_components/emby-webcomponents/viewsettings/viewsettings.js index 9fd54e6db5..7600932036 100644 --- a/src/bower_components/emby-webcomponents/viewsettings/viewsettings.js +++ b/src/bower_components/emby-webcomponents/viewsettings/viewsettings.js @@ -1,60 +1,146 @@ -define(["require", "dialogHelper", "loading", "apphost", "layoutManager", "connectionManager", "appRouter", "globalize", "userSettings", "emby-checkbox", "emby-input", "paper-icon-button-light", "emby-select", "material-icons", "css!./../formdialog", "emby-button", "emby-linkbutton", "flexStyles"], function(require, dialogHelper, loading, appHost, layoutManager, connectionManager, appRouter, globalize, userSettings) { - "use strict"; +define(['require', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectionManager', 'appRouter', 'globalize', 'userSettings', 'emby-checkbox', 'emby-input', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button', 'emby-linkbutton', 'flexStyles'], function (require, dialogHelper, loading, appHost, layoutManager, connectionManager, appRouter, globalize, userSettings) { + 'use strict'; function onSubmit(e) { - return e.preventDefault(), !1 + + e.preventDefault(); + return false; } function initEditor(context, settings) { - context.querySelector("form").addEventListener("submit", onSubmit); - for (var elems = context.querySelectorAll(".viewSetting-checkboxContainer"), i = 0, length = elems.length; i < length; i++) elems[i].querySelector("input").checked = settings[elems[i].getAttribute("data-settingname")] || !1; - context.querySelector(".selectImageType").value = settings.imageType || "primary" + + context.querySelector('form').addEventListener('submit', onSubmit); + + var elems = context.querySelectorAll('.viewSetting-checkboxContainer'); + + for (var i = 0, length = elems.length; i < length; i++) { + + elems[i].querySelector('input').checked = settings[elems[i].getAttribute('data-settingname')] || false; + } + + context.querySelector('.selectImageType').value = settings.imageType || 'primary'; } function saveValues(context, settings, settingsKey) { - for (var elems = context.querySelectorAll(".viewSetting-checkboxContainer"), i = 0, length = elems.length; i < length; i++) userSettings.set(settingsKey + "-" + elems[i].getAttribute("data-settingname"), elems[i].querySelector("input").checked); - userSettings.set(settingsKey + "-imageType", context.querySelector(".selectImageType").value) - } + + var elems = context.querySelectorAll('.viewSetting-checkboxContainer'); + for (var i = 0, length = elems.length; i < length; i++) { + userSettings.set(settingsKey + '-' + elems[i].getAttribute('data-settingname'), elems[i].querySelector('input').checked); + } + + userSettings.set(settingsKey + '-imageType', context.querySelector('.selectImageType').value); + } function centerFocus(elem, horiz, on) { - require(["scrollHelper"], function(scrollHelper) { - var fn = on ? "on" : "off"; - scrollHelper.centerFocus[fn](elem, horiz) - }) + require(['scrollHelper'], function (scrollHelper) { + var fn = on ? 'on' : 'off'; + scrollHelper.centerFocus[fn](elem, horiz); + }); } function showIfAllowed(context, selector, visible) { + var elem = context.querySelector(selector); - visible && !elem.classList.contains("hiddenFromViewSettings") ? elem.classList.remove("hide") : elem.classList.add("hide") + + if (visible && !elem.classList.contains('hiddenFromViewSettings')) { + elem.classList.remove('hide'); + } else { + elem.classList.add('hide'); + } } - function ViewSettings() {} - return ViewSettings.prototype.show = function(options) { - return new Promise(function(resolve, reject) { - require(["text!./viewsettings.template.html"], function(template) { + function ViewSettings() { + + } + + ViewSettings.prototype.show = function (options) { + + return new Promise(function (resolve, reject) { + + require(['text!./viewsettings.template.html'], function (template) { + var dialogOptions = { - removeOnClose: !0, - scrollY: !1 + removeOnClose: true, + scrollY: false }; - layoutManager.tv ? dialogOptions.size = "fullscreen" : dialogOptions.size = "small"; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'small'; + } + var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"); - var html = ""; - html += '
    ', html += '', html += '

    ${Settings}

    ', html += "
    ", html += template, dlg.innerHTML = globalize.translateDocument(html, "sharedcomponents"); - for (var settingElements = dlg.querySelectorAll(".viewSetting"), i = 0, length = settingElements.length; i < length; i++) - 1 === options.visibleSettings.indexOf(settingElements[i].getAttribute("data-settingname")) ? (settingElements[i].classList.add("hide"), settingElements[i].classList.add("hiddenFromViewSettings")) : (settingElements[i].classList.remove("hide"), settingElements[i].classList.remove("hiddenFromViewSettings")); - initEditor(dlg, options.settings), dlg.querySelector(".selectImageType").addEventListener("change", function() { - showIfAllowed(dlg, ".chkTitleContainer", "list" !== this.value), showIfAllowed(dlg, ".chkYearContainer", "list" !== this.value) - }), dlg.querySelector(".btnCancel").addEventListener("click", function() { - dialogHelper.close(dlg) - }), layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !0); + + dlg.classList.add('formDialog'); + + var html = ''; + + html += '
    '; + html += ''; + html += '

    ${Settings}

    '; + + html += '
    '; + + html += template; + + dlg.innerHTML = globalize.translateDocument(html, 'sharedcomponents'); + + var settingElements = dlg.querySelectorAll('.viewSetting'); + for (var i = 0, length = settingElements.length; i < length; i++) { + if (options.visibleSettings.indexOf(settingElements[i].getAttribute('data-settingname')) === -1) { + settingElements[i].classList.add('hide'); + settingElements[i].classList.add('hiddenFromViewSettings'); + } else { + settingElements[i].classList.remove('hide'); + settingElements[i].classList.remove('hiddenFromViewSettings'); + } + } + + initEditor(dlg, options.settings); + + dlg.querySelector('.selectImageType').addEventListener('change', function () { + + showIfAllowed(dlg, '.chkTitleContainer', this.value !== 'list'); + showIfAllowed(dlg, '.chkYearContainer', this.value !== 'list'); + }); + + dlg.querySelector('.btnCancel').addEventListener('click', function () { + + dialogHelper.close(dlg); + }); + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, true); + } + var submitted; - dlg.querySelector(".selectImageType").dispatchEvent(new CustomEvent("change", {})), dlg.querySelector("form").addEventListener("change", function() { - submitted = !0 - }, !0), dialogHelper.open(dlg).then(function() { - if (layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !1), submitted) return saveValues(dlg, options.settings, options.settingsKey), void resolve(); - reject() - }) - }) - }) - }, ViewSettings + + dlg.querySelector('.selectImageType').dispatchEvent(new CustomEvent('change', {})); + + dlg.querySelector('form').addEventListener('change', function () { + + submitted = true; + + }, true); + + dialogHelper.open(dlg).then(function () { + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, false); + } + + if (submitted) { + saveValues(dlg, options.settings, options.settingsKey); + resolve(); + return; + } + + reject(); + }); + }); + }); + }; + + return ViewSettings; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/visibleinviewport.js b/src/bower_components/emby-webcomponents/visibleinviewport.js index 60f5fd9dee..5f6064ff96 100644 --- a/src/bower_components/emby-webcomponents/visibleinviewport.js +++ b/src/bower_components/emby-webcomponents/visibleinviewport.js @@ -1,19 +1,41 @@ -define(["dom"], function(dom) { - "use strict"; +define(['dom'], function (dom) { + 'use strict'; + /** + * Copyright 2012, Digital Fusion + * Licensed under the MIT license. + * http://teamdf.com/jquery-plugins/license/ + * + * @author Sam Sehnert + * @desc A small plugin that checks whether elements are within + * the user visible viewport of a web browser. + * only accounts for vertical position, not horizontal. + */ function visibleInViewport(elem, partial, thresholdX, thresholdY) { - if (thresholdX = thresholdX || 0, thresholdY = thresholdY || 0, !elem.getBoundingClientRect) return !0; - var windowSize = dom.getWindowSize(), - vpWidth = windowSize.innerWidth, - vpHeight = windowSize.innerHeight, - rec = elem.getBoundingClientRect(), + + thresholdX = thresholdX || 0; + thresholdY = thresholdY || 0; + + if (!elem.getBoundingClientRect) { + return true; + } + + var windowSize = dom.getWindowSize(); + + var vpWidth = windowSize.innerWidth, + vpHeight = windowSize.innerHeight; + + // Use this native browser method, if available. + var rec = elem.getBoundingClientRect(), tViz = rec.top >= 0 && rec.top < vpHeight + thresholdY, bViz = rec.bottom > 0 && rec.bottom <= vpHeight + thresholdY, lViz = rec.left >= 0 && rec.left < vpWidth + thresholdX, rViz = rec.right > 0 && rec.right <= vpWidth + thresholdX, vVisible = partial ? tViz || bViz : tViz && bViz, hVisible = partial ? lViz || rViz : lViz && rViz; - return vVisible && hVisible + + return vVisible && hVisible; } - return visibleInViewport + + return visibleInViewport; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/youtubeplayer/plugin.js b/src/bower_components/emby-webcomponents/youtubeplayer/plugin.js index 243050eccb..1ed95e2110 100644 --- a/src/bower_components/emby-webcomponents/youtubeplayer/plugin.js +++ b/src/bower_components/emby-webcomponents/youtubeplayer/plugin.js @@ -1,181 +1,409 @@ -define(["require", "events", "browser", "appRouter", "loading"], function(require, events, browser, appRouter, loading) { +define(['require', 'events', 'browser', 'appRouter', 'loading'], function (require, events, browser, appRouter, loading) { "use strict"; function zoomIn(elem, iterations) { - var keyframes = [{ - transform: "scale3d(.2, .2, .2) ", - opacity: ".6", - offset: 0 - }, { - transform: "none", - opacity: "1", - offset: 1 - }], - timing = { - duration: 240, - iterations: iterations - }; - return elem.animate(keyframes, timing) + var keyframes = [ + { transform: 'scale3d(.2, .2, .2) ', opacity: '.6', offset: 0 }, + { transform: 'none', opacity: '1', offset: 1 } + ]; + + var timing = { duration: 240, iterations: iterations }; + return elem.animate(keyframes, timing); } function createMediaElement(instance, options) { - return new Promise(function(resolve, reject) { - var dlg = document.querySelector(".youtubePlayerContainer"); - dlg ? resolve(dlg.querySelector("#player")) : require(["css!./style"], function() { - loading.show(); - var dlg = document.createElement("div"); - dlg.classList.add("youtubePlayerContainer"), options.fullscreen && dlg.classList.add("onTop"), dlg.innerHTML = '
    '; - var videoElement = dlg.querySelector("#player"); - document.body.insertBefore(dlg, document.body.firstChild), instance.videoDialog = dlg, options.fullscreen && dlg.animate && !browser.slow ? zoomIn(dlg, 1).onfinish = function() { - resolve(videoElement) - } : resolve(videoElement) - }) - }) + + return new Promise(function (resolve, reject) { + + var dlg = document.querySelector('.youtubePlayerContainer'); + + if (!dlg) { + + require(['css!./style'], function () { + + loading.show(); + + var dlg = document.createElement('div'); + + dlg.classList.add('youtubePlayerContainer'); + + if (options.fullscreen) { + dlg.classList.add('onTop'); + } + + dlg.innerHTML = '
    '; + var videoElement = dlg.querySelector('#player'); + + document.body.insertBefore(dlg, document.body.firstChild); + instance.videoDialog = dlg; + + if (options.fullscreen && dlg.animate && !browser.slow) { + zoomIn(dlg, 1).onfinish = function () { + resolve(videoElement); + }; + } else { + resolve(videoElement); + } + + }); + + } else { + resolve(dlg.querySelector('#player')); + } + }); } function onVideoResize() { - var instance = this, - player = instance.currentYoutubePlayer, - dlg = instance.videoDialog; - player && dlg && player.setSize(dlg.offsetWidth, dlg.offsetHeight) + var instance = this; + var player = instance.currentYoutubePlayer; + var dlg = instance.videoDialog; + if (player && dlg) { + player.setSize(dlg.offsetWidth, dlg.offsetHeight); + } } function clearTimeUpdateInterval(instance) { - instance.timeUpdateInterval && clearInterval(instance.timeUpdateInterval), instance.timeUpdateInterval = null + if (instance.timeUpdateInterval) { + clearInterval(instance.timeUpdateInterval); + } + instance.timeUpdateInterval = null; } function onEndedInternal(instance) { + clearTimeUpdateInterval(instance); var resizeListener = instance.resizeListener; - resizeListener && (window.removeEventListener("resize", resizeListener), window.removeEventListener("orientationChange", resizeListener), instance.resizeListener = null); + if (resizeListener) { + window.removeEventListener('resize', resizeListener); + window.removeEventListener('orientationChange', resizeListener); + instance.resizeListener = null; + } + var stopInfo = { src: instance._currentSrc }; - events.trigger(instance, "stopped", [stopInfo]), instance._currentSrc = null, instance.currentYoutubePlayer && instance.currentYoutubePlayer.destroy(), instance.currentYoutubePlayer = null + + events.trigger(instance, 'stopped', [stopInfo]); + + instance._currentSrc = null; + if (instance.currentYoutubePlayer) { + instance.currentYoutubePlayer.destroy(); + } + instance.currentYoutubePlayer = null; } + // 4. The API will call this function when the video player is ready. function onPlayerReady(event) { - event.target.playVideo() + event.target.playVideo(); } function onTimeUpdate(e) { - events.trigger(this, "timeupdate") + + events.trigger(this, 'timeupdate'); } function onPlaying(instance, playOptions, resolve) { - instance.started || (instance.started = !0, resolve(), clearTimeUpdateInterval(instance), instance.timeUpdateInterval = setInterval(onTimeUpdate.bind(instance), 500), playOptions.fullscreen ? appRouter.showVideoOsd().then(function() { - instance.videoDialog.classList.remove("onTop") - }) : (appRouter.setTransparency("backdrop"), instance.videoDialog.classList.remove("onTop")), require(["loading"], function(loading) { - loading.hide() - })) + + if (!instance.started) { + + instance.started = true; + resolve(); + clearTimeUpdateInterval(instance); + instance.timeUpdateInterval = setInterval(onTimeUpdate.bind(instance), 500); + + if (playOptions.fullscreen) { + + appRouter.showVideoOsd().then(function () { + instance.videoDialog.classList.remove('onTop'); + }); + + } else { + appRouter.setTransparency('backdrop'); + instance.videoDialog.classList.remove('onTop'); + } + + require(['loading'], function (loading) { + + loading.hide(); + }); + } } function setCurrentSrc(instance, elem, options) { - return new Promise(function(resolve, reject) { - require(["queryString"], function(queryString) { + + return new Promise(function (resolve, reject) { + + require(['queryString'], function (queryString) { + + instance._currentSrc = options.url; - var params = queryString.parse(options.url.split("?")[1]); - if (window.onYouTubeIframeAPIReady = function() { - instance.currentYoutubePlayer = new YT.Player("player", { - height: instance.videoDialog.offsetHeight, - width: instance.videoDialog.offsetWidth, - videoId: params.v, - events: { - onReady: onPlayerReady, - onStateChange: function(event) { - event.data === YT.PlayerState.PLAYING ? onPlaying(instance, options, resolve) : event.data === YT.PlayerState.ENDED ? onEndedInternal(instance) : event.data === YT.PlayerState.PAUSED && events.trigger(instance, "pause") + var params = queryString.parse(options.url.split('?')[1]); + // 3. This function creates an