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..439c8227a3 100644 --- a/src/bower_components/emby-webcomponents/cardbuilder/cardbuilder.js +++ b/src/bower_components/emby-webcomponents/cardbuilder/cardbuilder.js @@ -1,515 +1,1818 @@ -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 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: item.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(name); + } + + 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, item.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: item.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..2ee47924dd 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 = "2D4B1DA3"; + + // 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 + + 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; }); \ No newline at end of file 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..65c7b2d934 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') { + return loadLibraryTiles(elem, apiClient, user, userSettings, 'smallBackdrop', userViews, allSections); + } + else if (section === 'librarybuttons') { + return 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,1031 @@ 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"); + + return Promise.all([getAppInfo(apiClient), getDownloadsSectionHtml(apiClient, user, userSettings)]).then(function (responses) { + + var infoHtml = responses[0]; + var 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) - }) + + elem.innerHTML = html + downloadsHtml + infoHtml; + + bindHomeScreenSettingsIcon(elem, apiClient, user.Id, userSettings); + + if (infoHtml) { + bindAppInfoEvents(elem); + } + imageLoader.lazyChildren(elem); + }); } function bindAppInfoEvents(elem) { - elem.querySelector(".appInfoSection").addEventListener("click", function(e) { - dom.parentWithClass(e.target, "card") && registrationServices.showPremiereInfo() - }) + + elem.querySelector('.appInfoSection').addEventListener('click', function (e) { + + if (dom.parentWithClass(e.target, 'card')) { + registrationServices.showPremiereInfo(); + } + }); } + /** + * 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 + 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 frequency = 172800000; + + var cacheKey = 'lastappinfopresent5'; + var lastDatePresented = parseInt(appSettings.get(cacheKey) || '0'); + + // Don't show the first time, right after installation + if (!lastDatePresented) { + appSettings.set(cacheKey, new Date().getTime()); + return Promise.resolve(''); + } + + if ((new Date().getTime() - lastDatePresented) < frequency) { + return Promise.resolve(''); + } + + return registrationServices.validateFeature('dvr', { + + showDialog: false, + viewOnly: true + + }).then(function () { + + appSettings.set(cacheKey, new Date().getTime()); + return ''; + + }, 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("")) + + if (appHost.supports('otherapppromotions')) { + infos.push(getTheaterInfo); + } + + return infos[getRandomInt(0, infos.length - 1)](); + }); } function getCard(img, shape) { - shape = shape || "backdropCard"; - var html = '
'; - return html += '
', html += '
', html += "
", html += "
" + + shape = shape || 'backdropCard'; + var html = '
'; + + html += '
'; + + html += '
'; + + html += '
'; + + html += '
'; + + return 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 += "
" + + var html = ''; + html += '
'; + html += '
'; + html += '

Discover Jellyfin Theater

'; + html += ''; + html += '
'; + + var nameText = 'Jellyfin Theater'; + html += '
'; + html += '

A beautiful app for your TV and large screen tablet. ' + nameText + ' 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 += '
'; + return 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 += "
" + + var html = ''; + html += '
'; + html += '
'; + html += '

Discover Emby Premiere

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

Enjoy Emby DVR, get free access to Emby 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 html; } 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({ + + if (!appHost.supports('sync') || !user.Policy.EnableContentDownloading) { + return Promise.resolve(''); + } + + var promise = 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({ + Filters: 'IsNotFolder' + + }) : Promise.resolve([]); + + return promise.then(function (items) { + + var html = ''; + + html += '
'; + + html += '
'; + + if (!layoutManager.tv) { + + html += ''; + html += '

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

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

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

'; + } + html += '
'; + + html += '
'; + + var cardLayout = false; + + 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, + preferThumb: 'auto', + shape: 'autooverflow', + overlayText: false, + showTitle: true, + showParentTitle: true, + lazy: true, + showDetailsMenu: true, + overlayPlayButton: true, + context: 'home', + centerText: !cardLayout, + allowBottomPadding: false, + cardLayout: cardLayout, + showYear: true, lines: 2 - }), html += "
", html += "
" - }) : Promise.resolve("") + }); + + html += '
'; + html += '
'; + + return html; + }); } 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 += '
'; + } + + return Promise.all([getAppInfo(apiClient), getDownloadsSectionHtml(apiClient, user, userSettings)]).then(function (responses) { + + var infoHtml = responses[0]; + var downloadsHtml = responses[1]; + + elem.innerHTML = html + downloadsHtml + infoHtml; + + bindHomeScreenSettingsIcon(elem, apiClient, user.Id, userSettings); + + if (infoHtml) { + bindAppInfoEvents(elem); + } + + 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 +1276,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..5d6c0f8745 100644 --- a/src/bower_components/emby-webcomponents/imageuploader/imageuploader.js +++ b/src/bower_components/emby-webcomponents/imageuploader/imageuploader.js @@ -1,80 +1,177 @@ -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/jpeg" && file.type !== "image/jpeg") { + 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; + + 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/style.css b/src/bower_components/emby-webcomponents/imageuploader/style.css index 2ac0954235..e259b4b7dc 100644 --- a/src/bower_components/emby-webcomponents/imageuploader/style.css +++ b/src/bower_components/emby-webcomponents/imageuploader/style.css @@ -1,17 +1,11 @@ -.imageEditor-dropZone { +.imageEditor-dropZone { border: .2em dashed currentcolor; - -webkit-border-radius: .25em; border-radius: .25em; + /* padding: 1.6em; */ text-align: center; position: relative; height: 12em; - 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; } \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/indicators/indicators.css b/src/bower_components/emby-webcomponents/indicators/indicators.css index 28999f2fe0..c2d089e1af 100644 --- a/src/bower_components/emby-webcomponents/indicators/indicators.css +++ b/src/bower_components/emby-webcomponents/indicators/indicators.css @@ -1,125 +1,96 @@ .itemProgressBar { background: #333; - background: rgba(51, 51, 51, .8); + background: rgba(51,51,51,.8); position: relative; - height: .28em + height: .28em; } .itemProgressBarForeground { position: absolute; top: 0; left: 0; - bottom: 0 + bottom: 0; } .indicator { - -webkit-border-radius: 100em; border-radius: 100em; display: -webkit-flex; - display: -webkit-box; 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-weight: 500; width: 2em; - height: 2em -} - -.countIndicator, -.playedIndicator { - -webkit-border-radius: 100em; - display: -webkit-flex; - display: -webkit-box; - -webkit-box-align: center; - -webkit-box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 1px 5px 0 rgba(0, 0, 0, .12), 0 3px 1px -2px rgba(0, 0, 0, .2) + height: 2em; } .timerIndicator { - color: #CB272A + color: #CB272A; } .timerIndicator-inactive { - color: #888 + color: #888; } -.indicator+.indicator { - margin-left: .25em +.indicator + .indicator { + margin-left: .25em; } .indicatorIcon { width: auto; height: auto; - font-size: 1.6em + font-size: 1.6em; } .countIndicator { border-radius: 100em; + display: -webkit-flex; display: flex; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; font-weight: 500; color: #fff; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 1px 5px 0 rgba(0, 0, 0, .12), 0 3px 1px -2px rgba(0, 0, 0, .2); - font-size: 88% + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2); + font-size: 88%; } .playedIndicator { border-radius: 100em; + display: -webkit-flex; display: flex; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; color: #fff; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 1px 5px 0 rgba(0, 0, 0, .12), 0 3px 1px -2px rgba(0, 0, 0, .2); - font-size: 80% + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2); + font-size: 80%; } .videoIndicator { background: #444; - -webkit-border-radius: 100em; border-radius: 100em; display: -webkit-flex; - display: -webkit-box; display: flex; - -webkit-box-align: center; - -webkit-align-items: center; align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; justify-content: center; color: #fff; - -webkit-box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 1px 5px 0 rgba(0, 0, 0, .12), 0 3px 1px -2px rgba(0, 0, 0, .2); - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 1px 5px 0 rgba(0, 0, 0, .12), 0 3px 1px -2px rgba(0, 0, 0, .2); - font-size: 88% + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2); + font-size: 88%; } .syncIndicator { - -webkit-border-radius: 100em; - border-radius: 100em + border-radius: 100em; } .emptySyncIndicator { background: #ccc; - color: #333 + color: #333; } -.missingIndicator, -.unairedIndicator { - background: #c33; +.missingIndicator, .unairedIndicator { + background: #cc3333; padding: .25em .5em; - -webkit-border-radius: 100em; border-radius: 100em; color: #fff; font-size: 84%; font-weight: 500; - margin: 0 .25em + margin: 0 .25em; } \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/indicators/indicators.js b/src/bower_components/emby-webcomponents/indicators/indicators.js index 41c3188aa2..3b6e6baf70 100644 --- a/src/bower_components/emby-webcomponents/indicators/indicators.js +++ b/src/bower_components/emby-webcomponents/indicators/indicators.js @@ -1,112 +1,274 @@ -define(["datetime", "itemHelper", "css!./indicators.css", "material-icons"], function(datetime, itemHelper) { - "use strict"; +define(['datetime', 'itemHelper', 'css!./indicators.css', 'material-icons'], function (datetime, itemHelper) { + 'use strict'; function enableProgressIndicator(item) { - return "Video" === item.MediaType && "TvChannel" !== item.Type || ("AudioBook" === item.Type || "AudioPodcast" === item.Type) + + if (item.MediaType === 'Video') { + if (item.Type !== 'TvChannel') { + return true; + } + } + + if (item.Type === 'AudioBook' || item.Type === 'AudioPodcast') { + return true; + } + + return false; } function getProgressHtml(pct, options) { - var containerClass = "itemProgressBar"; - return options && options.containerClass && (containerClass += " " + options.containerClass), '
' + + var containerClass = 'itemProgressBar'; + + if (options) { + if (options.containerClass) { + containerClass += ' ' + options.containerClass; + } + } + + return '
'; } function getAutoTimeProgressHtml(pct, options, isRecording, start, end) { - var containerClass = "itemProgressBar"; - options && options.containerClass && (containerClass += " " + options.containerClass); - var foregroundClass = "itemProgressBarForeground"; - return isRecording && (foregroundClass += " itemProgressBarForeground-recording"), '
' + + var containerClass = 'itemProgressBar'; + + if (options) { + if (options.containerClass) { + containerClass += ' ' + options.containerClass; + } + } + + var foregroundClass = 'itemProgressBarForeground'; + + if (isRecording) { + foregroundClass += ' itemProgressBarForeground-recording'; + } + + return '
'; } function getProgressBarHtml(item, options) { + var pct; - if (enableProgressIndicator(item) && "Recording" !== item.Type) { - var userData = options ? options.userData || item.UserData : item.UserData; - if (userData && (pct = userData.PlayedPercentage) && pct < 100) return getProgressHtml(pct, options) - } - if (("Program" === item.Type || "Timer" === item.Type || "Recording" === item.Type) && item.StartDate && item.EndDate) { - var startDate = 0, - endDate = 1; - try { - startDate = datetime.parseISO8601Date(item.StartDate).getTime() - } catch (err) {} - try { - endDate = datetime.parseISO8601Date(item.EndDate).getTime() - } catch (err) {} - if ((pct = ((new Date).getTime() - startDate) / (endDate - startDate) * 100) > 0 && pct < 100) { - return getAutoTimeProgressHtml(pct, options, "Timer" === item.Type || "Recording" === item.Type || item.TimerId, startDate, endDate) + + if (enableProgressIndicator(item) && item.Type !== "Recording") { + + var userData = options ? (options.userData || item.UserData) : item.UserData; + if (userData) { + pct = userData.PlayedPercentage; + + if (pct && pct < 100) { + + return getProgressHtml(pct, options); + } } } - return "" + + if ((item.Type === 'Program' || item.Type === 'Timer' || item.Type === 'Recording') && item.StartDate && item.EndDate) { + + var startDate = 0; + var endDate = 1; + + try { + + startDate = datetime.parseISO8601Date(item.StartDate).getTime(); + + } catch (err) { + } + + try { + + endDate = datetime.parseISO8601Date(item.EndDate).getTime(); + + } catch (err) { + } + + var now = new Date().getTime(); + var total = endDate - startDate; + pct = 100 * ((now - startDate) / total); + + if (pct > 0 && pct < 100) { + + var isRecording = item.Type === 'Timer' || item.Type === 'Recording' || item.TimerId; + + return getAutoTimeProgressHtml(pct, options, isRecording, startDate, endDate); + } + } + + return ''; } function enablePlayedIndicator(item) { - return itemHelper.canMarkPlayed(item) + + return itemHelper.canMarkPlayed(item); } function getPlayedIndicator(item) { + if (enablePlayedIndicator(item)) { + var userData = item.UserData || {}; - if (userData.UnplayedItemCount) return '
' + userData.UnplayedItemCount + "
"; - if (userData.PlayedPercentage && userData.PlayedPercentage >= 100 || userData.Played) return '
' + + if (userData.UnplayedItemCount) { + return '
' + userData.UnplayedItemCount + '
'; + } + + if (userData.PlayedPercentage && userData.PlayedPercentage >= 100 || (userData.Played)) { + return '
'; + } } - return "" + + return ''; } function getCountIndicatorHtml(count) { - return '
' + count + "
" + + return '
' + count + '
'; } function getChildCountIndicatorHtml(item, options) { + var minCount = 0; - return options && (minCount = options.minCount || minCount), item.ChildCount && item.ChildCount > minCount ? getCountIndicatorHtml(item.ChildCount) : "" + + if (options) { + minCount = options.minCount || minCount; + } + + if (item.ChildCount && item.ChildCount > minCount) { + return getCountIndicatorHtml(item.ChildCount); + } + + return ''; } 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 getSyncIndicator(item) { - return 100 === item.SyncPercent ? '
' : null != item.SyncPercent ? '
' : "" + + if (item.SyncPercent === 100) { + return '
'; + } else if (item.SyncPercent != null) { + return '
'; + } + + return ''; } function getTypeIndicator(item) { - return "Video" === item.Type ? '
' : "Folder" === item.Type || "PhotoAlbum" === item.Type ? '
' : "Photo" === item.Type ? '
' : "" + + if (item.Type === 'Video') { + + return '
'; + } + if (item.Type === 'Folder' || item.Type === 'PhotoAlbum') { + + return '
'; + } + if (item.Type === 'Photo') { + + return '
'; + //return '
'; + } + + return ''; } function getMissingIndicator(item) { - if ("Episode" === item.Type && "Virtual" === item.LocationType) { - if (item.PremiereDate) try { - if (datetime.parseISO8601Date(item.PremiereDate).getTime() > (new Date).getTime()) return '
Unaired
' - } catch (err) {} - return '
Missing
' + + if (item.Type === 'Episode' && item.LocationType === 'Virtual') { + + if (item.PremiereDate) { + try { + + var premiereDate = datetime.parseISO8601Date(item.PremiereDate).getTime(); + + if (premiereDate > new Date().getTime()) { + return '
Unaired
'; + } + + } catch (err) { + } + } + + return '
Missing
'; } - return "" + + return ''; } - function onAutoTimeProgress() { - var start = parseInt(this.getAttribute("data-starttime")), - end = parseInt(this.getAttribute("data-endtime")), - now = (new Date).getTime(), - total = end - start, - pct = (now - start) / total * 100; - pct = Math.min(100, pct), pct = Math.max(0, pct), this.querySelector(".itemProgressBarForeground").style.width = pct + "%" - } var ProgressBarPrototype = Object.create(HTMLDivElement.prototype); - return ProgressBarPrototype.attachedCallback = function() { - this.timeInterval && clearInterval(this.timeInterval), "time" === this.getAttribute("data-automode") && (this.timeInterval = setInterval(onAutoTimeProgress.bind(this), 6e4)) - }, ProgressBarPrototype.detachedCallback = function() { - this.timeInterval && (clearInterval(this.timeInterval), this.timeInterval = null) - }, document.registerElement("emby-progressbar", { + + function onAutoTimeProgress() { + + var start = parseInt(this.getAttribute('data-starttime')); + var end = parseInt(this.getAttribute('data-endtime')); + + var now = new Date().getTime(); + var total = end - start; + var pct = 100 * ((now - start) / total); + + pct = Math.min(100, pct); + pct = Math.max(0, pct); + + var itemProgressBarForeground = this.querySelector('.itemProgressBarForeground'); + itemProgressBarForeground.style.width = pct + '%'; + } + + ProgressBarPrototype.attachedCallback = function () { + + if (this.timeInterval) { + clearInterval(this.timeInterval); + } + + if (this.getAttribute('data-automode') === 'time') { + this.timeInterval = setInterval(onAutoTimeProgress.bind(this), 60000); + } + }; + + ProgressBarPrototype.detachedCallback = function () { + + if (this.timeInterval) { + clearInterval(this.timeInterval); + this.timeInterval = null; + } + }; + + document.registerElement('emby-progressbar', { prototype: ProgressBarPrototype, - extends: "div" - }), { + extends: 'div' + }); + + return { getProgressBarHtml: getProgressBarHtml, getPlayedIndicatorHtml: getPlayedIndicator, getChildCountIndicatorHtml: getChildCountIndicatorHtml, @@ -116,5 +278,5 @@ define(["datetime", "itemHelper", "css!./indicators.css", "material-icons"], fun getSyncIndicator: getSyncIndicator, getTypeIndicator: getTypeIndicator, getMissingIndicator: getMissingIndicator - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/input/api.js b/src/bower_components/emby-webcomponents/input/api.js index 13f0abc9ba..4cec8358cd 100644 --- a/src/bower_components/emby-webcomponents/input/api.js +++ b/src/bower_components/emby-webcomponents/input/api.js @@ -1,145 +1,245 @@ -define(["connectionManager", "playbackManager", "events", "inputManager", "focusManager", "appRouter"], function(connectionManager, playbackManager, events, inputManager, focusManager, appRouter) { - "use strict"; +define(['connectionManager', 'playbackManager', 'events', 'inputManager', 'focusManager', 'appRouter'], function (connectionManager, playbackManager, events, inputManager, focusManager, appRouter) { + 'use strict'; + + var serverNotifications = {}; function notifyApp() { - inputManager.notify() + + inputManager.notify(); } function displayMessage(cmd) { + var args = cmd.Arguments; - args.TimeoutMs ? require(["toast"], function(toast) { - toast({ - title: args.Header, - text: args.Text - }) - }) : require(["alert"], function(alert) { - alert({ - title: args.Header, - text: args.Text - }) - }) + + if (args.TimeoutMs) { + + require(['toast'], function (toast) { + toast({ title: args.Header, text: args.Text }); + }); + + } + else { + require(['alert'], function (alert) { + alert({ title: args.Header, text: args.Text }); + }); + } } function displayContent(cmd, apiClient) { - playbackManager.isPlayingLocally(["Video", "Book", "Game"]) || appRouter.showItem(cmd.Arguments.ItemId, apiClient.serverId()) + + if (!playbackManager.isPlayingLocally(['Video', 'Book', 'Game'])) { + appRouter.showItem(cmd.Arguments.ItemId, apiClient.serverId()); + } } function playTrailers(apiClient, itemId) { - apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function(item) { - playbackManager.playTrailers(item) - }) + + apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) { + + playbackManager.playTrailers(item); + }); } function processGeneralCommand(cmd, apiClient) { + + // Full list + // https://github.com/MediaBrowser/MediaBrowser/blob/master/MediaBrowser.Model/Session/GeneralCommand.cs#L23 + //console.log('Received command: ' + cmd.Name); + switch (cmd.Name) { - case "Select": - return void inputManager.trigger("select"); - case "Back": - return void inputManager.trigger("back"); - case "MoveUp": - return void inputManager.trigger("up"); - case "MoveDown": - return void inputManager.trigger("down"); - case "MoveLeft": - return void inputManager.trigger("left"); - case "MoveRight": - return void inputManager.trigger("right"); - case "PageUp": - return void inputManager.trigger("pageup"); - case "PageDown": - return void inputManager.trigger("pagedown"); - case "PlayTrailers": + + case 'Select': + inputManager.trigger('select'); + return; + case 'Back': + inputManager.trigger('back'); + return; + case 'MoveUp': + inputManager.trigger('up'); + return; + case 'MoveDown': + inputManager.trigger('down'); + return; + case 'MoveLeft': + inputManager.trigger('left'); + return; + case 'MoveRight': + inputManager.trigger('right'); + return; + case 'PageUp': + inputManager.trigger('pageup'); + return; + case 'PageDown': + inputManager.trigger('pagedown'); + return; + case 'PlayTrailers': playTrailers(apiClient, cmd.Arguments.ItemId); break; - case "SetRepeatMode": + case 'SetRepeatMode': playbackManager.setRepeatMode(cmd.Arguments.RepeatMode); break; - case "VolumeUp": - return void inputManager.trigger("volumeup"); - case "VolumeDown": - return void inputManager.trigger("volumedown"); - case "ChannelUp": - return void inputManager.trigger("channelup"); - case "ChannelDown": - return void inputManager.trigger("channeldown"); - case "Mute": - return void inputManager.trigger("mute"); - case "Unmute": - return void inputManager.trigger("unmute"); - case "ToggleMute": - return void inputManager.trigger("togglemute"); - case "SetVolume": - notifyApp(), playbackManager.setVolume(cmd.Arguments.Volume); + case 'VolumeUp': + inputManager.trigger('volumeup'); + return; + case 'VolumeDown': + inputManager.trigger('volumedown'); + return; + case 'ChannelUp': + inputManager.trigger('channelup'); + return; + case 'ChannelDown': + inputManager.trigger('channeldown'); + return; + case 'Mute': + inputManager.trigger('mute'); + return; + case 'Unmute': + inputManager.trigger('unmute'); + return; + case 'ToggleMute': + inputManager.trigger('togglemute'); + return; + case 'SetVolume': + notifyApp(); + playbackManager.setVolume(cmd.Arguments.Volume); break; - case "SetAudioStreamIndex": - notifyApp(), playbackManager.setAudioStreamIndex(parseInt(cmd.Arguments.Index)); + case 'SetAudioStreamIndex': + notifyApp(); + playbackManager.setAudioStreamIndex(parseInt(cmd.Arguments.Index)); break; - case "SetSubtitleStreamIndex": - notifyApp(), playbackManager.setSubtitleStreamIndex(parseInt(cmd.Arguments.Index)); + case 'SetSubtitleStreamIndex': + notifyApp(); + playbackManager.setSubtitleStreamIndex(parseInt(cmd.Arguments.Index)); break; - case "ToggleFullscreen": - return void inputManager.trigger("togglefullscreen"); - case "GoHome": - return void inputManager.trigger("home"); - case "GoToSettings": - return void inputManager.trigger("settings"); - case "DisplayContent": + case 'ToggleFullscreen': + inputManager.trigger('togglefullscreen'); + return; + case 'GoHome': + inputManager.trigger('home'); + return; + case 'GoToSettings': + inputManager.trigger('settings'); + return; + case 'DisplayContent': displayContent(cmd, apiClient); break; - case "GoToSearch": - return void inputManager.trigger("search"); - case "DisplayMessage": + case 'GoToSearch': + inputManager.trigger('search'); + return; + case 'DisplayMessage': displayMessage(cmd); break; - case "ToggleOsd": - case "ToggleContextMenu": - case "TakeScreenShot": - case "SendKey": + case 'ToggleOsd': + // todo break; - case "SendString": + case 'ToggleContextMenu': + // todo + break; + case 'TakeScreenShot': + // todo + break; + case 'SendKey': + // todo + break; + case 'SendString': + // todo focusManager.sendText(cmd.Arguments.String); break; default: - console.log("processGeneralCommand does not recognize: " + cmd.Name) + console.log('processGeneralCommand does not recognize: ' + cmd.Name); + break; } - notifyApp() + + notifyApp(); } function onMessageReceived(e, msg) { + var apiClient = this; - if ("Play" === msg.MessageType) { + + if (msg.MessageType === "Play") { + notifyApp(); var serverId = apiClient.serverInfo().Id; - "PlayNext" === msg.Data.PlayCommand ? playbackManager.queueNext({ - ids: msg.Data.ItemIds, - serverId: serverId - }) : "PlayLast" === msg.Data.PlayCommand ? playbackManager.queue({ - ids: msg.Data.ItemIds, - serverId: serverId - }) : playbackManager.play({ - ids: msg.Data.ItemIds, - startPositionTicks: msg.Data.StartPositionTicks, - mediaSourceId: msg.Data.MediaSourceId, - audioStreamIndex: msg.Data.AudioStreamIndex, - subtitleStreamIndex: msg.Data.SubtitleStreamIndex, - startIndex: msg.Data.StartIndex, - serverId: serverId - }) - } else if ("Playstate" === msg.MessageType) "Stop" === msg.Data.Command ? inputManager.trigger("stop") : "Pause" === msg.Data.Command ? inputManager.trigger("pause") : "Unpause" === msg.Data.Command ? inputManager.trigger("play") : "PlayPause" === msg.Data.Command ? inputManager.trigger("playpause") : "Seek" === msg.Data.Command ? playbackManager.seek(msg.Data.SeekPositionTicks) : "NextTrack" === msg.Data.Command ? inputManager.trigger("next") : "PreviousTrack" === msg.Data.Command ? inputManager.trigger("previous") : notifyApp(); - else if ("GeneralCommand" === msg.MessageType) { + + if (msg.Data.PlayCommand === "PlayNext") { + playbackManager.queueNext({ ids: msg.Data.ItemIds, serverId: serverId }); + } + else if (msg.Data.PlayCommand === "PlayLast") { + playbackManager.queue({ ids: msg.Data.ItemIds, serverId: serverId }); + } + else { + playbackManager.play({ + ids: msg.Data.ItemIds, + startPositionTicks: msg.Data.StartPositionTicks, + mediaSourceId: msg.Data.MediaSourceId, + audioStreamIndex: msg.Data.AudioStreamIndex, + subtitleStreamIndex: msg.Data.SubtitleStreamIndex, + startIndex: msg.Data.StartIndex, + serverId: serverId + }); + } + + } + else if (msg.MessageType === "Playstate") { + + if (msg.Data.Command === 'Stop') { + inputManager.trigger('stop'); + } + else if (msg.Data.Command === 'Pause') { + inputManager.trigger('pause'); + } + else if (msg.Data.Command === 'Unpause') { + inputManager.trigger('play'); + } + else if (msg.Data.Command === 'PlayPause') { + inputManager.trigger('playpause'); + } + else if (msg.Data.Command === 'Seek') { + playbackManager.seek(msg.Data.SeekPositionTicks); + } + else if (msg.Data.Command === 'NextTrack') { + inputManager.trigger('next'); + } + else if (msg.Data.Command === 'PreviousTrack') { + inputManager.trigger('previous'); + } else { + notifyApp(); + } + } + else if (msg.MessageType === "GeneralCommand") { var cmd = msg.Data; - processGeneralCommand(cmd, apiClient) - } else if ("UserDataChanged" === msg.MessageType) { - if (msg.Data.UserId === apiClient.getCurrentUserId()) - for (var i = 0, length = msg.Data.UserDataList.length; i < length; i++) events.trigger(serverNotifications, "UserDataChanged", [apiClient, msg.Data.UserDataList[i]]) - } else events.trigger(serverNotifications, msg.MessageType, [apiClient, msg.Data]) + processGeneralCommand(cmd, apiClient); + } + else if (msg.MessageType === "UserDataChanged") { + + if (msg.Data.UserId === apiClient.getCurrentUserId()) { + + for (var i = 0, length = msg.Data.UserDataList.length; i < length; i++) { + events.trigger(serverNotifications, 'UserDataChanged', [apiClient, msg.Data.UserDataList[i]]); + } + } + } + else { + + events.trigger(serverNotifications, msg.MessageType, [apiClient, msg.Data]); + } + } function bindEvents(apiClient) { - events.off(apiClient, "message", onMessageReceived), events.on(apiClient, "message", onMessageReceived) + + events.off(apiClient, "message", onMessageReceived); + events.on(apiClient, "message", onMessageReceived); } - var serverNotifications = {}; - return connectionManager.getApiClients().forEach(bindEvents), events.on(connectionManager, "apiclientcreated", function(e, newApiClient) { - bindEvents(newApiClient) - }), serverNotifications + + connectionManager.getApiClients().forEach(bindEvents); + + events.on(connectionManager, 'apiclientcreated', function (e, newApiClient) { + + bindEvents(newApiClient); + }); + + return serverNotifications; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/input/gamepadtokey.js b/src/bower_components/emby-webcomponents/input/gamepadtokey.js index e02a8fed4e..e8667bef27 100644 --- a/src/bower_components/emby-webcomponents/input/gamepadtokey.js +++ b/src/bower_components/emby-webcomponents/input/gamepadtokey.js @@ -1,153 +1,370 @@ -require(["apphost"], function(appHost) { +// # The MIT License (MIT) +// # +// # Copyright (c) 2016 Microsoft. All rights reserved. +// # +// # Permission is hereby granted, free of charge, to any person obtaining a copy +// # of this software and associated documentation files (the "Software"), to deal +// # in the Software without restriction, including without limitation the rights +// # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// # copies of the Software, and to permit persons to whom the Software is +// # furnished to do so, subject to the following conditions: +// # +// # The above copyright notice and this permission notice shall be included in +// # all copies or substantial portions of the Software. +// # +// # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// # THE SOFTWARE. +require(['apphost'], function (appHost) { "use strict"; + var _GAMEPAD_A_BUTTON_INDEX = 0, + _GAMEPAD_B_BUTTON_INDEX = 1, + _GAMEPAD_DPAD_UP_BUTTON_INDEX = 12, + _GAMEPAD_DPAD_DOWN_BUTTON_INDEX = 13, + _GAMEPAD_DPAD_LEFT_BUTTON_INDEX = 14, + _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX = 15, + _GAMEPAD_A_KEY = "GamepadA", + _GAMEPAD_B_KEY = "GamepadB", + _GAMEPAD_DPAD_UP_KEY = "GamepadDPadUp", + _GAMEPAD_DPAD_DOWN_KEY = "GamepadDPadDown", + _GAMEPAD_DPAD_LEFT_KEY = "GamepadDPadLeft", + _GAMEPAD_DPAD_RIGHT_KEY = "GamepadDPadRight", + _GAMEPAD_LEFT_THUMBSTICK_UP_KEY = "GamepadLeftThumbStickUp", + _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEY = "GamepadLeftThumbStickDown", + _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEY = "GamepadLeftThumbStickLeft", + _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEY = "GamepadLeftThumbStickRight", + _GAMEPAD_A_KEYCODE = 0, + _GAMEPAD_B_KEYCODE = 27, + _GAMEPAD_DPAD_UP_KEYCODE = 38, + _GAMEPAD_DPAD_DOWN_KEYCODE = 40, + _GAMEPAD_DPAD_LEFT_KEYCODE = 37, + _GAMEPAD_DPAD_RIGHT_KEYCODE = 39, + _GAMEPAD_LEFT_THUMBSTICK_UP_KEYCODE = 38, + _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEYCODE = 40, + _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEYCODE = 37, + _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEYCODE = 39, + _THUMB_STICK_THRESHOLD = 0.75; + + var _leftThumbstickUpPressed = false, + _leftThumbstickDownPressed = false, + _leftThumbstickLeftPressed = false, + _leftThumbstickRightPressed = false, + _dPadUpPressed = false, + _dPadDownPressed = false, + _dPadLeftPressed = false, + _dPadRightPressed = false, + _gamepadAPressed = false, + _gamepadBPressed = false; + + // The set of buttons on the gamepad we listen for. + var ProcessedButtons = [ + _GAMEPAD_DPAD_UP_BUTTON_INDEX, + _GAMEPAD_DPAD_DOWN_BUTTON_INDEX, + _GAMEPAD_DPAD_LEFT_BUTTON_INDEX, + _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX, + _GAMEPAD_A_BUTTON_INDEX, + _GAMEPAD_B_BUTTON_INDEX + ]; + + var _ButtonPressedState = {}; + _ButtonPressedState.getgamepadA = function () { + return _gamepadAPressed; + }; + + _ButtonPressedState.setgamepadA = function (newPressedState) { + raiseKeyEvent(_gamepadAPressed, newPressedState, _GAMEPAD_A_KEY, _GAMEPAD_A_KEYCODE, false, true); + _gamepadAPressed = newPressedState; + }; + + _ButtonPressedState.getgamepadB = function () { + return _gamepadBPressed; + }; + + _ButtonPressedState.setgamepadB = function (newPressedState) { + raiseKeyEvent(_gamepadBPressed, newPressedState, _GAMEPAD_B_KEY, _GAMEPAD_B_KEYCODE); + _gamepadBPressed = newPressedState; + }; + + _ButtonPressedState.getleftThumbstickUp = function () { + return _leftThumbstickUpPressed; + }; + + _ButtonPressedState.setleftThumbstickUp = function (newPressedState) { + raiseKeyEvent(_leftThumbstickUpPressed, newPressedState, _GAMEPAD_LEFT_THUMBSTICK_UP_KEY, _GAMEPAD_LEFT_THUMBSTICK_UP_KEYCODE, true); + _leftThumbstickUpPressed = newPressedState; + }; + + _ButtonPressedState.getleftThumbstickDown = function () { + return _leftThumbstickDownPressed; + }; + + _ButtonPressedState.setleftThumbstickDown = function (newPressedState) { + raiseKeyEvent(_leftThumbstickDownPressed, newPressedState, _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEY, _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEYCODE, true); + _leftThumbstickDownPressed = newPressedState; + }; + + _ButtonPressedState.getleftThumbstickLeft = function () { + return _leftThumbstickLeftPressed; + }; + + _ButtonPressedState.setleftThumbstickLeft = function (newPressedState) { + raiseKeyEvent(_leftThumbstickLeftPressed, newPressedState, _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEY, _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEYCODE, true); + _leftThumbstickLeftPressed = newPressedState; + }; + + _ButtonPressedState.getleftThumbstickRight = function () { + return _leftThumbstickRightPressed; + }; + + _ButtonPressedState.setleftThumbstickRight = function (newPressedState) { + raiseKeyEvent(_leftThumbstickRightPressed, newPressedState, _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEY, _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEYCODE, true); + _leftThumbstickRightPressed = newPressedState; + }; + + _ButtonPressedState.getdPadUp = function () { + return _dPadUpPressed; + }; + + _ButtonPressedState.setdPadUp = function (newPressedState) { + raiseKeyEvent(_dPadUpPressed, newPressedState, _GAMEPAD_DPAD_UP_KEY, _GAMEPAD_DPAD_UP_KEYCODE, true); + _dPadUpPressed = newPressedState; + }; + + _ButtonPressedState.getdPadDown = function () { + return _dPadDownPressed; + }; + + _ButtonPressedState.setdPadDown = function (newPressedState) { + raiseKeyEvent(_dPadDownPressed, newPressedState, _GAMEPAD_DPAD_DOWN_KEY, _GAMEPAD_DPAD_DOWN_KEYCODE, true); + _dPadDownPressed = newPressedState; + }; + + _ButtonPressedState.getdPadLeft = function () { + return _dPadLeftPressed; + }; + + _ButtonPressedState.setdPadLeft = function (newPressedState) { + raiseKeyEvent(_dPadLeftPressed, newPressedState, _GAMEPAD_DPAD_LEFT_KEY, _GAMEPAD_DPAD_LEFT_KEYCODE, true); + _dPadLeftPressed = newPressedState; + }; + + _ButtonPressedState.getdPadRight = function () { + return _dPadRightPressed; + }; + + _ButtonPressedState.setdPadRight = function (newPressedState) { + raiseKeyEvent(_dPadRightPressed, newPressedState, _GAMEPAD_DPAD_RIGHT_KEY, _GAMEPAD_DPAD_RIGHT_KEYCODE, true); + _dPadRightPressed = newPressedState; + }; + + var times = {}; + function throttle(key) { var time = times[key] || 0; - return (new Date).getTime() - time >= 200 + var now = new Date().getTime(); + + if ((now - time) >= 200) { + //times[key] = now; + return true; + } + + return false; } function resetThrottle(key) { - times[key] = (new Date).getTime() + times[key] = new Date().getTime(); } + var isElectron = navigator.userAgent.toLowerCase().indexOf('electron') !== -1; function allowInput() { - return !(!isElectron && document.hidden) && "Minimized" !== appHost.getWindowState() + + // This would be nice but always seems to return true with electron + if (!isElectron && document.hidden) { + return false; + } + + if (appHost.getWindowState() === 'Minimized') { + return false; + } + + return true; } function raiseEvent(name, key, keyCode) { - if (allowInput()) { - var event = document.createEvent("Event"); - event.initEvent(name, !0, !0), event.key = key, event.keyCode = keyCode, (document.activeElement || document.body).dispatchEvent(event) + + if (!allowInput()) { + return; } + + var event = document.createEvent('Event'); + event.initEvent(name, true, true); + event.key = key; + event.keyCode = keyCode; + (document.activeElement || document.body).dispatchEvent(event); } function clickElement(elem) { - allowInput() && elem.click() + + if (!allowInput()) { + return; + } + + elem.click(); } function raiseKeyEvent(oldPressedState, newPressedState, key, keyCode, enableRepeatKeyDown, clickonKeyUp) { - if (!0 === newPressedState) { - var fire = !1; - !1 === oldPressedState ? (fire = !0, resetThrottle(key)) : enableRepeatKeyDown && (fire = throttle(key)), fire && keyCode && raiseEvent("keydown", key, keyCode) - } else !1 === newPressedState && !0 === oldPressedState && (resetThrottle(key), keyCode && raiseEvent("keyup", key, keyCode), clickonKeyUp && clickElement(document.activeElement || window)) + + // No-op if oldPressedState === newPressedState + if (newPressedState === true) { + + // button down + var fire = false; + + // always fire if this is the initial down press + if (oldPressedState === false) { + fire = true; + resetThrottle(key); + } else if (enableRepeatKeyDown) { + fire = throttle(key); + } + + if (fire && keyCode) { + raiseEvent("keydown", key, keyCode); + } + + } else if (newPressedState === false && oldPressedState === true) { + + resetThrottle(key); + + // button up + if (keyCode) { + raiseEvent("keyup", key, keyCode); + } + if (clickonKeyUp) { + clickElement(document.activeElement || window); + } + } } function runInputLoop() { + // Get the latest gamepad state. var gamepads; - navigator.getGamepads ? gamepads = navigator.getGamepads() : navigator.webkitGetGamepads && (gamepads = navigator.webkitGetGamepads()), gamepads = gamepads || []; + if (navigator.getGamepads) { + gamepads = navigator.getGamepads(); + } else if (navigator.webkitGetGamepads) { + gamepads = navigator.webkitGetGamepads(); + } + gamepads = gamepads || []; var i, j, len; for (i = 0, len = gamepads.length; i < len; i++) { var gamepad = gamepads[i]; if (gamepad) { - var axes = gamepad.axes, - leftStickX = axes[0], - leftStickY = axes[1]; - leftStickX > _THUMB_STICK_THRESHOLD ? _ButtonPressedState.setleftThumbstickRight(!0) : leftStickX < -_THUMB_STICK_THRESHOLD ? _ButtonPressedState.setleftThumbstickLeft(!0) : leftStickY < -_THUMB_STICK_THRESHOLD ? _ButtonPressedState.setleftThumbstickUp(!0) : leftStickY > _THUMB_STICK_THRESHOLD ? _ButtonPressedState.setleftThumbstickDown(!0) : (_ButtonPressedState.setleftThumbstickLeft(!1), _ButtonPressedState.setleftThumbstickRight(!1), _ButtonPressedState.setleftThumbstickUp(!1), _ButtonPressedState.setleftThumbstickDown(!1)); + // Iterate through the axes + var axes = gamepad.axes; + var leftStickX = axes[0]; + var leftStickY = axes[1]; + if (leftStickX > _THUMB_STICK_THRESHOLD) { // Right + _ButtonPressedState.setleftThumbstickRight(true); + } else if (leftStickX < -_THUMB_STICK_THRESHOLD) { // Left + _ButtonPressedState.setleftThumbstickLeft(true); + } else if (leftStickY < -_THUMB_STICK_THRESHOLD) { // Up + _ButtonPressedState.setleftThumbstickUp(true); + } else if (leftStickY > _THUMB_STICK_THRESHOLD) { // Down + _ButtonPressedState.setleftThumbstickDown(true); + } else { + _ButtonPressedState.setleftThumbstickLeft(false); + _ButtonPressedState.setleftThumbstickRight(false); + _ButtonPressedState.setleftThumbstickUp(false); + _ButtonPressedState.setleftThumbstickDown(false); + } + // Iterate through the buttons to see if Left thumbstick, DPad, A and B are pressed. var buttons = gamepad.buttons; - for (j = 0, len = buttons.length; j < len; j++) - if (-1 !== ProcessedButtons.indexOf(j)) - if (buttons[j].pressed) switch (j) { - case _GAMEPAD_DPAD_UP_BUTTON_INDEX: - _ButtonPressedState.setdPadUp(!0); - break; - case _GAMEPAD_DPAD_DOWN_BUTTON_INDEX: - _ButtonPressedState.setdPadDown(!0); - break; - case _GAMEPAD_DPAD_LEFT_BUTTON_INDEX: - _ButtonPressedState.setdPadLeft(!0); - break; - case _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX: - _ButtonPressedState.setdPadRight(!0); - break; - case _GAMEPAD_A_BUTTON_INDEX: - _ButtonPressedState.setgamepadA(!0); - break; - case _GAMEPAD_B_BUTTON_INDEX: - _ButtonPressedState.setgamepadB(!0) - } else switch (j) { - case _GAMEPAD_DPAD_UP_BUTTON_INDEX: - _ButtonPressedState.getdPadUp() && _ButtonPressedState.setdPadUp(!1); - break; - case _GAMEPAD_DPAD_DOWN_BUTTON_INDEX: - _ButtonPressedState.getdPadDown() && _ButtonPressedState.setdPadDown(!1); - break; - case _GAMEPAD_DPAD_LEFT_BUTTON_INDEX: - _ButtonPressedState.getdPadLeft() && _ButtonPressedState.setdPadLeft(!1); - break; - case _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX: - _ButtonPressedState.getdPadRight() && _ButtonPressedState.setdPadRight(!1); - break; - case _GAMEPAD_A_BUTTON_INDEX: - _ButtonPressedState.getgamepadA() && _ButtonPressedState.setgamepadA(!1); - break; - case _GAMEPAD_B_BUTTON_INDEX: - _ButtonPressedState.getgamepadB() && _ButtonPressedState.setgamepadB(!1) + for (j = 0, len = buttons.length; j < len; j++) { + if (ProcessedButtons.indexOf(j) !== -1) { + + if (buttons[j].pressed) { + switch (j) { + case _GAMEPAD_DPAD_UP_BUTTON_INDEX: + _ButtonPressedState.setdPadUp(true); + break; + case _GAMEPAD_DPAD_DOWN_BUTTON_INDEX: + _ButtonPressedState.setdPadDown(true); + break; + case _GAMEPAD_DPAD_LEFT_BUTTON_INDEX: + _ButtonPressedState.setdPadLeft(true); + break; + case _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX: + _ButtonPressedState.setdPadRight(true); + break; + case _GAMEPAD_A_BUTTON_INDEX: + _ButtonPressedState.setgamepadA(true); + break; + case _GAMEPAD_B_BUTTON_INDEX: + _ButtonPressedState.setgamepadB(true); + break; + default: + // No-op + break; + } + } else { + switch (j) { + case _GAMEPAD_DPAD_UP_BUTTON_INDEX: + if (_ButtonPressedState.getdPadUp()) { + _ButtonPressedState.setdPadUp(false); + } + break; + case _GAMEPAD_DPAD_DOWN_BUTTON_INDEX: + if (_ButtonPressedState.getdPadDown()) { + _ButtonPressedState.setdPadDown(false); + } + break; + case _GAMEPAD_DPAD_LEFT_BUTTON_INDEX: + if (_ButtonPressedState.getdPadLeft()) { + _ButtonPressedState.setdPadLeft(false); + } + break; + case _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX: + if (_ButtonPressedState.getdPadRight()) { + _ButtonPressedState.setdPadRight(false); + } + break; + case _GAMEPAD_A_BUTTON_INDEX: + if (_ButtonPressedState.getgamepadA()) { + _ButtonPressedState.setgamepadA(false); + } + break; + case _GAMEPAD_B_BUTTON_INDEX: + if (_ButtonPressedState.getgamepadB()) { + _ButtonPressedState.setgamepadB(false); + } + break; + default: + // No-op + break; + } } + } + } } } - requestAnimationFrame(runInputLoop) + // Schedule the next one + requestAnimationFrame(runInputLoop); } - var _GAMEPAD_A_BUTTON_INDEX = 0, - _GAMEPAD_B_BUTTON_INDEX = 1, - _GAMEPAD_DPAD_UP_BUTTON_INDEX = 12, - _GAMEPAD_DPAD_DOWN_BUTTON_INDEX = 13, - _GAMEPAD_DPAD_LEFT_BUTTON_INDEX = 14, - _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX = 15, - _THUMB_STICK_THRESHOLD = .75, - _leftThumbstickUpPressed = !1, - _leftThumbstickDownPressed = !1, - _leftThumbstickLeftPressed = !1, - _leftThumbstickRightPressed = !1, - _dPadUpPressed = !1, - _dPadDownPressed = !1, - _dPadLeftPressed = !1, - _dPadRightPressed = !1, - _gamepadAPressed = !1, - _gamepadBPressed = !1, - ProcessedButtons = [_GAMEPAD_DPAD_UP_BUTTON_INDEX, _GAMEPAD_DPAD_DOWN_BUTTON_INDEX, _GAMEPAD_DPAD_LEFT_BUTTON_INDEX, _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX, _GAMEPAD_A_BUTTON_INDEX, _GAMEPAD_B_BUTTON_INDEX], - _ButtonPressedState = {}; - _ButtonPressedState.getgamepadA = function() { - return _gamepadAPressed - }, _ButtonPressedState.setgamepadA = function(newPressedState) { - raiseKeyEvent(_gamepadAPressed, newPressedState, "GamepadA", 0, !1, !0), _gamepadAPressed = newPressedState - }, _ButtonPressedState.getgamepadB = function() { - return _gamepadBPressed - }, _ButtonPressedState.setgamepadB = function(newPressedState) { - raiseKeyEvent(_gamepadBPressed, newPressedState, "GamepadB", 27), _gamepadBPressed = newPressedState - }, _ButtonPressedState.getleftThumbstickUp = function() { - return _leftThumbstickUpPressed - }, _ButtonPressedState.setleftThumbstickUp = function(newPressedState) { - raiseKeyEvent(_leftThumbstickUpPressed, newPressedState, "GamepadLeftThumbStickUp", 38, !0), _leftThumbstickUpPressed = newPressedState - }, _ButtonPressedState.getleftThumbstickDown = function() { - return _leftThumbstickDownPressed - }, _ButtonPressedState.setleftThumbstickDown = function(newPressedState) { - raiseKeyEvent(_leftThumbstickDownPressed, newPressedState, "GamepadLeftThumbStickDown", 40, !0), _leftThumbstickDownPressed = newPressedState - }, _ButtonPressedState.getleftThumbstickLeft = function() { - return _leftThumbstickLeftPressed - }, _ButtonPressedState.setleftThumbstickLeft = function(newPressedState) { - raiseKeyEvent(_leftThumbstickLeftPressed, newPressedState, "GamepadLeftThumbStickLeft", 37, !0), _leftThumbstickLeftPressed = newPressedState - }, _ButtonPressedState.getleftThumbstickRight = function() { - return _leftThumbstickRightPressed - }, _ButtonPressedState.setleftThumbstickRight = function(newPressedState) { - raiseKeyEvent(_leftThumbstickRightPressed, newPressedState, "GamepadLeftThumbStickRight", 39, !0), _leftThumbstickRightPressed = newPressedState - }, _ButtonPressedState.getdPadUp = function() { - return _dPadUpPressed - }, _ButtonPressedState.setdPadUp = function(newPressedState) { - raiseKeyEvent(_dPadUpPressed, newPressedState, "GamepadDPadUp", 38, !0), _dPadUpPressed = newPressedState - }, _ButtonPressedState.getdPadDown = function() { - return _dPadDownPressed - }, _ButtonPressedState.setdPadDown = function(newPressedState) { - raiseKeyEvent(_dPadDownPressed, newPressedState, "GamepadDPadDown", 40, !0), _dPadDownPressed = newPressedState - }, _ButtonPressedState.getdPadLeft = function() { - return _dPadLeftPressed - }, _ButtonPressedState.setdPadLeft = function(newPressedState) { - raiseKeyEvent(_dPadLeftPressed, newPressedState, "GamepadDPadLeft", 37, !0), _dPadLeftPressed = newPressedState - }, _ButtonPressedState.getdPadRight = function() { - return _dPadRightPressed - }, _ButtonPressedState.setdPadRight = function(newPressedState) { - raiseKeyEvent(_dPadRightPressed, newPressedState, "GamepadDPadRight", 39, !0), _dPadRightPressed = newPressedState - }; - var times = {}, - isElectron = -1 !== navigator.userAgent.toLowerCase().indexOf("electron"); - runInputLoop(), window.navigator && "string" == typeof window.navigator.gamepadInputEmulation && (window.navigator.gamepadInputEmulation = "gamepad") + + runInputLoop(); + + // The gamepadInputEmulation is a string property that exists in JavaScript UWAs and in WebViews in UWAs. + // It won't exist in Win8.1 style apps or browsers. + if (window.navigator && typeof window.navigator.gamepadInputEmulation === "string") { + // We want the gamepad to provide gamepad VK keyboard events rather than moving a + // mouse like cursor. Set to "keyboard", the gamepad will provide such keyboard events + // and provide input to the DOM navigator.getGamepads API. + window.navigator.gamepadInputEmulation = "gamepad"; + } + }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/input/mouse.js b/src/bower_components/emby-webcomponents/input/mouse.js index 56984561b0..16a9050530 100644 --- a/src/bower_components/emby-webcomponents/input/mouse.js +++ b/src/bower_components/emby-webcomponents/input/mouse.js @@ -1,75 +1,169 @@ -define(["inputManager", "focusManager", "browser", "layoutManager", "events", "dom"], function(inputmanager, focusManager, browser, layoutManager, events, dom) { - "use strict"; +define(['inputManager', 'focusManager', 'browser', 'layoutManager', 'events', 'dom'], function (inputmanager, focusManager, browser, layoutManager, events, dom) { + 'use strict'; + + var self = {}; + + var lastMouseInputTime = new Date().getTime(); + var isMouseIdle; function mouseIdleTime() { - return (new Date).getTime() - lastMouseInputTime + return new Date().getTime() - lastMouseInputTime; } function notifyApp() { - inputmanager.notifyMouseMove() + + inputmanager.notifyMouseMove(); } function removeIdleClasses() { + var classList = document.body.classList; - classList.remove("mouseIdle"), classList.remove("mouseIdle-tv") + + classList.remove('mouseIdle'); + classList.remove('mouseIdle-tv'); } function addIdleClasses() { + var classList = document.body.classList; - classList.add("mouseIdle"), layoutManager.tv && classList.add("mouseIdle-tv") + + classList.add('mouseIdle'); + + if (layoutManager.tv) { + classList.add('mouseIdle-tv'); + } } + var lastPointerMoveData; function onPointerMove(e) { - var eventX = e.screenX, - eventY = e.screenY; - if (void 0 !== eventX || void 0 !== eventY) { - var obj = lastPointerMoveData; - if (!obj) return void(lastPointerMoveData = { + + var eventX = e.screenX; + var eventY = e.screenY; + + // if coord don't exist how could it move + if (typeof eventX === "undefined" && typeof eventY === "undefined") { + return; + } + + var obj = lastPointerMoveData; + if (!obj) { + lastPointerMoveData = { x: eventX, y: eventY - }); - Math.abs(eventX - obj.x) < 10 && Math.abs(eventY - obj.y) < 10 || (obj.x = eventX, obj.y = eventY, lastMouseInputTime = (new Date).getTime(), notifyApp(), isMouseIdle && (isMouseIdle = !1, removeIdleClasses(), events.trigger(self, "mouseactive"))) + }; + 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; + + lastMouseInputTime = new Date().getTime(); + notifyApp(); + + if (isMouseIdle) { + isMouseIdle = false; + removeIdleClasses(); + events.trigger(self, 'mouseactive'); } } function onPointerEnter(e) { - if ("mouse" === (e.pointerType || (layoutManager.mobile ? "touch" : "mouse")) && !isMouseIdle) { - var parent = focusManager.focusableParent(e.target); - parent && focusManager.focus(parent) + + var pointerType = e.pointerType || (layoutManager.mobile ? 'touch' : 'mouse'); + + if (pointerType === 'mouse') { + if (!isMouseIdle) { + var parent = focusManager.focusableParent(e.target); + if (parent) { + focusManager.focus(parent); + } + } } } function enableFocusWithMouse() { - return !!layoutManager.tv && (!browser.web0s && !!browser.tv) + + if (!layoutManager.tv) { + return false; + } + + if (browser.web0s) { + return false; + } + + if (browser.tv) { + return true; + } + + return false; } function onMouseInterval() { - !isMouseIdle && mouseIdleTime() >= 5e3 && (isMouseIdle = !0, addIdleClasses(), events.trigger(self, "mouseidle")) + + if (!isMouseIdle && mouseIdleTime() >= 5000) { + isMouseIdle = true; + addIdleClasses(); + events.trigger(self, 'mouseidle'); + } } + var mouseInterval; function startMouseInterval() { - mouseInterval || (mouseInterval = setInterval(onMouseInterval, 5e3)) + + if (!mouseInterval) { + mouseInterval = setInterval(onMouseInterval, 5000); + } } function stopMouseInterval() { + var interval = mouseInterval; - interval && (clearInterval(interval), mouseInterval = null), removeIdleClasses() + + if (interval) { + clearInterval(interval); + mouseInterval = null; + } + + removeIdleClasses(); } function initMouse() { - stopMouseInterval(), dom.removeEventListener(document, window.PointerEvent ? "pointermove" : "mousemove", onPointerMove, { - passive: !0 - }), layoutManager.mobile || (startMouseInterval(), dom.addEventListener(document, window.PointerEvent ? "pointermove" : "mousemove", onPointerMove, { - passive: !0 - })), dom.removeEventListener(document, window.PointerEvent ? "pointerenter" : "mouseenter", onPointerEnter, { - capture: !0, - passive: !0 - }), enableFocusWithMouse() && dom.addEventListener(document, window.PointerEvent ? "pointerenter" : "mouseenter", onPointerEnter, { - capture: !0, - passive: !0 - }) + + stopMouseInterval(); + + dom.removeEventListener(document, (window.PointerEvent ? 'pointermove' : 'mousemove'), onPointerMove, { + passive: true + }); + + if (!layoutManager.mobile) { + startMouseInterval(); + + dom.addEventListener(document, (window.PointerEvent ? 'pointermove' : 'mousemove'), onPointerMove, { + passive: true + }); + } + + dom.removeEventListener(document, (window.PointerEvent ? 'pointerenter' : 'mouseenter'), onPointerEnter, { + capture: true, + passive: true + }); + + if (enableFocusWithMouse()) { + dom.addEventListener(document, (window.PointerEvent ? 'pointerenter' : 'mouseenter'), onPointerEnter, { + capture: true, + passive: true + }); + } } - var isMouseIdle, lastPointerMoveData, mouseInterval, self = {}, - lastMouseInputTime = (new Date).getTime(); - return initMouse(), events.on(layoutManager, "modechange", initMouse), self + + initMouse(); + + events.on(layoutManager, 'modechange', initMouse); + + return self; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/inputmanager.js b/src/bower_components/emby-webcomponents/inputmanager.js index 2977f284e3..f79eaa58da 100644 --- a/src/bower_components/emby-webcomponents/inputmanager.js +++ b/src/bower_components/emby-webcomponents/inputmanager.js @@ -1,211 +1,285 @@ -define(["playbackManager", "focusManager", "appRouter", "dom"], function(playbackManager, focusManager, appRouter, dom) { - "use strict"; +define(['playbackManager', 'focusManager', 'appRouter', 'dom'], function (playbackManager, focusManager, appRouter, dom) { + 'use strict'; + + var lastInputTime = new Date().getTime(); function notify() { - lastInputTime = (new Date).getTime(), handleCommand("unknown") + lastInputTime = new Date().getTime(); + + handleCommand('unknown'); } function notifyMouseMove() { - lastInputTime = (new Date).getTime() + lastInputTime = new Date().getTime(); } function idleTime() { - return (new Date).getTime() - lastInputTime + return new Date().getTime() - lastInputTime; } function select(sourceElement) { - sourceElement.click() + + sourceElement.click(); } + var eventListenerCount = 0; function on(scope, fn) { - eventListenerCount++, dom.addEventListener(scope, "command", fn, {}) + eventListenerCount++; + dom.addEventListener(scope, 'command', fn, { + + }); } function off(scope, fn) { - eventListenerCount && eventListenerCount--, dom.removeEventListener(scope, "command", fn, {}) + + if (eventListenerCount) { + eventListenerCount--; + } + + dom.removeEventListener(scope, 'command', fn, { + + }); } + var commandTimes = {}; + function checkCommandTime(command) { - var last = commandTimes[command] || 0, - now = (new Date).getTime(); - return !(now - last < 1e3) && (commandTimes[command] = now, !0) + + var last = commandTimes[command] || 0; + var now = new Date().getTime(); + + if ((now - last) < 1000) { + return false; + } + + commandTimes[command] = now; + return true; } function handleCommand(name, options) { - lastInputTime = (new Date).getTime(); - var sourceElement = options ? options.sourceElement : null; - if (sourceElement && (sourceElement = focusManager.focusableParent(sourceElement)), sourceElement = sourceElement || document.activeElement || window, eventListenerCount) { + + lastInputTime = new Date().getTime(); + + var sourceElement = (options ? options.sourceElement : null); + + if (sourceElement) { + sourceElement = focusManager.focusableParent(sourceElement); + } + + sourceElement = sourceElement || document.activeElement || window; + + if (eventListenerCount) { var customEvent = new CustomEvent("command", { detail: { command: name }, - bubbles: !0, - cancelable: !0 + bubbles: true, + cancelable: true }); - if (!sourceElement.dispatchEvent(customEvent)) return + + var eventResult = sourceElement.dispatchEvent(customEvent); + if (!eventResult) { + // event cancelled + return; + } } + switch (name) { - case "up": + + case 'up': focusManager.moveUp(sourceElement); break; - case "down": + case 'down': focusManager.moveDown(sourceElement); break; - case "left": + case 'left': focusManager.moveLeft(sourceElement); break; - case "right": + case 'right': focusManager.moveRight(sourceElement); break; - case "home": + case 'home': appRouter.goHome(); break; - case "settings": + case 'settings': appRouter.showSettings(); break; - case "back": + case 'back': appRouter.back(); break; - case "forward": + case 'forward': break; - case "select": + case 'select': select(sourceElement); break; - case "pageup": - case "pagedown": - case "end": + case 'pageup': break; - case "menu": - case "info": + case 'pagedown': break; - case "nextchapter": + case 'end': + break; + case 'menu': + case 'info': + break; + case 'nextchapter': playbackManager.nextChapter(); break; - case "next": - case "nexttrack": + case 'next': + case 'nexttrack': playbackManager.nextTrack(); break; - case "previous": - case "previoustrack": + case 'previous': + case 'previoustrack': playbackManager.previousTrack(); break; - case "previouschapter": + case 'previouschapter': playbackManager.previousChapter(); break; - case "guide": + case 'guide': appRouter.showGuide(); break; - case "recordedtv": + case 'recordedtv': appRouter.showRecordedTV(); break; - case "record": + case 'record': break; - case "livetv": + case 'livetv': appRouter.showLiveTV(); break; - case "mute": - playbackManager.setMute(!0); + case 'mute': + playbackManager.setMute(true); break; - case "unmute": - playbackManager.setMute(!1); + case 'unmute': + playbackManager.setMute(false); break; - case "togglemute": + case 'togglemute': playbackManager.toggleMute(); break; - case "channelup": + case 'channelup': playbackManager.channelUp(); break; - case "channeldown": + case 'channeldown': playbackManager.channelDown(); break; - case "volumedown": + case 'volumedown': playbackManager.volumeDown(); break; - case "volumeup": + case 'volumeup': playbackManager.volumeUp(); break; - case "play": + case 'play': playbackManager.unpause(); break; - case "pause": + case 'pause': playbackManager.pause(); break; - case "playpause": + case 'playpause': playbackManager.playPause(); break; - case "stop": - checkCommandTime("stop") && playbackManager.stop(); + case 'stop': + if (checkCommandTime('stop')) { + playbackManager.stop(); + } break; - case "changezoom": + case 'changezoom': playbackManager.toggleAspectRatio(); break; - case "changeaudiotrack": + case 'changeaudiotrack': playbackManager.changeAudioStream(); break; - case "changesubtitletrack": + case 'changesubtitletrack': playbackManager.changeSubtitleStream(); break; - case "search": + case 'search': appRouter.showSearch(); break; - case "favorites": + case 'favorites': appRouter.showFavorites(); break; - case "fastforward": + case 'fastforward': playbackManager.fastForward(); break; - case "rewind": + case 'rewind': playbackManager.rewind(); break; - case "togglefullscreen": + case 'togglefullscreen': playbackManager.toggleFullscreen(); break; - case "disabledisplaymirror": - playbackManager.enableDisplayMirroring(!1); + case 'disabledisplaymirror': + playbackManager.enableDisplayMirroring(false); break; - case "enabledisplaymirror": - playbackManager.enableDisplayMirroring(!0); + case 'enabledisplaymirror': + playbackManager.enableDisplayMirroring(true); break; - case "toggledisplaymirror": + case 'toggledisplaymirror': playbackManager.toggleDisplayMirroring(); break; - case "togglestats": + case 'togglestats': + //playbackManager.toggleStats(); break; - case "movies": - case "music": - case "tv": + case 'movies': + // TODO appRouter.goHome(); break; - case "nowplaying": + case 'music': + // TODO + appRouter.goHome(); + break; + case 'tv': + // TODO + appRouter.goHome(); + break; + case 'nowplaying': appRouter.showNowPlaying(); break; - case "save": - case "screensaver": - case "refresh": - case "changebrightness": - case "red": - case "green": - case "yellow": - case "blue": - case "grey": - case "brown": + case 'save': break; - case "repeatnone": - playbackManager.setRepeatMode("RepeatNone"); + case 'screensaver': + // TODO break; - case "repeatall": - playbackManager.setRepeatMode("RepeatAll"); + case 'refresh': + // TODO + break; + case 'changebrightness': + // TODO + break; + case 'red': + // TODO + break; + case 'green': + // TODO + break; + case 'yellow': + // TODO + break; + case 'blue': + // TODO + break; + case 'grey': + // TODO + break; + case 'brown': + // TODO + break; + case 'repeatnone': + playbackManager.setRepeatMode('RepeatNone'); + break; + case 'repeatall': + playbackManager.setRepeatMode('RepeatAll'); + break; + case 'repeatone': + playbackManager.setRepeatMode('RepeatOne'); + break; + default: break; - case "repeatone": - playbackManager.setRepeatMode("RepeatOne") } } - var lastInputTime = (new Date).getTime(), - eventListenerCount = 0, - commandTimes = {}; - return dom.addEventListener(document, "click", notify, { - passive: !0 - }), { + + dom.addEventListener(document, 'click', notify, { + passive: true + }); + + return { trigger: handleCommand, handle: handleCommand, notify: notify, @@ -213,5 +287,5 @@ define(["playbackManager", "focusManager", "appRouter", "dom"], function(playbac idleTime: idleTime, on: on, off: off - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/itemcontextmenu.js b/src/bower_components/emby-webcomponents/itemcontextmenu.js index ee61308267..439d3fc9ea 100644 --- a/src/bower_components/emby-webcomponents/itemcontextmenu.js +++ b/src/bower_components/emby-webcomponents/itemcontextmenu.js @@ -1,359 +1,672 @@ -define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter", "playbackManager", "loading", "appSettings", "browser", "actionsheet"], function(appHost, globalize, connectionManager, itemHelper, appRouter, playbackManager, loading, appSettings, browser, actionsheet) { - "use strict"; +define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter', 'playbackManager', 'loading', 'appSettings', 'browser', 'actionsheet'], function (appHost, globalize, connectionManager, itemHelper, appRouter, playbackManager, loading, appSettings, browser, actionsheet) { + 'use strict'; function getCommands(options) { - var item = options.item, - canPlay = playbackManager.canPlay(item), - commands = [], - user = options.user, - restrictOptions = (browser.operaTv || browser.web0s) && !user.Policy.IsAdministrator; - canPlay && "Photo" !== item.MediaType && (!1 !== options.play && commands.push({ - name: globalize.translate("sharedcomponents#Play"), - id: "resume" - }), options.playAllFromHere && "Program" !== item.Type && "TvChannel" !== item.Type && commands.push({ - name: globalize.translate("sharedcomponents#PlayAllFromHere"), - id: "playallfromhere" - })), playbackManager.canQueue(item) && (!1 !== options.queue && commands.push({ - name: globalize.translate("sharedcomponents#AddToPlayQueue"), - id: "queue" - }), !1 !== options.queue && commands.push({ - name: globalize.translate("sharedcomponents#PlayNext"), - id: "queuenext" - })), (item.IsFolder || "MusicArtist" === item.Type || "MusicGenre" === item.Type) && "livetv" !== item.CollectionType && !1 !== options.shuffle && commands.push({ - name: globalize.translate("sharedcomponents#Shuffle"), - id: "shuffle" - }), "Audio" !== item.MediaType && "MusicAlbum" !== item.Type && "MusicArtist" !== item.Type && "MusicGenre" !== item.Type || !1 === options.instantMix || itemHelper.isLocalItem(item) || commands.push({ - name: globalize.translate("sharedcomponents#InstantMix"), - id: "instantmix" - }), commands.length && commands.push({ - divider: !0 - }), restrictOptions || (itemHelper.supportsAddingToCollection(item) && commands.push({ - name: globalize.translate("sharedcomponents#AddToCollection"), - id: "addtocollection" - }), itemHelper.supportsAddingToPlaylist(item) && commands.push({ - name: globalize.translate("sharedcomponents#AddToPlaylist"), - id: "addtoplaylist" - })), "Timer" === item.Type && user.Policy.EnableLiveTvManagement && !1 !== options.cancelTimer && commands.push({ - name: globalize.translate("sharedcomponents#CancelRecording"), - id: "canceltimer" - }), "Recording" === item.Type && "InProgress" === item.Status && user.Policy.EnableLiveTvManagement && !1 !== options.cancelTimer && commands.push({ - name: globalize.translate("sharedcomponents#CancelRecording"), - id: "canceltimer" - }), "SeriesTimer" === item.Type && user.Policy.EnableLiveTvManagement && !1 !== options.cancelTimer && commands.push({ - name: globalize.translate("sharedcomponents#CancelSeries"), - id: "cancelseriestimer" - }), itemHelper.canConvert(item, user, connectionManager.getApiClient(item)) && commands.push({ - name: globalize.translate("sharedcomponents#Convert"), - id: "convert" - }), item.CanDelete && !1 !== options.deleteItem && ("Playlist" === item.Type || "BoxSet" === item.Type ? commands.push({ - name: globalize.translate("sharedcomponents#Delete"), - id: "delete" - }) : commands.push({ - name: globalize.translate("sharedcomponents#DeleteMedia"), - id: "delete" - })), item.CanDownload && appHost.supports("filedownload") && commands.push({ - name: globalize.translate("sharedcomponents#Download"), - id: "download" - }), appHost.supports("sync") && !1 !== options.syncLocal && itemHelper.canSync(user, item) && commands.push({ - name: globalize.translate("sharedcomponents#Download"), - id: "synclocal" + + var item = options.item; + + var canPlay = playbackManager.canPlay(item); + + var commands = []; + + var user = options.user; + + var restrictOptions = (browser.operaTv || browser.web0s) && !user.Policy.IsAdministrator; + + if (canPlay && item.MediaType !== 'Photo') { + if (options.play !== false) { + commands.push({ + name: globalize.translate('sharedcomponents#Play'), + id: 'resume' }); + } + + if (options.playAllFromHere && item.Type !== 'Program' && item.Type !== 'TvChannel') { + commands.push({ + name: globalize.translate('sharedcomponents#PlayAllFromHere'), + id: 'playallfromhere' + }); + } + } + + if (playbackManager.canQueue(item)) { + + if (options.queue !== false) { + commands.push({ + name: globalize.translate('sharedcomponents#AddToPlayQueue'), + id: 'queue' + }); + } + + if (options.queue !== false) { + commands.push({ + name: globalize.translate('sharedcomponents#PlayNext'), + id: 'queuenext' + }); + } + + //if (options.queueAllFromHere) { + // commands.push({ + // name: globalize.translate('sharedcomponents#QueueAllFromHere'), + // id: 'queueallfromhere' + // }); + //} + } + + + + if (item.IsFolder || item.Type === "MusicArtist" || item.Type === "MusicGenre") { + if (item.CollectionType !== 'livetv') { + if (options.shuffle !== false) { + commands.push({ + name: globalize.translate('sharedcomponents#Shuffle'), + id: 'shuffle' + }); + } + } + } + + if (item.MediaType === "Audio" || item.Type === "MusicAlbum" || item.Type === "MusicArtist" || item.Type === "MusicGenre") { + if (options.instantMix !== false && !itemHelper.isLocalItem(item)) { + commands.push({ + name: globalize.translate('sharedcomponents#InstantMix'), + id: 'instantmix' + }); + } + } + + if (commands.length) { + commands.push({ + divider: true + }); + } + + if (!restrictOptions) { + if (itemHelper.supportsAddingToCollection(item)) { + commands.push({ + name: globalize.translate('sharedcomponents#AddToCollection'), + id: 'addtocollection' + }); + } + + if (itemHelper.supportsAddingToPlaylist(item)) { + commands.push({ + name: globalize.translate('sharedcomponents#AddToPlaylist'), + id: 'addtoplaylist' + }); + } + } + + if ((item.Type === 'Timer') && user.Policy.EnableLiveTvManagement && options.cancelTimer !== false) { + commands.push({ + name: globalize.translate('sharedcomponents#CancelRecording'), + id: 'canceltimer' + }); + } + + if ((item.Type === 'Recording' && item.Status === 'InProgress') && user.Policy.EnableLiveTvManagement && options.cancelTimer !== false) { + commands.push({ + name: globalize.translate('sharedcomponents#CancelRecording'), + id: 'canceltimer' + }); + } + + if ((item.Type === 'SeriesTimer') && user.Policy.EnableLiveTvManagement && options.cancelTimer !== false) { + commands.push({ + name: globalize.translate('sharedcomponents#CancelSeries'), + id: 'cancelseriestimer' + }); + } + + if (itemHelper.canConvert(item, user, connectionManager.getApiClient(item))) { + commands.push({ + name: globalize.translate('sharedcomponents#Convert'), + id: 'convert' + }); + } + + if (item.CanDelete && options.deleteItem !== false) { + + if (item.Type === 'Playlist' || item.Type === 'BoxSet') { + commands.push({ + name: globalize.translate('sharedcomponents#Delete'), + id: 'delete' + }); + } else { + commands.push({ + name: globalize.translate('sharedcomponents#DeleteMedia'), + id: 'delete' + }); + } + } + + if (item.CanDownload && appHost.supports('filedownload')) { + commands.push({ + name: globalize.translate('sharedcomponents#Download'), + id: 'download' + }); + } + + if (appHost.supports('sync') && options.syncLocal !== false) { + if (itemHelper.canSync(user, item)) { + commands.push({ + name: globalize.translate('sharedcomponents#Download'), + id: 'synclocal' + }); + } + } + var canEdit = itemHelper.canEdit(user, item); - if (canEdit && !1 !== options.edit && "SeriesTimer" !== item.Type) { - var text = "Timer" === item.Type || "SeriesTimer" === item.Type ? globalize.translate("sharedcomponents#Edit") : globalize.translate("sharedcomponents#EditMetadata"); + if (canEdit) { + + if (options.edit !== false && item.Type !== 'SeriesTimer') { + + var text = (item.Type === 'Timer' || item.Type === 'SeriesTimer') ? globalize.translate('sharedcomponents#Edit') : globalize.translate('sharedcomponents#EditMetadata'); + commands.push({ name: text, - id: "edit" - }) + id: 'edit' + }); } - return itemHelper.canEditImages(user, item) && !1 !== options.editImages && commands.push({ - name: globalize.translate("sharedcomponents#EditImages"), - id: "editimages" - }), canEdit && ("Video" !== item.MediaType || "TvChannel" === item.Type || "Program" === item.Type || "Virtual" === item.LocationType || "Recording" === item.Type && "Completed" !== item.Status || !1 !== options.editSubtitles && commands.push({ - name: globalize.translate("sharedcomponents#EditSubtitles"), - id: "editsubtitles" - })), !1 !== options.identify && itemHelper.canIdentify(user, item) && commands.push({ - name: globalize.translate("sharedcomponents#Identify"), - id: "identify" - }), "Program" === item.Type && !1 !== options.record && item.TimerId && commands.push({ - name: Globalize.translate("sharedcomponents#ManageRecording"), - id: "record" - }), "Program" === item.Type && !1 !== options.record && (item.TimerId || commands.push({ - name: Globalize.translate("sharedcomponents#Record"), - id: "record" - })), itemHelper.canRefreshMetadata(item, user) && commands.push({ - name: globalize.translate("sharedcomponents#RefreshMetadata"), - id: "refresh" - }), item.PlaylistItemId && options.playlistId && commands.push({ - name: globalize.translate("sharedcomponents#RemoveFromPlaylist"), - id: "removefromplaylist" - }), options.collectionId && commands.push({ - name: globalize.translate("sharedcomponents#RemoveFromCollection"), - id: "removefromcollection" - }), restrictOptions || !0 === options.share && itemHelper.canShare(item, user) && commands.push({ - name: globalize.translate("sharedcomponents#Share"), - id: "share" - }), !1 !== options.sync && itemHelper.canSync(user, item) && commands.push({ - name: globalize.translate("sharedcomponents#Sync"), - id: "sync" - }), !1 !== options.openAlbum && item.AlbumId && "Photo" !== item.MediaType && commands.push({ - name: Globalize.translate("sharedcomponents#ViewAlbum"), - id: "album" - }), !1 !== options.openArtist && item.ArtistItems && item.ArtistItems.length && commands.push({ - name: Globalize.translate("sharedcomponents#ViewArtist"), - id: "artist" - }), commands + } + + if (itemHelper.canEditImages(user, item)) { + + if (options.editImages !== false) { + commands.push({ + name: globalize.translate('sharedcomponents#EditImages'), + id: 'editimages' + }); + } + } + + if (canEdit) { + + if (item.MediaType === 'Video' && item.Type !== 'TvChannel' && item.Type !== 'Program' && item.LocationType !== 'Virtual' && !(item.Type === 'Recording' && item.Status !== 'Completed')) { + if (options.editSubtitles !== false) { + commands.push({ + name: globalize.translate('sharedcomponents#EditSubtitles'), + id: 'editsubtitles' + }); + } + } + } + + if (options.identify !== false) { + if (itemHelper.canIdentify(user, item)) { + commands.push({ + name: globalize.translate('sharedcomponents#Identify'), + id: 'identify' + }); + } + } + + if (item.Type === 'Program' && options.record !== false) { + + if (item.TimerId) { + commands.push({ + name: Globalize.translate('sharedcomponents#ManageRecording'), + id: 'record' + }); + } + } + + if (item.Type === 'Program' && options.record !== false) { + + if (!item.TimerId) { + commands.push({ + name: Globalize.translate('sharedcomponents#Record'), + id: 'record' + }); + } + } + + if (itemHelper.canRefreshMetadata(item, user)) { + commands.push({ + name: globalize.translate('sharedcomponents#RefreshMetadata'), + id: 'refresh' + }); + } + + if (item.PlaylistItemId && options.playlistId) { + commands.push({ + name: globalize.translate('sharedcomponents#RemoveFromPlaylist'), + id: 'removefromplaylist' + }); + } + + if (options.collectionId) { + commands.push({ + name: globalize.translate('sharedcomponents#RemoveFromCollection'), + id: 'removefromcollection' + }); + } + + if (!restrictOptions) { + if (options.share === true) { + if (itemHelper.canShare(item, user)) { + commands.push({ + name: globalize.translate('sharedcomponents#Share'), + id: 'share' + }); + } + } + } + + if (options.sync !== false) { + if (itemHelper.canSync(user, item)) { + commands.push({ + name: globalize.translate('sharedcomponents#Sync'), + id: 'sync' + }); + } + } + + if (options.openAlbum !== false && item.AlbumId && item.MediaType !== 'Photo') { + commands.push({ + name: Globalize.translate('sharedcomponents#ViewAlbum'), + id: 'album' + }); + } + + if (options.openArtist !== false && item.ArtistItems && item.ArtistItems.length) { + commands.push({ + name: Globalize.translate('sharedcomponents#ViewArtist'), + id: 'artist' + }); + } + + return commands; } function getResolveFunction(resolve, id, changed, deleted) { - return function() { + + return function () { resolve({ command: id, updated: changed, deleted: deleted - }) + }); + }; } - } function executeCommand(item, id, options) { - var itemId = item.Id, - serverId = item.ServerId, - apiClient = connectionManager.getApiClient(serverId); - return new Promise(function(resolve, reject) { + + var itemId = item.Id; + var serverId = item.ServerId; + var apiClient = connectionManager.getApiClient(serverId); + + return new Promise(function (resolve, reject) { + switch (id) { - case "addtocollection": - require(["collectionEditor"], function(collectionEditor) { - (new collectionEditor).show({ + + case 'addtocollection': + { + require(['collectionEditor'], function (collectionEditor) { + + new collectionEditor().show({ items: [itemId], serverId: serverId - }).then(getResolveFunction(resolve, id, !0), getResolveFunction(resolve, id)) + + }).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); }); break; - case "addtoplaylist": - require(["playlistEditor"], function(playlistEditor) { - (new playlistEditor).show({ + } + case 'addtoplaylist': + { + require(['playlistEditor'], function (playlistEditor) { + + new playlistEditor().show({ items: [itemId], serverId: serverId - }).then(getResolveFunction(resolve, id, !0), getResolveFunction(resolve, id)) + + }).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); }); break; - case "download": - require(["fileDownloader"], function(fileDownloader) { + } + case 'download': + { + require(['fileDownloader'], function (fileDownloader) { var downloadHref = apiClient.getItemDownloadUrl(itemId); - fileDownloader.download([{ + + fileDownloader.download([ + { url: downloadHref, itemId: itemId, serverId: serverId - }]), getResolveFunction(getResolveFunction(resolve, id), id)() + }]); + + getResolveFunction(getResolveFunction(resolve, id), id)(); + }); + + break; + } + case 'editsubtitles': + { + require(['subtitleEditor'], function (subtitleEditor) { + + subtitleEditor.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); }); break; - case "editsubtitles": - require(["subtitleEditor"], function(subtitleEditor) { - subtitleEditor.show(itemId, serverId).then(getResolveFunction(resolve, id, !0), getResolveFunction(resolve, id)) - }); + } + case 'edit': + { + editItem(apiClient, item).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); break; - case "edit": - editItem(apiClient, item).then(getResolveFunction(resolve, id, !0), getResolveFunction(resolve, id)); - break; - case "editimages": - require(["imageEditor"], function(imageEditor) { + } + case 'editimages': + { + require(['imageEditor'], function (imageEditor) { + imageEditor.show({ itemId: itemId, serverId: serverId - }).then(getResolveFunction(resolve, id, !0), getResolveFunction(resolve, id)) + + }).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); }); break; - case "identify": - require(["itemIdentifier"], function(itemIdentifier) { - itemIdentifier.show(itemId, serverId).then(getResolveFunction(resolve, id, !0), getResolveFunction(resolve, id)) + } + case 'identify': + { + require(['itemIdentifier'], function (itemIdentifier) { + + itemIdentifier.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); }); break; - case "refresh": - refresh(apiClient, item), getResolveFunction(resolve, id)(); + } + case 'refresh': + { + refresh(apiClient, item); + getResolveFunction(resolve, id)(); break; - case "open": - appRouter.showItem(item), getResolveFunction(resolve, id)(); + } + case 'open': + { + appRouter.showItem(item); + getResolveFunction(resolve, id)(); break; - case "play": - play(item, !1), getResolveFunction(resolve, id)(); + } + case 'play': + { + play(item, false); + getResolveFunction(resolve, id)(); break; - case "resume": - play(item, !0), getResolveFunction(resolve, id)(); + } + case 'resume': + { + play(item, true); + getResolveFunction(resolve, id)(); break; - case "queue": - play(item, !1, !0), getResolveFunction(resolve, id)(); + } + case 'queue': + { + play(item, false, true); + getResolveFunction(resolve, id)(); break; - case "queuenext": - play(item, !1, !0, !0), getResolveFunction(resolve, id)(); + } + case 'queuenext': + { + play(item, false, true, true); + getResolveFunction(resolve, id)(); break; - case "record": - require(["recordingCreator"], function(recordingCreator) { - recordingCreator.show(itemId, serverId).then(getResolveFunction(resolve, id, !0), getResolveFunction(resolve, id)) + } + case 'record': + require(['recordingCreator'], function (recordingCreator) { + recordingCreator.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); }); break; - case "shuffle": - playbackManager.shuffle(item), getResolveFunction(resolve, id)(); + case 'shuffle': + { + playbackManager.shuffle(item); + getResolveFunction(resolve, id)(); break; - case "instantmix": - playbackManager.instantMix(item), getResolveFunction(resolve, id)(); + } + case 'instantmix': + { + playbackManager.instantMix(item); + getResolveFunction(resolve, id)(); break; - case "delete": - deleteItem(apiClient, item).then(getResolveFunction(resolve, id, !0, !0), getResolveFunction(resolve, id)); + } + case 'delete': + { + deleteItem(apiClient, item).then(getResolveFunction(resolve, id, true, true), getResolveFunction(resolve, id)); break; - case "share": + } + case 'share': + { navigator.share({ title: item.Name, text: item.Overview, url: "https://github.com/jellyfin/jellyfin" }); break; - case "album": - appRouter.showItem(item.AlbumId, item.ServerId), getResolveFunction(resolve, id)(); + } + case 'album': + { + appRouter.showItem(item.AlbumId, item.ServerId); + getResolveFunction(resolve, id)(); + break; + } + case 'artist': + { + appRouter.showItem(item.ArtistItems[0].Id, item.ServerId); + getResolveFunction(resolve, id)(); break; - case "artist": - appRouter.showItem(item.ArtistItems[0].Id, item.ServerId), getResolveFunction(resolve, id)(); + } + case 'playallfromhere': + { + getResolveFunction(resolve, id)(); break; - case "playallfromhere": - case "queueallfromhere": + } + case 'queueallfromhere': + { getResolveFunction(resolve, id)(); break; - case "convert": - require(["syncDialog"], function(syncDialog) { + } + case 'convert': + { + require(['syncDialog'], function (syncDialog) { syncDialog.showMenu({ items: [item], serverId: serverId, - mode: "convert" - }) - }), getResolveFunction(resolve, id)(); + mode: 'convert' + }); + }); + getResolveFunction(resolve, id)(); break; - case "sync": - require(["syncDialog"], function(syncDialog) { + } + case 'sync': + { + require(['syncDialog'], function (syncDialog) { syncDialog.showMenu({ items: [item], serverId: serverId, - mode: "sync" - }) - }), getResolveFunction(resolve, id)(); + mode: 'sync' + }); + }); + getResolveFunction(resolve, id)(); break; - case "synclocal": - require(["syncDialog"], function(syncDialog) { + } + case 'synclocal': + { + require(['syncDialog'], function (syncDialog) { syncDialog.showMenu({ items: [item], serverId: serverId, - mode: "download" - }) - }), getResolveFunction(resolve, id)(); + mode: 'download' + }); + }); + getResolveFunction(resolve, id)(); break; - case "removefromplaylist": + } + case 'removefromplaylist': + apiClient.ajax({ - url: apiClient.getUrl("Playlists/" + options.playlistId + "/Items", { - EntryIds: [item.PlaylistItemId].join(",") + + url: apiClient.getUrl('Playlists/' + options.playlistId + '/Items', { + EntryIds: [item.PlaylistItemId].join(',') }), - type: "DELETE" - }).then(function() { - getResolveFunction(resolve, id, !0)() + + type: 'DELETE' + + }).then(function () { + + getResolveFunction(resolve, id, true)(); }); + break; - case "removefromcollection": + case 'removefromcollection': + apiClient.ajax({ type: "DELETE", url: apiClient.getUrl("Collections/" + options.collectionId + "/Items", { - Ids: [item.Id].join(",") + + Ids: [item.Id].join(',') }) - }).then(function() { - getResolveFunction(resolve, id, !0)() + + }).then(function () { + + getResolveFunction(resolve, id, true)(); }); + break; - case "canceltimer": + case 'canceltimer': deleteTimer(apiClient, item, resolve, id); break; - case "cancelseriestimer": + case 'cancelseriestimer': deleteSeriesTimer(apiClient, item, resolve, id); break; default: - reject() + reject(); + break; } - }) + }); } function deleteTimer(apiClient, item, resolve, command) { - require(["recordingHelper"], function(recordingHelper) { + + require(['recordingHelper'], function (recordingHelper) { + var timerId = item.TimerId || item.Id; - recordingHelper.cancelTimerWithConfirmation(timerId, item.ServerId).then(function() { - getResolveFunction(resolve, command, !0)() - }) - }) + + recordingHelper.cancelTimerWithConfirmation(timerId, item.ServerId).then(function () { + getResolveFunction(resolve, command, true)(); + }); + }); } function deleteSeriesTimer(apiClient, item, resolve, command) { - require(["recordingHelper"], function(recordingHelper) { - recordingHelper.cancelSeriesTimerWithConfirmation(item.Id, item.ServerId).then(function() { - getResolveFunction(resolve, command, !0)() - }) - }) + + require(['recordingHelper'], function (recordingHelper) { + + recordingHelper.cancelSeriesTimerWithConfirmation(item.Id, item.ServerId).then(function () { + getResolveFunction(resolve, command, true)(); + }); + }); } function play(item, resume, queue, queueNext) { - var method = queue ? queueNext ? "queueNext" : "queue" : "play", - startPosition = 0; - resume && item.UserData && item.UserData.PlaybackPositionTicks && (startPosition = item.UserData.PlaybackPositionTicks), "Program" === item.Type ? playbackManager[method]({ + + var method = queue ? (queueNext ? 'queueNext' : 'queue') : 'play'; + + var startPosition = 0; + if (resume && item.UserData && item.UserData.PlaybackPositionTicks) { + startPosition = item.UserData.PlaybackPositionTicks; + } + + if (item.Type === 'Program') { + playbackManager[method]({ ids: [item.ChannelId], startPositionTicks: startPosition, serverId: item.ServerId - }) : playbackManager[method]({ + }); + } else { + playbackManager[method]({ items: [item], startPositionTicks: startPosition - }) + }); + } } function editItem(apiClient, item) { - return new Promise(function(resolve, reject) { + + return new Promise(function (resolve, reject) { + var serverId = apiClient.serverInfo().Id; - "Timer" === item.Type ? require(["recordingEditor"], function(recordingEditor) { - recordingEditor.show(item.Id, serverId).then(resolve, reject) - }) : "SeriesTimer" === item.Type ? require(["seriesRecordingEditor"], 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') { + require(['recordingEditor'], function (recordingEditor) { + + recordingEditor.show(item.Id, serverId).then(resolve, reject); + }); + } else if (item.Type === 'SeriesTimer') { + require(['seriesRecordingEditor'], function (recordingEditor) { + + recordingEditor.show(item.Id, serverId).then(resolve, reject); + }); + } else { + require(['metadataEditor'], function (metadataEditor) { + + metadataEditor.show(item.Id, serverId).then(resolve, reject); + }); + } + }); } function deleteItem(apiClient, item) { - return new Promise(function(resolve, reject) { - require(["deleteHelper"], function(deleteHelper) { + + return new Promise(function (resolve, reject) { + + require(['deleteHelper'], function (deleteHelper) { + deleteHelper.deleteItem({ + item: item, - navigate: !1 - }).then(function() { - resolve(!0) - }, reject) - }) - }) + navigate: false + + }).then(function () { + + resolve(true); + + }, reject); + + }); + }); } function refresh(apiClient, item) { - require(["refreshDialog"], function(refreshDialog) { + + require(['refreshDialog'], function (refreshDialog) { new refreshDialog({ itemIds: [item.Id], serverId: apiClient.serverInfo().Id, - mode: "CollectionFolder" === item.Type ? "scan" : null - }).show() - }) + mode: item.Type === 'CollectionFolder' ? 'scan' : null + }).show(); + }); } function show(options) { + var commands = getCommands(options); - return commands.length ? actionsheet.show({ + + if (!commands.length) { + return Promise.reject(); + } + + return actionsheet.show({ + items: commands, positionTo: options.positionTo, - resolveOnClick: ["share"] - }).then(function(id) { - return executeCommand(options.item, id, options) - }) : Promise.reject() + + resolveOnClick: ['share'] + + }).then(function (id) { + return executeCommand(options.item, id, options); + }); } + return { getCommands: getCommands, show: show - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/itemhelper.js b/src/bower_components/emby-webcomponents/itemhelper.js index 4dc99ca3c6..6d22fd4d2b 100644 --- a/src/bower_components/emby-webcomponents/itemhelper.js +++ b/src/bower_components/emby-webcomponents/itemhelper.js @@ -1,92 +1,366 @@ -define(["apphost", "globalize"], function(appHost, globalize) { - "use strict"; +define(['apphost', 'globalize'], function (appHost, globalize) { + 'use strict'; function getDisplayName(item, options) { - if (!item) throw new Error("null item passed into getDisplayName"); - options = options || {}, "Timer" === item.Type && (item = item.ProgramInfo || item); - var name = ("Program" !== item.Type && "Recording" !== item.Type || !item.IsSeries && !item.EpisodeTitle ? item.Name : item.EpisodeTitle) || ""; - if ("TvChannel" === item.Type) return item.ChannelNumber ? item.ChannelNumber + " " + name : name; - if ("Episode" === item.Type && 0 === item.ParentIndexNumber) name = globalize.translate("sharedcomponents#ValueSpecialEpisodeName", name); - else if (("Episode" === item.Type || "Program" === item.Type) && null != item.IndexNumber && null != item.ParentIndexNumber && !1 !== options.includeIndexNumber) { - var displayIndexNumber = item.IndexNumber, - number = displayIndexNumber, - nameSeparator = " - "; - !1 !== options.includeParentInfo ? number = "S" + item.ParentIndexNumber + ":E" + number : nameSeparator = ". ", item.IndexNumberEnd && (displayIndexNumber = item.IndexNumberEnd, number += "-" + displayIndexNumber), number && (name = name ? number + nameSeparator + name : number) + + if (!item) { + throw new Error("null item passed into getDisplayName"); } - return name + + options = options || {}; + + if (item.Type === 'Timer') { + item = item.ProgramInfo || item; + } + + var name = ((item.Type === 'Program' || item.Type === 'Recording') && (item.IsSeries || item.EpisodeTitle) ? item.EpisodeTitle : item.Name) || ''; + + if (item.Type === "TvChannel") { + + if (item.ChannelNumber) { + return item.ChannelNumber + ' ' + name; + } + return name; + } + if (/*options.isInlineSpecial &&*/ item.Type === "Episode" && item.ParentIndexNumber === 0) { + + name = globalize.translate('sharedcomponents#ValueSpecialEpisodeName', name); + + } else if ((item.Type === "Episode" || item.Type === 'Program') && item.IndexNumber != null && item.ParentIndexNumber != null && options.includeIndexNumber !== false) { + + var displayIndexNumber = item.IndexNumber; + + var number = displayIndexNumber; + var nameSeparator = " - "; + + if (options.includeParentInfo !== false) { + number = "S" + item.ParentIndexNumber + ":E" + number; + } else { + nameSeparator = ". "; + } + + if (item.IndexNumberEnd) { + + displayIndexNumber = item.IndexNumberEnd; + number += "-" + displayIndexNumber; + } + + if (number) { + name = name ? (number + nameSeparator + name) : number; + } + } + + return name; } function supportsAddingToCollection(item) { - var invalidTypes = ["Genre", "MusicGenre", "Studio", "GameGenre", "UserView", "CollectionFolder", "Audio", "Program", "Timer", "SeriesTimer"]; - return ("Recording" !== item.Type || "Completed" === item.Status) && (!item.CollectionType && -1 === invalidTypes.indexOf(item.Type) && "Photo" !== item.MediaType && !isLocalItem(item)) + + var invalidTypes = ['Genre', 'MusicGenre', 'Studio', 'GameGenre', 'UserView', 'CollectionFolder', 'Audio', 'Program', 'Timer', 'SeriesTimer']; + + if (item.Type === 'Recording') { + if (item.Status !== 'Completed') { + return false; + } + } + + return !item.CollectionType && invalidTypes.indexOf(item.Type) === -1 && item.MediaType !== 'Photo' && !isLocalItem(item); } function supportsAddingToPlaylist(item) { - return "Program" !== item.Type && ("TvChannel" !== item.Type && ("Timer" !== item.Type && ("SeriesTimer" !== item.Type && ("Photo" !== item.MediaType && (("Recording" !== item.Type || "Completed" === item.Status) && (!isLocalItem(item) && ("livetv" !== item.CollectionType && (item.MediaType || item.IsFolder || "Genre" === item.Type || "MusicGenre" === item.Type || "MusicArtist" === item.Type)))))))) + + if (item.Type === 'Program') { + return false; + } + if (item.Type === 'TvChannel') { + return false; + } + if (item.Type === 'Timer') { + return false; + } + if (item.Type === 'SeriesTimer') { + return false; + } + if (item.MediaType === 'Photo') { + return false; + } + + if (item.Type === 'Recording') { + if (item.Status !== 'Completed') { + return false; + } + } + + if (isLocalItem(item)) { + return false; + } + if (item.CollectionType === 'livetv') { + return false; + } + + return item.MediaType || item.IsFolder || item.Type === "Genre" || item.Type === "MusicGenre" || item.Type === "MusicArtist"; } function canEdit(user, item) { + var itemType = item.Type; - return "UserRootFolder" !== itemType && "UserView" !== itemType && ("Program" !== itemType && ("Timer" !== itemType && ("SeriesTimer" !== itemType && (("Recording" !== item.Type || "Completed" === item.Status) && (!isLocalItem(item) && user.Policy.IsAdministrator))))) + + if (itemType === "UserRootFolder" || /*itemType == "CollectionFolder" ||*/ itemType === "UserView") { + return false; + } + + if (itemType === 'Program') { + return false; + } + + if (itemType === 'Timer') { + return false; + } + + if (itemType === 'SeriesTimer') { + return false; + } + + if (item.Type === 'Recording') { + if (item.Status !== 'Completed') { + return false; + } + } + + if (isLocalItem(item)) { + return false; + } + + return user.Policy.IsAdministrator; } function isLocalItem(item) { - return !(!item || !item.Id || 0 !== item.Id.indexOf("local")) + + if (item && item.Id && item.Id.indexOf('local') === 0) { + return true; + } + + return false; } + return { getDisplayName: getDisplayName, supportsAddingToCollection: supportsAddingToCollection, supportsAddingToPlaylist: supportsAddingToPlaylist, isLocalItem: isLocalItem, - canIdentify: function(user, item) { + + canIdentify: function (user, item) { + var itemType = item.Type; - return !("Movie" !== itemType && "Trailer" !== itemType && "Series" !== itemType && "Game" !== itemType && "BoxSet" !== itemType && "Person" !== itemType && "Book" !== itemType && "MusicAlbum" !== itemType && "MusicArtist" !== itemType && "MusicVideo" !== itemType || !user.Policy.IsAdministrator || isLocalItem(item)) + + if (itemType === "Movie" || + itemType === "Trailer" || + itemType === "Series" || + itemType === "Game" || + itemType === "BoxSet" || + itemType === "Person" || + itemType === "Book" || + itemType === "MusicAlbum" || + itemType === "MusicArtist" || + itemType === "MusicVideo") { + + if (user.Policy.IsAdministrator) { + + if (!isLocalItem(item)) { + return true; + } + } + } + + return false; }, + canEdit: canEdit, - canEditImages: function(user, item) { + + canEditImages: function (user, item) { + var itemType = item.Type; - return "Photo" !== item.MediaType && ("UserView" === itemType ? !!user.Policy.IsAdministrator : ("Recording" !== item.Type || "Completed" === item.Status) && ("Timer" !== itemType && "SeriesTimer" !== itemType && canEdit(user, item) && !isLocalItem(item))) - }, - canSync: function(user, item) { - return !(user && !user.Policy.EnableContentDownloading) && (!isLocalItem(item) && item.SupportsSync) - }, - canShare: function(item, user) { - return "Program" !== item.Type && ("TvChannel" !== item.Type && ("Timer" !== item.Type && ("SeriesTimer" !== item.Type && (("Recording" !== item.Type || "Completed" === item.Status) && (!isLocalItem(item) && (user.Policy.EnablePublicSharing && appHost.supports("sharing"))))))) - }, - enableDateAddedDisplay: function(item) { - return !item.IsFolder && item.MediaType && "Program" !== item.Type && "TvChannel" !== item.Type && "Trailer" !== item.Type - }, - canMarkPlayed: function(item) { - if ("Program" === item.Type) return !1; - if ("Video" === item.MediaType) { - if ("TvChannel" !== item.Type) return !0 - } else if ("Audio" === item.MediaType) { - if ("AudioPodcast" === item.Type) return !0; - if ("AudioBook" === item.Type) return !0 + + if (item.MediaType === 'Photo') { + return false; } - return "Series" === item.Type || "Season" === item.Type || "BoxSet" === item.Type || "Game" === item.MediaType || "Book" === item.MediaType || "Recording" === item.MediaType + + if (itemType === 'UserView') { + if (user.Policy.IsAdministrator) { + + return true; + } + + return false; + } + + if (item.Type === 'Recording') { + if (item.Status !== 'Completed') { + return false; + } + } + + return itemType !== 'Timer' && itemType !== 'SeriesTimer' && canEdit(user, item) && !isLocalItem(item); }, - canRate: function(item) { - return "Program" !== item.Type && "Timer" !== item.Type && "SeriesTimer" !== item.Type && "CollectionFolder" !== item.Type && "UserView" !== item.Type && "Channel" !== item.Type && "Season" !== item.Type && "Studio" !== item.Type && !!item.UserData + + canSync: function (user, item) { + + if (user && !user.Policy.EnableContentDownloading) { + return false; + } + + if (isLocalItem(item)) { + return false; + } + + return item.SupportsSync; }, - canConvert: function(item, user) { - if (!user.Policy.EnableMediaConversion) return !1; - if (isLocalItem(item)) return !1; + + canShare: function (item, user) { + + if (item.Type === 'Program') { + return false; + } + if (item.Type === 'TvChannel') { + return false; + } + if (item.Type === 'Timer') { + return false; + } + if (item.Type === 'SeriesTimer') { + return false; + } + if (item.Type === 'Recording') { + if (item.Status !== 'Completed') { + return false; + } + } + if (isLocalItem(item)) { + return false; + } + return user.Policy.EnablePublicSharing && appHost.supports('sharing'); + }, + + enableDateAddedDisplay: function (item) { + return !item.IsFolder && item.MediaType && item.Type !== 'Program' && item.Type !== 'TvChannel' && item.Type !== 'Trailer'; + }, + + canMarkPlayed: function (item) { + + if (item.Type === 'Program') { + return false; + } + + if (item.MediaType === 'Video') { + if (item.Type !== 'TvChannel') { + return true; + } + } + + else if (item.MediaType === 'Audio') { + if (item.Type === 'AudioPodcast') { + return true; + } + if (item.Type === 'AudioBook') { + return true; + } + } + + if (item.Type === "Series" || + item.Type === "Season" || + item.Type === "BoxSet" || + item.MediaType === "Game" || + item.MediaType === "Book" || + item.MediaType === "Recording") { + return true; + } + + return false; + }, + + canRate: function (item) { + + if (item.Type === 'Program' || item.Type === 'Timer' || item.Type === 'SeriesTimer' || item.Type === 'CollectionFolder' || item.Type === 'UserView' || item.Type === 'Channel') { + return false; + } + + return true; + }, + + canConvert: function (item, user) { + + if (!user.Policy.EnableMediaConversion) { + return false; + } + + if (isLocalItem(item)) { + return false; + } + var mediaType = item.MediaType; - if ("Book" === mediaType || "Photo" === mediaType || "Game" === mediaType || "Audio" === mediaType) return !1; - if ("livetv" === item.CollectionType) return !1; - var type = item.Type; - return "Channel" !== type && "Person" !== type && "Year" !== type && "Program" !== type && "Timer" !== type && "SeriesTimer" !== type && (!("Virtual" === item.LocationType && !item.IsFolder) && !item.IsPlaceHolder) - }, - canRefreshMetadata: function(item, user) { - if (user.Policy.IsAdministrator) { - if ("livetv" === item.CollectionType) return !1; - if ("Timer" !== item.Type && "SeriesTimer" !== item.Type && "Program" !== item.Type && "TvChannel" !== item.Type && ("Recording" !== item.Type || "Completed" === item.Status) && !isLocalItem(item)) return !0 + if (mediaType === 'Book' || mediaType === 'Photo' || mediaType === 'Game' || mediaType === 'Audio') { + return false; } - return !1 + + var collectionType = item.CollectionType; + if (collectionType === 'livetv') { + return false; + } + + var type = item.Type; + if (type === 'Channel' || type === 'Person' || type === 'Year' || type === 'Program' || type === 'Timer' || type === 'SeriesTimer') { + return false; + } + + if (item.LocationType === 'Virtual' && !item.IsFolder) { + return false; + } + + if (item.IsPlaceHolder) { + return false; + } + + return true; }, - supportsMediaSourceSelection: function(item) { - return "Video" === item.MediaType && ("TvChannel" !== item.Type && (!(!item.MediaSources || 1 === item.MediaSources.length && "Placeholder" === item.MediaSources[0].Type) && (!1 !== item.EnableMediaSourceDisplay && (null != item.EnableMediaSourceDisplay || !item.SourceType || "Library" === item.SourceType)))) + + canRefreshMetadata: function (item, user) { + + if (user.Policy.IsAdministrator) { + + var collectionType = item.CollectionType; + if (collectionType === 'livetv') { + return false; + } + + if (item.Type !== 'Timer' && item.Type !== 'SeriesTimer' && item.Type !== 'Program' && item.Type !== 'TvChannel' && !(item.Type === 'Recording' && item.Status !== 'Completed')) { + + if (!isLocalItem(item)) { + return true; + } + } + } + + return false; + }, + + supportsMediaSourceSelection: function (item) { + + if (item.MediaType !== 'Video') { + return false; + } + if (item.Type === 'TvChannel') { + return false; + } + if (!item.MediaSources || (item.MediaSources.length === 1 && item.MediaSources[0].Type === 'Placeholder')) { + return false; + } + if (item.EnableMediaSourceDisplay === false) { + return false; + } + if (item.EnableMediaSourceDisplay == null && item.SourceType && item.SourceType !== 'Library') { + return false; + } + + return true; } - } + }; }); \ No newline at end of file diff --git a/src/bower_components/emby-webcomponents/itemidentifier/itemidentifier.js b/src/bower_components/emby-webcomponents/itemidentifier/itemidentifier.js index 3024bee3d1..0107ca4097 100644 --- a/src/bower_components/emby-webcomponents/itemidentifier/itemidentifier.js +++ b/src/bower_components/emby-webcomponents/itemidentifier/itemidentifier.js @@ -1,192 +1,528 @@ -define(["dialogHelper", "loading", "connectionManager", "require", "globalize", "scrollHelper", "layoutManager", "focusManager", "browser", "emby-input", "emby-checkbox", "paper-icon-button-light", "css!./../formdialog", "material-icons", "cardStyle"], function(dialogHelper, loading, connectionManager, require, globalize, scrollHelper, layoutManager, focusManager, browser) { - "use strict"; +define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize', 'scrollHelper', 'layoutManager', 'focusManager', 'browser', 'emby-input', 'emby-checkbox', 'paper-icon-button-light', 'css!./../formdialog', 'material-icons', 'cardStyle'], function (dialogHelper, loading, connectionManager, require, globalize, scrollHelper, layoutManager, focusManager, browser) { + 'use strict'; + + var currentItem; + var currentItemType; + var currentServerId; + var currentResolve; + var currentReject; + var hasChanges = false; + var currentSearchResult; function getApiClient() { - return connectionManager.getApiClient(currentServerId) + return connectionManager.getApiClient(currentServerId); } function searchForIdentificationResults(page) { - var i, length, value, lookupInfo = { - ProviderIds: {} - }, - identifyField = page.querySelectorAll(".identifyField"); - for (i = 0, length = identifyField.length; i < length; i++)(value = identifyField[i].value) && ("number" === identifyField[i].type && (value = parseInt(value)), lookupInfo[identifyField[i].getAttribute("data-lookup")] = value); - var hasId = !1, - txtLookupId = page.querySelectorAll(".txtLookupId"); - for (i = 0, length = txtLookupId.length; i < length; i++) value = txtLookupId[i].value, value && (hasId = !0), lookupInfo.ProviderIds[txtLookupId[i].getAttribute("data-providerkey")] = value; - if (!hasId && !lookupInfo.Name) return void require(["toast"], function(toast) { - toast(globalize.translate("sharedcomponents#PleaseEnterNameOrId")) - }); - currentItem && currentItem.GameSystem && (lookupInfo.GameSystem = currentItem.GameSystem), lookupInfo = { + + var lookupInfo = { + ProviderIds: {} + }; + + var i, length; + var identifyField = page.querySelectorAll('.identifyField'); + var value; + for (i = 0, length = identifyField.length; i < length; i++) { + + value = identifyField[i].value; + + if (value) { + + if (identifyField[i].type === 'number') { + value = parseInt(value); + } + + lookupInfo[identifyField[i].getAttribute('data-lookup')] = value; + } + } + + var hasId = false; + + var txtLookupId = page.querySelectorAll('.txtLookupId'); + for (i = 0, length = txtLookupId.length; i < length; i++) { + + value = txtLookupId[i].value; + + if (value) { + hasId = true; + } + lookupInfo.ProviderIds[txtLookupId[i].getAttribute('data-providerkey')] = value; + } + + if (!hasId && !lookupInfo.Name) { + require(['toast'], function (toast) { + toast(globalize.translate('sharedcomponents#PleaseEnterNameOrId')); + }); + return; + } + + if (currentItem && currentItem.GameSystem) { + lookupInfo.GameSystem = currentItem.GameSystem; + } + + lookupInfo = { SearchInfo: lookupInfo - }, currentItem && currentItem.Id ? lookupInfo.ItemId = currentItem.Id : lookupInfo.IncludeDisabledProviders = !0, loading.show(); + }; + + if (currentItem && currentItem.Id) { + lookupInfo.ItemId = currentItem.Id; + } + else { + lookupInfo.IncludeDisabledProviders = true; + } + + loading.show(); + var apiClient = getApiClient(); + apiClient.ajax({ type: "POST", url: apiClient.getUrl("Items/RemoteSearch/" + currentItemType), data: JSON.stringify(lookupInfo), contentType: "application/json", - dataType: "json" - }).then(function(results) { - loading.hide(), showIdentificationSearchResults(page, results) - }) + dataType: 'json' + + }).then(function (results) { + + loading.hide(); + showIdentificationSearchResults(page, results); + }); } function showIdentificationSearchResults(page, results) { - function onSearchImageClick() { - var index = parseInt(this.getAttribute("data-index")), - currentResult = results[index]; - null != currentItem ? showIdentifyOptions(page, currentResult) : finishFindNewDialog(page, currentResult) - } - var identificationSearchResults = page.querySelector(".identificationSearchResults"); - page.querySelector(".popupIdentifyForm").classList.add("hide"), identificationSearchResults.classList.remove("hide"), page.querySelector(".identifyOptionsForm").classList.add("hide"), page.querySelector(".dialogContentInner").classList.remove("dialog-content-centered"); - var i, length, html = ""; + + var identificationSearchResults = page.querySelector('.identificationSearchResults'); + + page.querySelector('.popupIdentifyForm').classList.add('hide'); + identificationSearchResults.classList.remove('hide'); + page.querySelector('.identifyOptionsForm').classList.add('hide'); + page.querySelector('.dialogContentInner').classList.remove('dialog-content-centered'); + + var html = ''; + var i, length; for (i = 0, length = results.length; i < length; i++) { - html += getSearchResultHtml(results[i], i) + + var result = results[i]; + html += getSearchResultHtml(result, i); } - var elem = page.querySelector(".identificationSearchResultList"); + + var elem = page.querySelector('.identificationSearchResultList'); elem.innerHTML = html; - var searchImages = elem.querySelectorAll(".card"); - for (i = 0, length = searchImages.length; i < length; i++) searchImages[i].addEventListener("click", onSearchImageClick); - layoutManager.tv && focusManager.autoFocus(identificationSearchResults) + + function onSearchImageClick() { + var index = parseInt(this.getAttribute('data-index')); + + var currentResult = results[index]; + + if (currentItem != null) { + + showIdentifyOptions(page, currentResult); + } else { + + finishFindNewDialog(page, currentResult); + } + } + + var searchImages = elem.querySelectorAll('.card'); + for (i = 0, length = searchImages.length; i < length; i++) { + + searchImages[i].addEventListener('click', onSearchImageClick); + } + + if (layoutManager.tv) { + focusManager.autoFocus(identificationSearchResults); + } } function finishFindNewDialog(dlg, identifyResult) { - currentSearchResult = identifyResult, hasChanges = !0, loading.hide(), dialogHelper.close(dlg) + currentSearchResult = identifyResult; + hasChanges = true; + loading.hide(); + + dialogHelper.close(dlg); } function showIdentifyOptions(page, identifyResult) { - var identifyOptionsForm = page.querySelector(".identifyOptionsForm"); - page.querySelector(".popupIdentifyForm").classList.add("hide"), page.querySelector(".identificationSearchResults").classList.add("hide"), identifyOptionsForm.classList.remove("hide"), page.querySelector("#chkIdentifyReplaceImages").checked = !0, page.querySelector(".dialogContentInner").classList.add("dialog-content-centered"), currentSearchResult = identifyResult; + + var identifyOptionsForm = page.querySelector('.identifyOptionsForm'); + + page.querySelector('.popupIdentifyForm').classList.add('hide'); + page.querySelector('.identificationSearchResults').classList.add('hide'); + identifyOptionsForm.classList.remove('hide'); + page.querySelector('#chkIdentifyReplaceImages').checked = true; + page.querySelector('.dialogContentInner').classList.add('dialog-content-centered'); + + currentSearchResult = identifyResult; + var lines = []; - lines.push(identifyResult.Name), identifyResult.ProductionYear && lines.push(identifyResult.ProductionYear), identifyResult.GameSystem && lines.push(identifyResult.GameSystem); - var resultHtml = lines.join("
"); - if (identifyResult.ImageUrl) { - resultHtml = '
' + resultHtml + "
" + lines.push(identifyResult.Name); + + if (identifyResult.ProductionYear) { + lines.push(identifyResult.ProductionYear); } - page.querySelector(".selectedSearchResult").innerHTML = resultHtml, focusManager.focus(identifyOptionsForm.querySelector(".btnSubmit")) + + if (identifyResult.GameSystem) { + lines.push(identifyResult.GameSystem); + } + + var resultHtml = lines.join('
'); + + if (identifyResult.ImageUrl) { + var displayUrl = getSearchImageDisplayUrl(identifyResult.ImageUrl, identifyResult.SearchProviderName); + + resultHtml = '
' + resultHtml + '
'; + } + + page.querySelector('.selectedSearchResult').innerHTML = resultHtml; + + focusManager.focus(identifyOptionsForm.querySelector('.btnSubmit')); } function getSearchResultHtml(result, index) { - var padderClass, html = "", - cssClass = "card scalableCard", - cardBoxCssClass = "cardBox"; - if ("Episode" === currentItemType ? (cssClass += " backdropCard backdropCard-scalable", padderClass = "cardPadder-backdrop") : "MusicAlbum" === currentItemType || "MusicArtist" === currentItemType ? (cssClass += " squareCard squareCard-scalable", padderClass = "cardPadder-square") : (cssClass += " portraitCard portraitCard-scalable", padderClass = "cardPadder-portrait"), layoutManager.tv && !browser.slow && (cardBoxCssClass += " cardBox-focustransform"), cardBoxCssClass += " cardBox-bottompadded", layoutManager.tv && (cardBoxCssClass += " card-focuscontent cardBox-withfocuscontent"), html += '" + + if (result.AlbumArtist) { + lines.push(result.AlbumArtist.Name); + } + if (result.ProductionYear) { + lines.push(result.ProductionYear); + } + if (result.GameSystem) { + lines.push(result.GameSystem); + } + + for (var i = 0; i < numLines; i++) { + + if (i === 0) { + html += '
'; + } else { + html += '
'; + } + html += lines[i] || ' '; + html += '
'; + } + + html += '
'; + html += ''; + return html; } function getSearchImageDisplayUrl(url, provider) { - return getApiClient().getUrl("Items/RemoteSearch/Image", { - imageUrl: url, - ProviderName: provider - }) + var apiClient = getApiClient(); + + return apiClient.getUrl("Items/RemoteSearch/Image", { imageUrl: url, ProviderName: provider }); } function submitIdentficationResult(page) { + loading.show(); + var options = { - ReplaceAllImages: page.querySelector("#chkIdentifyReplaceImages").checked - }, - apiClient = getApiClient(); + ReplaceAllImages: page.querySelector('#chkIdentifyReplaceImages').checked + }; + + var apiClient = getApiClient(); + apiClient.ajax({ type: "POST", url: apiClient.getUrl("Items/RemoteSearch/Apply/" + currentItem.Id, options), data: JSON.stringify(currentSearchResult), contentType: "application/json" - }).then(function() { - hasChanges = !0, loading.hide(), dialogHelper.close(page) - }, function() { - loading.hide(), dialogHelper.close(page) - }) + + }).then(function () { + + hasChanges = true; + loading.hide(); + + dialogHelper.close(page); + + }, function () { + + loading.hide(); + + dialogHelper.close(page); + }); } function showIdentificationForm(page, item) { + var apiClient = getApiClient(); - apiClient.getJSON(apiClient.getUrl("Items/" + item.Id + "/ExternalIdInfos")).then(function(idList) { - for (var html = "", providerIds = item.ProviderIds || {}, i = 0, length = idList.length; i < length; i++) { - var idInfo = idList[i], - id = "txtLookup" + idInfo.Key; + + apiClient.getJSON(apiClient.getUrl("Items/" + item.Id + "/ExternalIdInfos")).then(function (idList) { + + var html = ''; + + var providerIds = item.ProviderIds || {}; + + for (var i = 0, length = idList.length; i < length; i++) { + + var idInfo = idList[i]; + + var id = "txtLookup" + idInfo.Key; + html += '
'; - var idLabel = globalize.translate("sharedcomponents#LabelDynamicExternalId").replace("{0}", idInfo.Name); - idInfo.Key; - html += '', 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..eccbeadacd 100644 --- a/src/bower_components/emby-webcomponents/registrationservices/registrationservices.js +++ b/src/bower_components/emby-webcomponents/registrationservices/registrationservices.js @@ -1,301 +1,733 @@ -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"; +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'; + + var currentDisplayingProductInfos = []; + var currentDisplayingResolve = null; + var currentValidatingFeature = null; + var isCurrentDialogRejected = null; function alertText(options) { - return new Promise(function(resolve, reject) { - require(["alert"], function(alert) { - alert(options).then(resolve, reject) - }) - }) + 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 - }) - }) + + 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() { + + 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 + size: layoutManager.tv ? 'fullscreen' : 'fullscreen-border', + removeOnClose: true, + scrollY: false }); - dlg.classList.add("formDialog"); - var html = ""; + + 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) - }) - }) + + html += '
' + globalize.translate('sharedcomponents#ContinueInSecondsValue', seconds) + '
'; + + html += ''; + + html += '
'; + + html += '
'; + html += '
'; + + dlg.innerHTML = html; + + var isRejected = true; + + var timeTextInterval = setInterval(function () { + + seconds -= 1; + if (seconds <= 0) { + dlg.querySelector('.continueTimeText').classList.add('hide'); + dlg.querySelector('.btnContinue').classList.remove('hide'); + } else { + dlg.querySelector('.continueTimeText').innerHTML = globalize.translate('sharedcomponents#ContinueInSecondsValue', seconds); + } + + }, 1000); + + var i, length; + var btnPurchases = dlg.querySelectorAll('.buttonPremiereInfo'); + for (i = 0, length = btnPurchases.length; i < length; i++) { + btnPurchases[i].addEventListener('click', showExternalPremiereInfo); + } + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, true); + } + + // Has to be assigned a z-index after the call to .open() + dlg.addEventListener('close', function (e) { + + clearInterval(timeTextInterval); + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, false); + } + + if (isRejected) { + reject(); + } else { + appSettings.set(settingsKey, new Date().getTime()); + + resolve(); + } + }); + + dlg.querySelector('.btnContinue').addEventListener('click', function () { + isRejected = false; + dialogHelper.close(dlg); + }); + + dlg.querySelector('.btnGetPremiere').addEventListener('click', showPremiereInfo); + + dialogHelper.open(dlg); + + var onCancelClick = function () { + dialogHelper.close(dlg); + }; + var 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) - }) + + if (feature !== 'playback') { + return Promise.resolve(); } - return Promise.resolve() + + var intervalMs = iapManager.getPeriodicMessageIntervalMs(feature); + if (intervalMs <= 0) { + return Promise.resolve(); + } + + var settingsKey = 'periodicmessage11-' + feature; + + var lastMessage = parseInt(appSettings.get(settingsKey) || '0'); + + if (!lastMessage) { + + // Don't show on the very first playback attempt + appSettings.set(settingsKey, new Date().getTime()); + return Promise.resolve(); + } + + if ((new Date().getTime() - lastMessage) > intervalMs) { + + var apiClient = connectionManager.currentApiClient(); + if (apiClient.serverId() === '6da60dd6edfc4508bca2c434d4400816') { + return Promise.resolve(); + } + + var registrationOptions = { + viewOnly: true + }; + + // Get supporter status + return connectionManager.getRegistrationInfo(iapManager.getAdminFeatureName(feature), apiClient, registrationOptions).catch(function (errorResult) { + + if (errorResult === 'overlimit') { + appSettings.set(settingsKey, new Date().getTime()); + return Promise.resolve(); + } + + return showPeriodicMessage(feature, settingsKey); + }); + } + + return Promise.resolve(); } 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); + + options = options || {}; + + console.log('validateFeature: ' + feature); + + return iapManager.isUnlockedByDefault(feature, options).then(function () { + + return showPeriodicMessageIfNeeded(feature); + + }, function () { + + var unlockableFeatureCacheKey = 'featurepurchased-' + feature; + if (appSettings.get(unlockableFeatureCacheKey) === '1') { + 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 unlockableCacheKey = 'productpurchased-' + unlockableProduct.id; + if (unlockableProduct.owned) { + + // Cache this to eliminate the store as a possible point of failure in the future + appSettings.set(unlockableFeatureCacheKey, '1'); + appSettings.set(unlockableCacheKey, '1'); + return showPeriodicMessageIfNeeded(feature); + } + + if (appSettings.get(unlockableCacheKey) === '1') { + return showPeriodicMessageIfNeeded(feature); + } } + var unlockableProductInfo = unlockableProduct ? { - enableAppUnlock: !0, + enableAppUnlock: true, 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(); + + 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(); + + // Get supporter status + return connectionManager.getRegistrationInfo(iapManager.getAdminFeatureName(feature), connectionManager.currentApiClient(), registrationOptions).catch(function (errorResult) { + + if (options.showDialog === false) { + return Promise.reject(); + } + var alertPromise; - return "overlimit" === errorResult && (alertPromise = showOverLimitAlert()), alertPromise || (alertPromise = Promise.resolve()), alertPromise.then(function() { + + if (errorResult === 'overlimit') { + alertPromise = showOverLimitAlert(); + } + + if (!alertPromise) { + alertPromise = Promise.resolve(); + } + + return alertPromise.then(function () { + var dialogOptions = { - title: globalize.translate("sharedcomponents#HeaderUnlockFeature"), + title: globalize.translate('sharedcomponents#HeaderUnlockFeature'), feature: feature }; - return currentValidatingFeature = feature, showInAppPurchaseInfo(subscriptionOptions, unlockableProductInfo, dialogOptions) - }) - }) - }) - }) + + currentValidatingFeature = feature; + + return 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() - }) + + return alertText('Your Jellyfin Premiere device limit has been exceeded. Please check with the owner of your Jellyfin Server and have them contact Emby support at apps@emby.media if necessary.').catch(function () { + return Promise.resolve(); + }); } function cancelInAppPurchase() { - var elem = document.querySelector(".inAppPurchaseOverlay"); - elem && dialogHelper.close(elem) + + var elem = document.querySelector('.inAppPurchaseOverlay'); + if (elem) { + dialogHelper.close(elem); + } } function clearCurrentDisplayingInfo() { - currentDisplayingProductInfos = [], currentDisplayingResolve = null, currentValidatingFeature = null, isCurrentDialogRejected = null + currentDisplayingProductInfos = []; + currentDisplayingResolve = null; + currentValidatingFeature = null; + isCurrentDialogRejected = null; } function showExternalPremiereInfo() { - shell.openUrl(iapManager.getPremiumInfoUrl()) + shell.openUrl(iapManager.getPremiumInfoUrl()); } 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 getPurchaseTermHtml(term) { - return "
  • " + 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 + + var html = ''; + + var termsOfPurchase = iapManager.getTermsOfPurchase ? iapManager.getTermsOfPurchase() : []; + + if (!termsOfPurchase.length) { + + return html; + } + + html += '

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

    '; + + termsOfPurchase.push('' + globalize.translate('sharedcomponents#PrivacyPolicy') + ''); + termsOfPurchase.push('' + globalize.translate('sharedcomponents#TermsOfUse') + ''); + + html += ''; + + return 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 += "

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

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

    " + currentDisplayingProductInfos.push(unlockableProductInfo); } - 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 dlg = dialogHelper.createDialog({ + size: layoutManager.tv ? 'fullscreen' : 'fullscreen-border', + removeOnClose: true, + scrollY: false + }); + + dlg.classList.add('formDialog'); + + var html = ''; + html += '
    '; + html += ''; + html += '

    '; + html += dialogOptions.title || ''; + html += '

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

    '; + + if (unlockableProductInfo) { + html += globalize.translate('sharedcomponents#MessageUnlockAppWithPurchaseOrSupporter'); + } + else { + html += globalize.translate('sharedcomponents#MessageUnlockAppWithSupporter'); + } + html += '

    '; + + html += '

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

    '; + + var hasProduct = false; + var i, length; + + for (i = 0, length = subscriptionOptions.length; i < length; i++) { + + hasProduct = true; + html += '

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

    '; + } + + if (unlockableProductInfo) { + + hasProduct = true; + var unlockText = globalize.translate('sharedcomponents#ButtonUnlockWithPurchase'); + if (unlockableProductInfo.price) { + unlockText = globalize.translate('sharedcomponents#ButtonUnlockPrice', unlockableProductInfo.price); + } + html += '

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

    '; + } + + html += '

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

    '; + + if (subscriptionOptions.length) { + html += '

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

    '; + + html += '
    '; + html += getSubscriptionBenefits().map(getSubscriptionBenefitHtml).join(''); + html += '
    '; + } + + if (dialogOptions.feature === 'playback') { + 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); + } + + btnPurchases = dlg.querySelectorAll('.buttonPremiereInfo'); + for (i = 0, length = btnPurchases.length; i < length; i++) { + btnPurchases[i].addEventListener('click', showExternalPremiereInfo); + } + + isCurrentDialogRejected = true; + var resolveWithTimeLimit = false; + + var btnPlayMinute = dlg.querySelector('.btnPlayMinute'); + if (btnPlayMinute) { + btnPlayMinute.addEventListener('click', function () { + + resolveWithTimeLimit = true; + isCurrentDialogRejected = false; + dialogHelper.close(dlg); + }); + } + + dlg.querySelector('.btnRestorePurchase').addEventListener('click', function () { + restorePurchase(unlockableProductInfo); + }); + + loading.hide(); + + function onCloseButtonClick() { + + dialogHelper.close(dlg); + } + + var btnCloseDialogs = dlg.querySelectorAll('.btnCloseDialog'); + for (i = 0, length = btnCloseDialogs.length; i < length; i++) { + btnCloseDialogs[i].addEventListener('click', onCloseButtonClick); + } + + dlg.classList.add('inAppPurchaseOverlay'); + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, true); + } + + dialogHelper.open(dlg).then(function () { + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, false); + } + var rejected = isCurrentDialogRejected; - clearCurrentDisplayingInfo(), rejected ? reject() : resolveWithTimeLimit && resolve({ - enableTimeLimit: !0 - }) - }) + clearCurrentDisplayingInfo(); + if (rejected) { + reject(); + } else if (resolveWithTimeLimit) { + resolve({ + enableTimeLimit: true + }); + } + }); } 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 + + list.push({ + name: globalize.translate('sharedcomponents#HeaderFreeApps'), + icon: '', + text: globalize.translate('sharedcomponents#FreeAppsFeatureDescription') + }); + + if (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') + }); + + return list; } function getSubscriptionBenefitHtml(item) { - var enableLink = appHost.supports("externalpremium"), - html = "", - cssClass = "listItem"; - return layoutManager.tv && (cssClass += " listItem-focusscale"), enableLink ? (cssClass += " listItem-button", html += '" : "
    " + + var enableLink = appHost.supports('externalpremium'); + + var html = ''; + + var cssClass = "listItem"; + + if (layoutManager.tv) { + cssClass += ' listItem-focusscale'; + } + + if (enableLink) { + cssClass += ' listItem-button'; + + html += ''; + } else { + html += ''; + } + + return 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) + + var featureId = this.getAttribute('data-featureid'); + + if (this.getAttribute('data-email') === 'true') { + getUserEmail().then(function (email) { + iapManager.beginPurchase(featureId, email); + }); + } else { + iapManager.beginPurchase(featureId); + } } function restorePurchase(unlockableProductInfo) { + var dlg = dialogHelper.createDialog({ - size: layoutManager.tv ? "fullscreen" : "fullscreen-border", - removeOnClose: !0, - scrollY: !1 + size: layoutManager.tv ? 'fullscreen' : 'fullscreen-border', + removeOnClose: true, + scrollY: false }); - 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" - }) + + 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 += '

    '; + + if (unlockableProductInfo) { + html += '

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

    '; + } + + html += '
    '; + html += '
    '; + + dlg.innerHTML = html; + document.body.appendChild(dlg); + + loading.hide(); + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, true); + } + + 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'); + if (btnRestoreUnlock) { + btnRestoreUnlock.addEventListener('click', function () { + + dialogHelper.close(dlg); + iapManager.restorePurchase(); + }); + } + + dialogHelper.open(dlg).then(function () { + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, false); + } }); - 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) + + if (connectUser && connectUser.Email) { + return Promise.resolve(connectUser.Email); + } } - return new Promise(function(resolve, reject) { - require(["prompt"], function(prompt) { + + return new Promise(function (resolve, reject) { + + require(['prompt'], function (prompt) { + prompt({ - label: globalize.translate("sharedcomponents#LabelEmailAddress") - }).then(resolve, reject) - }) - }) + + 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() + + if (resolve && currentDisplayingProductInfos.filter(function (p) { + + return product.id === p.id; + + }).length) { + + isCurrentDialogRejected = false; + cancelInAppPurchase(); + resolve(); + return; + } } + var feature = currentValidatingFeature; - feature && iapManager.isUnlockedByDefault(feature).then(function() { - isCurrentDialogRejected = !1, cancelInAppPurchase(), resolve() - }) + if (feature) { + iapManager.isUnlockedByDefault(feature).then(function () { + isCurrentDialogRejected = false; + cancelInAppPurchase(); + resolve(); + }); + } } function showPremiereInfo() { - return appHost.supports("externalpremium") ? (showExternalPremiereInfo(), Promise.resolve()) : iapManager.getSubscriptionOptions().then(function(subscriptionOptions) { - return showInAppPurchaseInfo(subscriptionOptions, null, { - title: "Jellyfin Premiere", - feature: "sync" - }) - }) + + if (appHost.supports('externalpremium')) { + showExternalPremiereInfo(); + return Promise.resolve(); + } + + return iapManager.getSubscriptionOptions().then(function (subscriptionOptions) { + + var dialogOptions = { + title: 'Jellyfin Premiere', + feature: 'sync' + }; + + return showInAppPurchaseInfo(subscriptionOptions, null, dialogOptions); + }); } - var currentDisplayingProductInfos = [], - currentDisplayingResolve = null, - currentValidatingFeature = null, - isCurrentDialogRejected = null; - return events.on(iapManager, "productupdated", onProductUpdated), { + + 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..3e86eeae05 100644 --- a/src/bower_components/emby-webcomponents/router.js +++ b/src/bower_components/emby-webcomponents/router.js @@ -1,416 +1,904 @@ -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'); + }, + 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(); + } + }; 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": - appRouter.showSelectServer(); + case 'ServerSelection': + { + appRouter.showSelectServer(); + } break; - case "ConnectSignIn": - appRouter.showWelcome(); + case 'ConnectSignIn': + { + appRouter.showWelcome(); + } + break; + 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(); + }); + }); + } + break; + default: break; - 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() - }) - }) } } 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')); + + if (connectionManager.isLoggedIntoConnect()) { + appRouter.showConnectLogin(); + } else { + 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); + + if (path.indexOf('/') !== 0 && path.indexOf('://') === -1) { + 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) - }) + 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 === 'downloads') { + return 'offline/offline.html'; + } + if (item === 'managedownloads') { + return 'offline/managedownloads.html'; + } + 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.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..182178359c 100644 --- a/src/bower_components/emby-webcomponents/shell.js +++ b/src/bower_components/emby-webcomponents/shell.js @@ -1,12 +1,15 @@ -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(); } - } + }; }); \ 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/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