Merge branch 'master' into return-of-the-scrollbar

This commit is contained in:
Dmitry Lyzo 2020-10-31 11:42:58 +03:00 committed by GitHub
commit 88b0d6d458
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
151 changed files with 4431 additions and 2598 deletions

View file

@ -6,29 +6,42 @@
-ms-user-select: none;
}
.osdPoster img,
.videoOsdBottom {
bottom: 0;
left: 0;
right: 0;
position: fixed;
background: linear-gradient(0deg, rgba(16, 16, 16, 0.75) 0%, rgba(16, 16, 16, 0) 100%);
padding-top: 7.5em;
padding-bottom: 1.75em;
display: flex;
flex-direction: row;
justify-content: center;
will-change: opacity;
transition: opacity 0.3s ease-out;
color: #fff;
user-select: none;
-webkit-touch-callout: none;
}
.osdHeader {
-webkit-transition: opacity 0.3s ease-out;
-o-transition: opacity 0.3s ease-out;
.skinHeader-withBackground.osdHeader {
transition: opacity 0.3s ease-out;
position: relative;
z-index: 1;
background: rgba(0, 0, 0, 0.7) !important;
-webkit-backdrop-filter: none !important;
backdrop-filter: none !important;
color: #eee !important;
background: linear-gradient(180deg, rgba(16, 16, 16, 0.75) 0%, rgba(16, 16, 16, 0) 100%);
backdrop-filter: none;
color: #eee;
height: 7.5em;
}
.osdHeader-hidden {
opacity: 0;
}
.osdHeader .headerTop {
max-height: 3.5em;
}
.osdHeader .headerButton:not(.headerBackButton):not(.headerCastButton):not(.headerSyncButton) {
display: none;
}
@ -86,34 +99,17 @@
opacity: 0.6;
}
.videoOsdBottom {
position: fixed;
background-color: rgba(0, 0, 0, 0.7);
padding: 1%;
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-webkit-flex-direction: row;
flex-direction: row;
will-change: opacity;
-webkit-transition: opacity 0.3s ease-out;
-o-transition: opacity 0.3s ease-out;
transition: opacity 0.3s ease-out;
color: #fff;
user-select: none;
-webkit-touch-callout: none;
}
.videoOsdBottom-hidden {
opacity: 0;
}
.osdControls {
-webkit-box-flex: 1;
-webkit-flex-grow: 1;
flex-grow: 1;
padding: 0 0.8em;
}
.layout-desktop .osdControls {
max-width: calc(100vh * 1.77 - 2vh);
}
.videoOsdBottom .buttons {
@ -145,7 +141,7 @@
}
.volumeButtons {
margin: 0 0.5em 0 auto;
margin: 0 1em 0 0.29em;
display: flex;
-webkit-align-items: center;
align-items: center;
@ -153,33 +149,13 @@
.osdTimeText {
margin-left: 1em;
margin-right: auto;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.osdPoster {
width: 10%;
position: relative;
margin-right: 0.5em;
}
.osdPoster img {
position: absolute;
height: auto;
width: 100%;
-webkit-box-shadow: 0 0 1.9vh #000;
box-shadow: 0 0 1.9vh #000;
border: 0.08em solid #222;
user-drag: none;
user-select: none;
-moz-user-select: none;
-webkit-user-drag: none;
-webkit-user-select: none;
-ms-user-select: none;
}
.osdTitle,
.osdTitleSmall {
margin: 0 1em 0 0;

View file

@ -90,17 +90,16 @@ _define('material-icons', function() {
return materialIcons;
});
// noto font
const noto = require('jellyfin-noto');
_define('jellyfin-noto', function () {
return noto;
});
const epubjs = require('epubjs');
_define('epubjs', function () {
return epubjs;
});
const pdfjs = require('pdfjs-dist/build/pdf');
_define('pdfjs', function () {
return pdfjs;
});
// page.js
const page = require('page');
_define('page', function() {
@ -177,7 +176,7 @@ _define('appStorage', function () {
});
// libarchive.js
var libarchive = require('libarchive.js');
const libarchive = require('libarchive.js');
_define('libarchive', function () {
return libarchive;
});

View file

@ -27,7 +27,7 @@ class appFooter {
};
}
destroy() {
var self = this;
const self = this;
self.element = null;
}

View file

@ -78,7 +78,7 @@ function getDeviceId() {
}
function getDeviceName() {
var deviceName;
let deviceName;
if (browser.tizen) {
deviceName = 'Samsung Smart TV';
} else if (browser.web0s) {
@ -172,7 +172,6 @@ function supportsCue() {
function onAppVisible() {
if (isHidden) {
isHidden = false;
console.debug('triggering app resume event');
events.trigger(appHost, 'resume');
}
}
@ -180,7 +179,6 @@ function onAppVisible() {
function onAppHidden() {
if (!isHidden) {
isHidden = true;
console.debug('app is hidden');
}
}

View file

@ -23,7 +23,7 @@ import 'emby-button';
}).join('');
// get default theme
var defaultTheme = themes.find(theme => {
const defaultTheme = themes.find(theme => {
return theme.default;
});

View file

@ -19,7 +19,7 @@ function onSubmit(e) {
return false;
}
function renderOptions(context, selector, cssClass, items, isCheckedFn) {
var elem = context.querySelector(selector);
const elem = context.querySelector(selector);
if (items.length) {
elem.classList.remove('hide');
@ -27,12 +27,12 @@ function renderOptions(context, selector, cssClass, items, isCheckedFn) {
elem.classList.add('hide');
}
var html = '';
let html = '';
html += items.map(function (filter) {
var itemHtml = '';
let itemHtml = '';
var checkedHtml = isCheckedFn(filter) ? ' checked' : '';
const checkedHtml = isCheckedFn(filter) ? ' checked' : '';
itemHtml += '<label>';
itemHtml += '<input is="emby-checkbox" type="checkbox"' + checkedHtml + ' data-filter="' + filter.Id + '" class="' + cssClass + '"/>';
itemHtml += '<span>' + filter.Name + '</span>';
@ -47,21 +47,21 @@ function renderOptions(context, selector, cssClass, items, isCheckedFn) {
function renderDynamicFilters(context, result, options) {
renderOptions(context, '.genreFilters', 'chkGenreFilter', result.Genres, function (i) {
// Switching from | to ,
var delimeter = (options.settings.GenreIds || '').indexOf('|') === -1 ? ',' : '|';
const delimeter = (options.settings.GenreIds || '').indexOf('|') === -1 ? ',' : '|';
return (delimeter + (options.settings.GenreIds || '') + delimeter).indexOf(delimeter + i.Id + delimeter) !== -1;
});
}
function setBasicFilter(context, key, elem) {
var value = elem.checked;
let value = elem.checked;
value = value ? value : null;
userSettings.setFilter(key, value);
}
function moveCheckboxFocus(elem, offset) {
var parent = dom.parentWithClass(elem, 'checkboxList-verticalwrap');
var elems = focusManager.getFocusableElements(parent);
const parent = dom.parentWithClass(elem, 'checkboxList-verticalwrap');
const elems = focusManager.getFocusableElements(parent);
var index = -1;
let index = -1;
for (let i = 0, length = elems.length; i < length; i++) {
if (elems[i] === elem) {
index = i;
@ -74,14 +74,14 @@ function moveCheckboxFocus(elem, offset) {
index = Math.min(elems.length - 1, index);
index = Math.max(0, index);
var newElem = elems[index];
const newElem = elems[index];
if (newElem) {
focusManager.focus(newElem);
}
}
function centerFocus(elem, horiz, on) {
import('scrollHelper').then(({ default: scrollHelper }) => {
var fn = on ? 'on' : 'off';
const fn = on ? 'on' : 'off';
scrollHelper.centerFocus[fn](elem, horiz);
});
}
@ -100,7 +100,7 @@ function onInputCommand(e) {
}
}
function saveValues(context, settings, settingsKey) {
var elems = context.querySelectorAll('.simpleFilter');
let elems = context.querySelectorAll('.simpleFilter');
for (let i = 0, length = elems.length; i < length; i++) {
if (elems[i].tagName === 'INPUT') {
setBasicFilter(context, settingsKey + '-filter-' + elems[i].getAttribute('data-settingname'), elems[i]);
@ -110,7 +110,7 @@ function saveValues(context, settings, settingsKey) {
}
// Video type
var videoTypes = [];
const videoTypes = [];
elems = context.querySelectorAll('.chkVideoTypeFilter');
for (let i = 0, length = elems.length; i < length; i++) {
@ -121,7 +121,7 @@ function saveValues(context, settings, settingsKey) {
userSettings.setFilter(settingsKey + '-filter-VideoTypes', videoTypes.join(','));
// Series status
var seriesStatuses = [];
const seriesStatuses = [];
elems = context.querySelectorAll('.chkSeriesStatus');
for (let i = 0, length = elems.length; i < length; i++) {
@ -131,7 +131,7 @@ function saveValues(context, settings, settingsKey) {
}
// Genres
var genres = [];
const genres = [];
elems = context.querySelectorAll('.chkGenreFilter');
for (let i = 0, length = elems.length; i < length; i++) {
@ -142,7 +142,7 @@ function saveValues(context, settings, settingsKey) {
userSettings.setFilter(settingsKey + '-filter-GenreIds', genres.join(','));
}
function bindCheckboxInput(context, on) {
var elems = context.querySelectorAll('.checkboxList-verticalwrap');
const elems = context.querySelectorAll('.checkboxList-verticalwrap');
for (let i = 0, length = elems.length; i < length; i++) {
if (on) {
inputManager.on(elems[i], onInputCommand);
@ -154,9 +154,9 @@ function bindCheckboxInput(context, on) {
function initEditor(context, settings) {
context.querySelector('form').addEventListener('submit', onSubmit);
var elems = context.querySelectorAll('.simpleFilter');
var i;
var length;
let elems = context.querySelectorAll('.simpleFilter');
let i;
let length;
for (i = 0, length = elems.length; i < length; i++) {
if (elems[i].tagName === 'INPUT') {
@ -166,14 +166,14 @@ function initEditor(context, settings) {
}
}
var videoTypes = settings.VideoTypes ? settings.VideoTypes.split(',') : [];
const 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(',') : [];
const seriesStatuses = settings.SeriesStatus ? settings.SeriesStatus.split(',') : [];
elems = context.querySelectorAll('.chkSeriesStatus');
for (i = 0, length = elems.length; i < length; i++) {
@ -193,9 +193,9 @@ function initEditor(context, settings) {
}
}
function loadDynamicFilters(context, options) {
var apiClient = window.connectionManager.getApiClient(options.serverId);
const apiClient = window.connectionManager.getApiClient(options.serverId);
var filterMenuOptions = Object.assign(options.filterMenuOptions, {
const filterMenuOptions = Object.assign(options.filterMenuOptions, {
UserId: apiClient.getCurrentUserId(),
ParentId: options.parentId,
@ -210,7 +210,7 @@ class FilterMenu {
show(options) {
return new Promise( (resolve, reject) => {
import('text!./filtermenu.template.html').then(({ default: template }) => {
var dialogOptions = {
const dialogOptions = {
removeOnClose: true,
scrollY: false
};
@ -220,11 +220,11 @@ class FilterMenu {
dialogOptions.size = 'small';
}
var dlg = dialogHelper.createDialog(dialogOptions);
const dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog');
var html = '';
let html = '';
html += '<div class="formDialogHeader">';
html += '<button is="paper-icon-button-light" class="btnCancel hide-mouse-idle-tv" tabindex="-1"><span class="material-icons arrow_back"></span></button>';
@ -236,7 +236,7 @@ class FilterMenu {
dlg.innerHTML = globalize.translateHtml(html, 'core');
var settingElements = dlg.querySelectorAll('.viewSetting');
const settingElements = dlg.querySelectorAll('.viewSetting');
for (let i = 0, length = settingElements.length; i < length; i++) {
if (options.visibleSettings.indexOf(settingElements[i].getAttribute('data-settingname')) === -1) {
settingElements[i].classList.add('hide');
@ -257,7 +257,7 @@ class FilterMenu {
centerFocus(dlg.querySelector('.formDialogContent'), false, true);
}
var submitted;
let submitted;
dlg.querySelector('form').addEventListener('change', function () {
submitted = true;

View file

@ -3,7 +3,7 @@
import dom from 'dom';
import scrollManager from 'scrollManager';
var scopes = [];
const scopes = [];
function pushScope(elem) {
scopes.push(elem);
}
@ -15,7 +15,7 @@ import scrollManager from 'scrollManager';
}
function autoFocus(view, defaultToFirst, findAutoFocusElement) {
var element;
let element;
if (findAutoFocusElement !== false) {
element = view.querySelector('*[autofocus]');
if (element) {
@ -46,9 +46,9 @@ import scrollManager from 'scrollManager';
}
}
var focusableTagNames = ['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON', 'A'];
var focusableContainerTagNames = ['BODY', 'DIALOG'];
var focusableQuery = focusableTagNames.map(function (t) {
const focusableTagNames = ['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON', 'A'];
const focusableContainerTagNames = ['BODY', 'DIALOG'];
const focusableQuery = focusableTagNames.map(function (t) {
if (t === 'INPUT') {
t += ':not([type="range"]):not([type="file"])';
}
@ -69,7 +69,7 @@ import scrollManager from 'scrollManager';
function normalizeFocusable(elem, originalElement) {
if (elem) {
var tagName = elem.tagName;
const tagName = elem.tagName;
if (!tagName || tagName === 'HTML' || tagName === 'BODY') {
elem = originalElement;
}
@ -79,10 +79,10 @@ import scrollManager from 'scrollManager';
}
function focusableParent(elem) {
var originalElement = elem;
const originalElement = elem;
while (!isFocusable(elem)) {
var parent = elem.parentNode;
const parent = elem.parentNode;
if (!parent) {
return normalizeFocusable(elem, originalElement);
@ -115,7 +115,7 @@ import scrollManager from 'scrollManager';
}
if (elem.tagName === 'INPUT') {
var type = elem.type;
const type = elem.type;
if (type === 'range') {
return false;
}
@ -132,11 +132,11 @@ import scrollManager from 'scrollManager';
}
function getFocusableElements(parent, limit, excludeClass) {
var elems = (parent || getDefaultScope()).querySelectorAll(focusableQuery);
var focusableElements = [];
const elems = (parent || getDefaultScope()).querySelectorAll(focusableQuery);
const focusableElements = [];
for (var i = 0, length = elems.length; i < length; i++) {
var elem = elems[i];
for (let i = 0, length = elems.length; i < length; i++) {
const elem = elems[i];
if (excludeClass && elem.classList.contains(excludeClass)) {
continue;
@ -159,7 +159,7 @@ import scrollManager from 'scrollManager';
return true;
}
var classList = elem.classList;
const classList = elem.classList;
if (classList.contains('focuscontainer')) {
return true;
@ -208,7 +208,7 @@ import scrollManager from 'scrollManager';
}
function getOffset(elem) {
var box;
let box;
// Support: BlackBerry 5, iOS 3 (original iPhone)
// If we don't have gBCR, just use 0,0 rather than error
@ -225,7 +225,7 @@ import scrollManager from 'scrollManager';
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 = {
const newBox = {
top: box.top,
left: box.left,
width: box.width,
@ -255,27 +255,27 @@ import scrollManager from 'scrollManager';
return;
}
var focusableContainer = dom.parentWithClass(activeElement, 'focusable');
const focusableContainer = dom.parentWithClass(activeElement, 'focusable');
var rect = getOffset(activeElement);
const rect = getOffset(activeElement);
// Get elements and work out x/y points
var point1x = parseFloat(rect.left) || 0;
var point1y = parseFloat(rect.top) || 0;
var point2x = parseFloat(point1x + rect.width - 1) || point1x;
var point2y = parseFloat(point1y + rect.height - 1) || point1y;
const point1x = parseFloat(rect.left) || 0;
const point1y = parseFloat(rect.top) || 0;
const point2x = parseFloat(point1x + rect.width - 1) || point1x;
const point2y = parseFloat(point1y + rect.height - 1) || point1y;
var sourceMidX = rect.left + (rect.width / 2);
var sourceMidY = rect.top + (rect.height / 2);
const sourceMidX = rect.left + (rect.width / 2);
const sourceMidY = rect.top + (rect.height / 2);
var focusable = focusableElements || container.querySelectorAll(focusableQuery);
const focusable = focusableElements || container.querySelectorAll(focusableQuery);
var maxDistance = Infinity;
var minDistance = maxDistance;
var nearestElement;
const maxDistance = Infinity;
let minDistance = maxDistance;
let nearestElement;
for (var i = 0, length = focusable.length; i < length; i++) {
var curr = focusable[i];
for (let i = 0, length = focusable.length; i < length; i++) {
const curr = focusable[i];
if (curr === activeElement) {
continue;
@ -285,7 +285,7 @@ import scrollManager from 'scrollManager';
continue;
}
var elementRect = getOffset(curr);
const elementRect = getOffset(curr);
// not currently visible
if (!elementRect.width && !elementRect.height) {
@ -333,19 +333,19 @@ import scrollManager from 'scrollManager';
break;
}
var x = elementRect.left;
var y = elementRect.top;
var x2 = x + elementRect.width - 1;
var y2 = y + elementRect.height - 1;
const x = elementRect.left;
const y = elementRect.top;
const x2 = x + elementRect.width - 1;
const y2 = y + elementRect.height - 1;
var intersectX = intersects(point1x, point2x, x, x2);
var intersectY = intersects(point1y, point2y, y, y2);
const intersectX = intersects(point1x, point2x, x, x2);
const intersectY = intersects(point1y, point2y, y, y2);
var midX = elementRect.left + (elementRect.width / 2);
var midY = elementRect.top + (elementRect.height / 2);
const midX = elementRect.left + (elementRect.width / 2);
const midY = elementRect.top + (elementRect.height / 2);
var distX;
var distY;
let distX;
let distY;
switch (direction) {
case 0:
@ -372,7 +372,7 @@ import scrollManager from 'scrollManager';
break;
}
var dist = Math.sqrt(distX * distX + distY * distY);
const dist = Math.sqrt(distX * distX + distY * distY);
if (dist < minDistance) {
nearestElement = curr;
@ -383,7 +383,7 @@ import scrollManager from 'scrollManager';
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');
const nearestElementFocusableParent = dom.parentWithClass(nearestElement, 'focusable');
if (nearestElementFocusableParent && nearestElementFocusableParent !== nearestElement) {
if (focusableContainer !== nearestElementFocusableParent) {
nearestElement = nearestElementFocusableParent;
@ -403,16 +403,16 @@ import scrollManager from 'scrollManager';
}
function sendText(text) {
var elem = document.activeElement;
const elem = document.activeElement;
elem.value = text;
}
function focusFirst(container, focusableSelector) {
var elems = container.querySelectorAll(focusableSelector);
const elems = container.querySelectorAll(focusableSelector);
for (var i = 0, length = elems.length; i < length; i++) {
var elem = elems[i];
for (let i = 0, length = elems.length; i < length; i++) {
const elem = elems[i];
if (isCurrentlyFocusableInternal(elem)) {
focus(elem);
@ -422,10 +422,10 @@ import scrollManager from 'scrollManager';
}
function focusLast(container, focusableSelector) {
var elems = [].slice.call(container.querySelectorAll(focusableSelector), 0).reverse();
const elems = [].slice.call(container.querySelectorAll(focusableSelector), 0).reverse();
for (var i = 0, length = elems.length; i < length; i++) {
var elem = elems[i];
for (let i = 0, length = elems.length; i < length; i++) {
const elem = elems[i];
if (isCurrentlyFocusableInternal(elem)) {
focus(elem);
@ -435,11 +435,11 @@ import scrollManager from 'scrollManager';
}
function moveFocus(sourceElement, container, focusableSelector, offset) {
var elems = container.querySelectorAll(focusableSelector);
var list = [];
var i;
var length;
var elem;
const elems = container.querySelectorAll(focusableSelector);
const list = [];
let i;
let length;
let elem;
for (i = 0, length = elems.length; i < length; i++) {
elem = elems[i];
@ -449,7 +449,7 @@ import scrollManager from 'scrollManager';
}
}
var currentIndex = -1;
let currentIndex = -1;
for (i = 0, length = list.length; i < length; i++) {
elem = list[i];
@ -464,11 +464,11 @@ import scrollManager from 'scrollManager';
return;
}
var newIndex = currentIndex + offset;
let newIndex = currentIndex + offset;
newIndex = Math.max(0, newIndex);
newIndex = Math.min(newIndex, list.length - 1);
var newElem = list[newIndex];
const newElem = list[newIndex];
if (newElem) {
focus(newElem);
}
@ -482,23 +482,23 @@ export default {
focusableParent: focusableParent,
getFocusableElements: getFocusableElements,
moveLeft: function (sourceElement, options) {
var container = options ? options.container : null;
var focusableElements = options ? options.focusableElements : null;
const container = options ? options.container : null;
const focusableElements = options ? options.focusableElements : null;
nav(sourceElement, 0, container, focusableElements);
},
moveRight: function (sourceElement, options) {
var container = options ? options.container : null;
var focusableElements = options ? options.focusableElements : null;
const container = options ? options.container : null;
const focusableElements = options ? options.focusableElements : null;
nav(sourceElement, 1, container, focusableElements);
},
moveUp: function (sourceElement, options) {
var container = options ? options.container : null;
var focusableElements = options ? options.focusableElements : null;
const container = options ? options.container : null;
const focusableElements = options ? options.focusableElements : null;
nav(sourceElement, 2, container, focusableElements);
},
moveDown: function (sourceElement, options) {
var container = options ? options.container : null;
var focusableElements = options ? options.focusableElements : null;
const container = options ? options.container : null;
const focusableElements = options ? options.focusableElements : null;
nav(sourceElement, 3, container, focusableElements);
},
sendText: sendText,

View file

@ -4,19 +4,19 @@ import dom from 'dom';
import appRouter from 'appRouter';
function onGroupedCardClick(e, card) {
var itemId = card.getAttribute('data-id');
var serverId = card.getAttribute('data-serverid');
var apiClient = window.connectionManager.getApiClient(serverId);
var userId = apiClient.getCurrentUserId();
var playedIndicator = card.querySelector('.playedIndicator');
var playedIndicatorHtml = playedIndicator ? playedIndicator.innerHTML : null;
var options = {
const itemId = card.getAttribute('data-id');
const serverId = card.getAttribute('data-serverid');
const apiClient = window.connectionManager.getApiClient(serverId);
const userId = apiClient.getCurrentUserId();
const playedIndicator = card.querySelector('.playedIndicator');
const playedIndicatorHtml = playedIndicator ? playedIndicator.innerHTML : null;
const options = {
Limit: parseInt(playedIndicatorHtml || '10'),
Fields: 'PrimaryImageAspectRatio,DateCreated',
ParentId: itemId,
GroupItems: false
};
var actionableParent = dom.parentWithTag(e.target, ['A', 'BUTTON', 'INPUT']);
const actionableParent = dom.parentWithTag(e.target, ['A', 'BUTTON', 'INPUT']);
if (!actionableParent || actionableParent.classList.contains('cardContent')) {
apiClient.getJSON(apiClient.getUrl('Users/' + userId + '/Items/Latest', options)).then(function (items) {
@ -24,7 +24,7 @@ import appRouter from 'appRouter';
return void appRouter.showItem(items[0]);
}
var url = 'details?id=' + itemId + '&serverId=' + serverId;
const url = 'details?id=' + itemId + '&serverId=' + serverId;
Dashboard.navigate(url);
});
e.stopPropagation();
@ -34,7 +34,7 @@ import appRouter from 'appRouter';
}
export default function onItemsContainerClick(e) {
var groupedCard = dom.parentWithClass(e.target, 'groupedCard');
const groupedCard = dom.parentWithClass(e.target, 'groupedCard');
if (groupedCard) {
onGroupedCardClick(e, groupedCard);

View file

@ -75,26 +75,30 @@ import 'emby-checkbox';
value: 'suggestions'
});
list.push({
name: globalize.translate('Latest'),
value: 'latest'
name: globalize.translate('Upcoming'),
value: 'upcoming'
});
list.push({
name: globalize.translate('Genres'),
value: 'genres'
});
list.push({
name: globalize.translate('Favorites'),
value: 'favorites'
name: globalize.translate('Networks'),
value: 'networks'
});
list.push({
name: globalize.translate('Episodes'),
value: 'episodes'
});
} else if (type === 'music') {
list.push({
name: globalize.translate('Suggestions'),
value: 'suggestions',
name: globalize.translate('Albums'),
value: 'albums',
isDefault: true
});
list.push({
name: globalize.translate('Albums'),
value: 'albums'
name: globalize.translate('Suggestions'),
value: 'suggestions'
});
list.push({
name: globalize.translate('HeaderAlbumArtists'),

View file

@ -23,7 +23,7 @@ import events from 'events';
}
function canPlayNativeHls() {
var media = document.createElement('video');
const media = document.createElement('video');
if (media.canPlayType('application/x-mpegURL').replace(/no/, '') ||
media.canPlayType('application/vnd.apple.mpegURL').replace(/no/, '')) {
@ -71,16 +71,16 @@ import events from 'events';
return true;
}
var recoverDecodingErrorDate;
var recoverSwapAudioCodecDate;
let recoverDecodingErrorDate;
let recoverSwapAudioCodecDate;
export function handleHlsJsMediaError(instance, reject) {
var hlsPlayer = instance._hlsPlayer;
const hlsPlayer = instance._hlsPlayer;
if (!hlsPlayer) {
return;
}
var now = Date.now();
let now = Date.now();
if (window.performance && window.performance.now) {
now = performance.now(); // eslint-disable-line compat/compat
@ -136,7 +136,7 @@ import events from 'events';
}
export function seekOnPlaybackStart(instance, element, ticks, onMediaReady) {
var seconds = (ticks || 0) / 10000000;
const seconds = (ticks || 0) / 10000000;
if (seconds) {
// Appending #t=xxx to the query string doesn't seem to work with HLS
@ -148,8 +148,8 @@ import events from 'events';
if (onMediaReady) onMediaReady();
} else {
// update video player position when media is ready to be sought
var events = ['durationchange', 'loadeddata', 'play', 'loadedmetadata'];
var onMediaChange = function(e) {
const events = ['durationchange', 'loadeddata', 'play', 'loadedmetadata'];
const onMediaChange = function(e) {
if (element.currentTime === 0 && element.duration >= seconds) {
// seek only when video position is exactly zero,
// as this is true only if video hasn't started yet or
@ -173,10 +173,10 @@ import events from 'events';
export function applySrc(elem, src, options) {
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();
const playlist = new Windows.Media.Playback.MediaPlaybackList();
var source1 = Windows.Media.Core.MediaSource.createFromStorageFile(file);
var startTime = (options.playerStartPositionTicks || 0) / 10000;
const source1 = Windows.Media.Core.MediaSource.createFromStorageFile(file);
const 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();
@ -194,11 +194,11 @@ import events from 'events';
export function playWithPromise(elem, onErrorFn) {
try {
var promise = elem.play();
const promise = elem.play();
if (promise && promise.then) {
// Chrome now returns a promise
return promise.catch(function (e) {
var errorName = (e.name || '').toLowerCase();
const errorName = (e.name || '').toLowerCase();
// safari uses aborterror
if (errorName === 'notallowederror' ||
errorName === 'aborterror') {
@ -219,7 +219,7 @@ import events from 'events';
}
export function destroyCastPlayer(instance) {
var player = instance._castPlayer;
const player = instance._castPlayer;
if (player) {
try {
player.unload();
@ -232,7 +232,7 @@ import events from 'events';
}
export function destroyHlsPlayer(instance) {
var player = instance._hlsPlayer;
const player = instance._hlsPlayer;
if (player) {
try {
player.destroy();
@ -245,7 +245,7 @@ import events from 'events';
}
export function destroyFlvPlayer(instance) {
var player = instance._flvPlayer;
const player = instance._flvPlayer;
if (player) {
try {
player.unload();
@ -322,9 +322,8 @@ import events from 'events';
break;
case Hls.ErrorTypes.MEDIA_ERROR:
console.debug('fatal media error encountered, try to recover');
var currentReject = reject;
handleHlsJsMediaError(instance, reject);
reject = null;
handleHlsJsMediaError(instance, currentReject);
break;
default:
@ -356,7 +355,7 @@ import events from 'events';
destroyFlvPlayer(instance);
destroyCastPlayer(instance);
var stopInfo = {
const stopInfo = {
src: instance._currentSrc
};
@ -368,20 +367,20 @@ import events from 'events';
}
export function getBufferedRanges(instance, elem) {
var ranges = [];
var seekable = elem.buffered || [];
const ranges = [];
const seekable = elem.buffered || [];
var offset;
var currentPlayOptions = instance._currentPlayOptions;
let offset;
const currentPlayOptions = instance._currentPlayOptions;
if (currentPlayOptions) {
offset = currentPlayOptions.transcodingOffsetTicks;
}
offset = offset || 0;
for (var i = 0, length = seekable.length; i < length; i++) {
var start = seekable.start(i);
var end = seekable.end(i);
for (let i = 0, length = seekable.length; i < length; i++) {
let start = seekable.start(i);
let end = seekable.end(i);
if (!isValidDuration(start)) {
start = 0;

View file

@ -93,7 +93,7 @@ import 'emby-input';
const response = await fetch('components/imageOptionsEditor/imageOptionsEditor.template.html');
const template = await response.text();
var dlg = dialogHelper.createDialog({
const dlg = dialogHelper.createDialog({
size: 'small',
removeOnClose: true,
scrollY: false

View file

@ -58,8 +58,8 @@ import 'css!./imageeditor';
currentItem = item;
apiClient.getRemoteImageProviders(getBaseRemoteOptions()).then(function (providers) {
var btnBrowseAllImages = page.querySelectorAll('.btnBrowseAllImages');
for (var i = 0, length = btnBrowseAllImages.length; i < length; i++) {
const btnBrowseAllImages = page.querySelectorAll('.btnBrowseAllImages');
for (let i = 0, length = btnBrowseAllImages.length; i < length; i++) {
if (providers.length) {
btnBrowseAllImages[i].classList.remove('hide');
} else {

View file

@ -56,7 +56,7 @@ import 'css!./style';
throw new Error('entry cannot be null');
}
const target = entry.target;
var source = undefined;
let source = undefined;
if (target) {
source = target.getAttribute('data-src');
@ -106,7 +106,7 @@ import 'css!./style';
}
function emptyImageElement(elem) {
var url;
let url;
if (elem.tagName !== 'IMG') {
url = elem.style.backgroundImage.slice(4, -1).replace(/"/g, '');
@ -137,10 +137,10 @@ import 'css!./style';
}
export function getPrimaryImageAspectRatio(items) {
var values = [];
const values = [];
for (var i = 0, length = items.length; i < length; i++) {
var ratio = items[i].PrimaryImageAspectRatio || 0;
for (let i = 0, length = items.length; i < length; i++) {
const ratio = items[i].PrimaryImageAspectRatio || 0;
if (!ratio) {
continue;
@ -158,9 +158,9 @@ import 'css!./style';
return a - b;
});
var half = Math.floor(values.length / 2);
const half = Math.floor(values.length / 2);
var result;
let result;
if (values.length % 2) {
result = values[half];
@ -169,13 +169,13 @@ import 'css!./style';
}
// If really close to 2:3 (poster image), just return 2:3
var aspect2x3 = 2 / 3;
const 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;
const aspect16x9 = 16 / 9;
if (Math.abs(aspect16x9 - result) <= 0.2) {
return aspect16x9;
}
@ -186,7 +186,7 @@ import 'css!./style';
}
// If really close to 4:3 (poster image), just return 2:3
var aspect4x3 = 4 / 3;
const aspect4x3 = 4 / 3;
if (Math.abs(aspect4x3 - result) <= 0.15) {
return aspect4x3;
}
@ -195,8 +195,8 @@ import 'css!./style';
}
export function fillImages(elems) {
for (var i = 0, length = elems.length; i < length; i++) {
var elem = elems[0];
for (let i = 0, length = elems.length; i < length; i++) {
const elem = elems[0];
fillImage(elem);
}
}

View file

@ -399,12 +399,10 @@ import 'emby-input';
}
if (contentType === 'tvshows') {
parent.querySelector('.chkImportMissingEpisodesContainer').classList.remove('hide');
parent.querySelector('.chkAutomaticallyGroupSeriesContainer').classList.remove('hide');
parent.querySelector('.fldSeasonZeroDisplayName').classList.remove('hide');
parent.querySelector('#txtSeasonZeroName').setAttribute('required', 'required');
} else {
parent.querySelector('.chkImportMissingEpisodesContainer').classList.add('hide');
parent.querySelector('.chkAutomaticallyGroupSeriesContainer').classList.add('hide');
parent.querySelector('.fldSeasonZeroDisplayName').classList.add('hide');
parent.querySelector('#txtSeasonZeroName').removeAttribute('required');
@ -511,7 +509,6 @@ import 'emby-input';
EnableChapterImageExtraction: parent.querySelector('.chkExtractChapterImages').checked,
DownloadImagesInAdvance: parent.querySelector('#chkDownloadImagesInAdvance').checked,
EnableInternetProviders: true,
ImportMissingEpisodes: parent.querySelector('#chkImportMissingEpisodes').checked,
SaveLocalMetadata: parent.querySelector('#chkSaveLocal').checked,
EnableAutomaticSeriesGrouping: parent.querySelector('.chkAutomaticallyGroupSeries').checked,
PreferredMetadataLanguage: parent.querySelector('#selectLanguage').value,
@ -569,7 +566,6 @@ import 'emby-input';
parent.querySelector('.chkExtractChapterImages').checked = options.EnableChapterImageExtraction;
parent.querySelector('#chkDownloadImagesInAdvance').checked = options.DownloadImagesInAdvance;
parent.querySelector('#chkSaveLocal').checked = options.SaveLocalMetadata;
parent.querySelector('#chkImportMissingEpisodes').checked = options.ImportMissingEpisodes;
parent.querySelector('.chkAutomaticallyGroupSeries').checked = options.EnableAutomaticSeriesGrouping;
parent.querySelector('#chkEnableEmbeddedTitles').checked = options.EnableEmbeddedTitles;
parent.querySelector('#chkEnableEmbeddedEpisodeInfos').checked = options.EnableEmbeddedEpisodeInfos;

View file

@ -85,14 +85,6 @@
<div class="fieldDescription checkboxFieldDescription">${OptionAutomaticallyGroupSeriesHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription hide chkImportMissingEpisodesContainer advanced">
<label>
<input is="emby-checkbox" type="checkbox" id="chkImportMissingEpisodes" />
<span>${DisplayMissingEpisodesWithinSeasons}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${ImportMissingEpisodesHelp}</div>
</div>
<div class="chapterSettingsSection hide">
<h2>${HeaderChapterImages}</h2>
<div class="checkboxContainer checkboxContainer-withDescription fldExtractChapterImages">

View file

@ -256,8 +256,8 @@ import 'emby-playstatebutton';
}
if (options.image !== false) {
var imgUrl = options.imageSource === 'channel' ? getChannelImageUrl(item, downloadWidth) : getImageUrl(item, downloadWidth);
var imageClass = isLargeStyle ? 'listItemImage listItemImage-large' : 'listItemImage';
const imgUrl = options.imageSource === 'channel' ? getChannelImageUrl(item, downloadWidth) : getImageUrl(item, downloadWidth);
let imageClass = isLargeStyle ? 'listItemImage listItemImage-large' : 'listItemImage';
if (isLargeStyle && layoutManager.tv) {
imageClass += ' listItemImage-large-tv';

View file

@ -100,11 +100,10 @@ import 'emby-button';
return html;
}
export function getMediaInfoHtml(item, options) {
export function getMediaInfoHtml(item, options = {}) {
let html = '';
const miscInfo = [];
options = options || {};
let text;
let date;
let minutes;
@ -289,7 +288,9 @@ import 'emby-button';
return getMediaInfoItem(m);
}).join('');
html += getStarIconsHtml(item);
if (options.starRating !== false) {
html += getStarIconsHtml(item);
}
if (item.HasSubtitles && options.subtitles !== false) {
html += '<div class="mediaInfoItem mediaInfoText closedCaptionMediaInfoText">CC</div>';
@ -418,9 +419,8 @@ import 'emby-button';
return false;
}
export function getPrimaryMediaInfoHtml(item, options) {
options = options || {};
if (options.interactive == null) {
export function getPrimaryMediaInfoHtml(item, options = {}) {
if (options.interactive === undefined) {
options.interactive = false;
}

View file

@ -239,7 +239,7 @@ import 'flexStyles';
}
function afterDeleted(context, item) {
var parentId = item.ParentId || item.SeasonId || item.SeriesId;
const parentId = item.ParentId || item.SeasonId || item.SeriesId;
if (parentId) {
reload(context, parentId, item.ServerId);
@ -252,7 +252,7 @@ import 'flexStyles';
function showMoreMenu(context, button, user) {
import('itemContextMenu').then(({default: itemContextMenu}) => {
var item = currentItem;
const item = currentItem;
itemContextMenu.show({
item: item,

View file

@ -251,7 +251,7 @@
</div>
<br />
<div class="formDialogFooter">
<button is="emby-button" class="raised button-cancel block btnCancel formDialogFooterItem">
<button is="emby-button" type="button" class="raised button-cancel block btnCancel formDialogFooterItem">
<span>${Cancel}</span>
</button>
<button is="emby-button" type="submit" class="raised button-submit block btnSave formDialogFooterItem">

View file

@ -582,7 +582,7 @@ import 'emby-ratingbutton';
function onPlaybackStart(e, state) {
console.debug('nowplaying event: ' + e.type);
var player = this;
const player = this;
onStateChanged.call(player, e, state);
}

View file

@ -8,7 +8,7 @@ import pluginManager from 'pluginManager';
init() {
console.groupCollapsed('loading packages');
var manifestUrls = JSON.parse(appSettings.get(this.#settingsKey) || '[]');
const manifestUrls = JSON.parse(appSettings.get(this.#settingsKey) || '[]');
return Promise.all(manifestUrls.map((url) => {
return this.loadPackage(url);
@ -30,7 +30,7 @@ import pluginManager from 'pluginManager';
install(url) {
return this.loadPackage(url, true).then((pkg) => {
var manifestUrls = JSON.parse(appSettings.get(this.#settingsKey) || '[]');
const manifestUrls = JSON.parse(appSettings.get(this.#settingsKey) || '[]');
if (!manifestUrls.includes(url)) {
manifestUrls.push(url);
@ -42,7 +42,7 @@ import pluginManager from 'pluginManager';
}
uninstall(name) {
var pkg = this.#packagesList.filter((p) => {
const pkg = this.#packagesList.filter((p) => {
return p.name === name;
})[0];
@ -58,12 +58,12 @@ import pluginManager from 'pluginManager';
}
mapPath(pkg, pluginUrl) {
var urlLower = pluginUrl.toLowerCase();
const urlLower = pluginUrl.toLowerCase();
if (urlLower.startsWith('http:') || urlLower.startsWith('https:') || urlLower.startsWith('file:')) {
return pluginUrl;
}
var packageUrl = pkg.url;
let packageUrl = pkg.url;
packageUrl = packageUrl.substring(0, packageUrl.lastIndexOf('/'));
packageUrl += '/';
@ -81,7 +81,7 @@ import pluginManager from 'pluginManager';
}
removeUrl(url) {
var manifestUrls = JSON.parse(appSettings.get(this.#settingsKey) || '[]');
let manifestUrls = JSON.parse(appSettings.get(this.#settingsKey) || '[]');
manifestUrls = manifestUrls.filter((i) => {
return i !== url;
@ -92,14 +92,14 @@ import pluginManager from 'pluginManager';
loadPackage(url, throwError = false) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
var originalUrl = url;
const xhr = new XMLHttpRequest();
const originalUrl = url;
url += url.indexOf('?') === -1 ? '?' : '&';
url += 't=' + new Date().getTime();
xhr.open('GET', url, true);
var onError = () => {
const onError = () => {
if (throwError === true) {
reject();
} else {
@ -110,16 +110,16 @@ import pluginManager from 'pluginManager';
xhr.onload = () => {
if (this.status < 400) {
var pkg = JSON.parse(this.response);
const pkg = JSON.parse(this.response);
pkg.url = originalUrl;
this.addPackage(pkg);
var plugins = pkg.plugins || [];
const plugins = pkg.plugins || [];
if (pkg.plugin) {
plugins.push(pkg.plugin);
}
var promises = plugins.map((pluginUrl) => {
const promises = plugins.map((pluginUrl) => {
return pluginManager.loadPlugin(this.mapPath(pkg, pluginUrl));
});
Promise.all(promises).then(resolve, resolve);

View file

@ -5,15 +5,15 @@ import browser from 'browser';
import 'css!./iconosd';
import 'material-icons';
var currentPlayer;
var osdElement;
var iconElement;
var progressElement;
let currentPlayer;
let osdElement;
let iconElement;
let progressElement;
var enableAnimation;
let enableAnimation;
function getOsdElementHtml() {
var html = '';
let html = '';
html += '<span class="material-icons iconOsdIcon brightness_high"></span>';
@ -23,7 +23,7 @@ function getOsdElementHtml() {
}
function ensureOsdElement() {
var elem = osdElement;
let elem = osdElement;
if (!elem) {
enableAnimation = browser.supportsCssAnimation();
@ -46,11 +46,11 @@ function onHideComplete() {
this.classList.add('hide');
}
var hideTimeout;
let hideTimeout;
function showOsd() {
clearHideTimeout();
var elem = osdElement;
const elem = osdElement;
dom.removeEventListener(elem, dom.whichTransitionEvent(), onHideComplete, {
once: true
@ -78,7 +78,7 @@ function clearHideTimeout() {
function hideOsd() {
clearHideTimeout();
var elem = osdElement;
const elem = osdElement;
if (elem) {
if (enableAnimation) {
// trigger reflow
@ -118,7 +118,7 @@ function updateElementsFromPlayer(brightness) {
}
function releaseCurrentPlayer() {
var player = currentPlayer;
const player = currentPlayer;
if (player) {
events.off(player, 'brightnesschange', onBrightnessChanged);
@ -128,7 +128,7 @@ function releaseCurrentPlayer() {
}
function onBrightnessChanged(e) {
var player = this;
const player = this;
ensureOsdElement();

View file

@ -1,5 +1,6 @@
import playbackManager from 'playbackManager';
import nowPlayingHelper from 'nowPlayingHelper';
import shell from 'shell';
import events from 'events';
/* eslint-disable indent */
@ -127,8 +128,7 @@ import events from 'events';
});
} else {
const itemImageUrl = seriesImageUrl(item, { maxHeight: 3000 }) || imageUrl(item, { maxHeight: 3000 });
window.NativeShell.updateMediaSession({
shell.updateMediaSession({
action: eventName,
isLocalPlayer: isLocalPlayer,
itemId: itemId,
@ -182,7 +182,7 @@ import events from 'events';
/* eslint-disable-next-line compat/compat */
navigator.mediaSession.metadata = null;
} else {
window.NativeShell.hideMediaSession();
shell.hideMediaSession();
}
}

View file

@ -1,7 +1,7 @@
export function getNowPlayingNames(nowPlayingItem, includeNonNameInfo) {
var topItem = nowPlayingItem;
var bottomItem = null;
var topText = nowPlayingItem.Name;
let topItem = nowPlayingItem;
let bottomItem = null;
let topText = nowPlayingItem.Name;
if (nowPlayingItem.AlbumId && nowPlayingItem.MediaType === 'Audio') {
topItem = {
@ -21,7 +21,7 @@ export function getNowPlayingNames(nowPlayingItem, includeNonNameInfo) {
}
}
var bottomText = '';
let bottomText = '';
if (nowPlayingItem.ArtistItems && nowPlayingItem.ArtistItems.length) {
bottomItem = {
@ -56,7 +56,7 @@ export function getNowPlayingNames(nowPlayingItem, includeNonNameInfo) {
bottomText = nowPlayingItem.ProductionYear;
}
var list = [];
const list = [];
list.push({
text: topText,

View file

@ -2,7 +2,7 @@ import playbackManager from 'playbackManager';
import layoutManager from 'layoutManager';
import events from 'events';
var orientationLocked;
let orientationLocked;
function onOrientationChangeSuccess() {
orientationLocked = true;
@ -14,15 +14,15 @@ function onOrientationChangeError(err) {
}
events.on(playbackManager, 'playbackstart', function (e, player, state) {
var isLocalVideo = player.isLocalPlayer && !player.isExternalPlayer && playbackManager.isPlayingVideo(player);
const isLocalVideo = player.isLocalPlayer && !player.isExternalPlayer && playbackManager.isPlayingVideo(player);
if (isLocalVideo && layoutManager.mobile) {
/* eslint-disable-next-line compat/compat */
var lockOrientation = window.screen.lockOrientation || window.screen.mozLockOrientation || window.screen.msLockOrientation || (window.screen.orientation && window.screen.orientation.lock);
const lockOrientation = window.screen.lockOrientation || window.screen.mozLockOrientation || window.screen.msLockOrientation || (window.screen.orientation && window.screen.orientation.lock);
if (lockOrientation) {
try {
var promise = lockOrientation('landscape');
const promise = lockOrientation('landscape');
if (promise.then) {
promise.then(onOrientationChangeSuccess, onOrientationChangeError);
} else {
@ -39,7 +39,7 @@ events.on(playbackManager, 'playbackstart', function (e, player, state) {
events.on(playbackManager, 'playbackstop', function (e, playbackStopInfo) {
if (orientationLocked && !playbackStopInfo.nextMediaType) {
/* eslint-disable-next-line compat/compat */
var unlockOrientation = window.screen.unlockOrientation || window.screen.mozUnlockOrientation || window.screen.msUnlockOrientation || (window.screen.orientation && window.screen.orientation.unlock);
const unlockOrientation = window.screen.unlockOrientation || window.screen.mozUnlockOrientation || window.screen.msUnlockOrientation || (window.screen.orientation && window.screen.orientation.unlock);
if (unlockOrientation) {
try {

View file

@ -9,7 +9,7 @@ import appHost from 'apphost';
import * as autocast from 'autocast';
function mirrorItem(info, player) {
var item = info.item;
const item = info.item;
playbackManager.displayContent({
@ -22,7 +22,7 @@ function mirrorItem(info, player) {
function mirrorIfEnabled(info) {
if (info && playbackManager.enableDisplayMirroring()) {
var getPlayerInfo = playbackManager.getPlayerInfo();
const getPlayerInfo = playbackManager.getPlayerInfo();
if (getPlayerInfo) {
if (!getPlayerInfo.isLocalPlayer && getPlayerInfo.supportedCommands.indexOf('DisplayContent') !== -1) {
@ -45,7 +45,7 @@ function getTargetSecondaryText(target) {
}
function getIcon(target) {
var deviceType = target.deviceType;
let deviceType = target.deviceType;
if (!deviceType && target.isLocalPlayer) {
if (browser.tv) {
@ -78,7 +78,7 @@ function getIcon(target) {
}
export function show(button) {
var currentPlayerInfo = playbackManager.getPlayerInfo();
const currentPlayerInfo = playbackManager.getPlayerInfo();
if (currentPlayerInfo) {
if (!currentPlayerInfo.isLocalPlayer) {
@ -87,13 +87,13 @@ export function show(button) {
}
}
var currentPlayerId = currentPlayerInfo ? currentPlayerInfo.id : null;
const currentPlayerId = currentPlayerInfo ? currentPlayerInfo.id : null;
loading.show();
playbackManager.getTargets().then(function (targets) {
var menuItems = targets.map(function (t) {
var name = t.name;
const menuItems = targets.map(function (t) {
let name = t.name;
if (t.appName && t.appName !== t.name) {
name += ' - ' + t.appName;
@ -111,7 +111,7 @@ export function show(button) {
import('actionsheet').then(({default: actionsheet}) => {
loading.hide();
var menuOptions = {
const menuOptions = {
title: globalize.translate('HeaderPlayOn'),
items: menuItems,
positionTo: button,
@ -127,7 +127,7 @@ export function show(button) {
}
actionsheet.show(menuOptions).then(function (id) {
var target = targets.filter(function (t) {
const target = targets.filter(function (t) {
return t.id === id;
})[0];
@ -153,7 +153,7 @@ function showActivePlayerMenu(playerInfo) {
function disconnectFromPlayer(currentDeviceName) {
if (playbackManager.getSupportedCommands().indexOf('EndSession') !== -1) {
import('dialog').then(({default: dialog}) => {
var menuItems = [];
const menuItems = [];
menuItems.push({
name: globalize.translate('Yes'),
@ -188,9 +188,9 @@ function disconnectFromPlayer(currentDeviceName) {
}
function showActivePlayerMenuInternal(dialogHelper, playerInfo) {
var html = '';
let html = '';
var dialogOptions = {
const dialogOptions = {
removeOnClose: true
};
@ -199,11 +199,11 @@ function showActivePlayerMenuInternal(dialogHelper, playerInfo) {
dialogOptions.exitAnimationDuration = 160;
dialogOptions.autoFocus = false;
var dlg = dialogHelper.createDialog(dialogOptions);
const dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('promptDialog');
var currentDeviceName = (playerInfo.deviceName || playerInfo.name);
const currentDeviceName = (playerInfo.deviceName || playerInfo.name);
html += '<div class="promptDialogContent" style="padding:1.5em;">';
html += '<h2 style="margin-top:.5em;">';
@ -214,7 +214,7 @@ function showActivePlayerMenuInternal(dialogHelper, playerInfo) {
if (playerInfo.supportedCommands.indexOf('DisplayContent') !== -1) {
html += '<label class="checkboxContainer">';
var checkedHtml = playbackManager.enableDisplayMirroring() ? ' checked' : '';
const checkedHtml = playbackManager.enableDisplayMirroring() ? ' checked' : '';
html += '<input type="checkbox" is="emby-checkbox" class="chkMirror"' + checkedHtml + '/>';
html += '<span>' + globalize.translate('EnableDisplayMirroring') + '</span>';
html += '</label>';
@ -224,7 +224,7 @@ function showActivePlayerMenuInternal(dialogHelper, playerInfo) {
if (autocast.supported()) {
html += '<div><label class="checkboxContainer">';
var checkedHtmlAC = autocast.isEnabled() ? ' checked' : '';
const checkedHtmlAC = autocast.isEnabled() ? ' checked' : '';
html += '<input type="checkbox" is="emby-checkbox" class="chkAutoCast"' + checkedHtmlAC + '/>';
html += '<span>' + globalize.translate('EnableAutoCast') + '</span>';
html += '</label></div>';
@ -240,21 +240,21 @@ function showActivePlayerMenuInternal(dialogHelper, playerInfo) {
html += '</div>';
dlg.innerHTML = html;
var chkMirror = dlg.querySelector('.chkMirror');
const chkMirror = dlg.querySelector('.chkMirror');
if (chkMirror) {
chkMirror.addEventListener('change', onMirrorChange);
}
var chkAutoCast = dlg.querySelector('.chkAutoCast');
const chkAutoCast = dlg.querySelector('.chkAutoCast');
if (chkAutoCast) {
chkAutoCast.addEventListener('change', onAutoCastChange);
}
var destination = '';
let destination = '';
var btnRemoteControl = dlg.querySelector('.btnRemoteControl');
const btnRemoteControl = dlg.querySelector('.btnRemoteControl');
if (btnRemoteControl) {
btnRemoteControl.addEventListener('click', function () {
destination = 'nowplaying';
@ -289,8 +289,8 @@ function onAutoCastChange() {
}
document.addEventListener('viewshow', function (e) {
var state = e.detail.state || {};
var item = state.item;
const state = e.detail.state || {};
const item = state.item;
if (item && item.ServerId) {
mirrorIfEnabled({

View file

@ -4,13 +4,13 @@ import globalize from 'globalize';
import qualityoptions from 'qualityoptions';
function showQualityMenu(player, btn) {
var videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) {
const videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) {
return stream.Type === 'Video';
})[0];
var videoWidth = videoStream ? videoStream.Width : null;
var videoHeight = videoStream ? videoStream.Height : null;
const videoWidth = videoStream ? videoStream.Width : null;
const videoHeight = videoStream ? videoStream.Height : null;
var options = qualityoptions.getVideoQualityOptions({
const options = qualityoptions.getVideoQualityOptions({
currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player),
isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player),
videoWidth: videoWidth,
@ -18,8 +18,8 @@ function showQualityMenu(player, btn) {
enableAuto: true
});
var menuItems = options.map(function (o) {
var opt = {
const menuItems = options.map(function (o) {
const opt = {
name: o.name,
id: o.bitrate,
asideText: o.secondaryText
@ -32,7 +32,7 @@ function showQualityMenu(player, btn) {
return opt;
});
var selectedId = options.filter(function (o) {
let selectedId = options.filter(function (o) {
return o.selected;
});
@ -42,7 +42,7 @@ function showQualityMenu(player, btn) {
items: menuItems,
positionTo: btn
}).then(function (id) {
var bitrate = parseInt(id);
const bitrate = parseInt(id);
if (bitrate !== selectedId) {
playbackManager.setMaxStreamingBitrate({
enableAutomaticBitrateDetection: bitrate ? false : true,
@ -53,8 +53,8 @@ function showQualityMenu(player, btn) {
}
function showRepeatModeMenu(player, btn) {
var menuItems = [];
var currentValue = playbackManager.getRepeatMode(player);
const menuItems = [];
const currentValue = playbackManager.getRepeatMode(player);
menuItems.push({
name: globalize.translate('RepeatAll'),
@ -85,16 +85,16 @@ function showRepeatModeMenu(player, btn) {
}
function getQualitySecondaryText(player) {
var state = playbackManager.getPlayerState(player);
const state = playbackManager.getPlayerState(player);
var videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) {
const videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) {
return stream.Type === 'Video';
})[0];
var videoWidth = videoStream ? videoStream.Width : null;
var videoHeight = videoStream ? videoStream.Height : null;
const videoWidth = videoStream ? videoStream.Width : null;
const videoHeight = videoStream ? videoStream.Height : null;
var options = qualityoptions.getVideoQualityOptions({
const options = qualityoptions.getVideoQualityOptions({
currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player),
isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player),
videoWidth: videoWidth,
@ -102,7 +102,7 @@ function getQualitySecondaryText(player) {
enableAuto: true
});
var selectedOption = options.filter(function (o) {
let selectedOption = options.filter(function (o) {
return o.selected;
});
@ -111,7 +111,7 @@ function getQualitySecondaryText(player) {
}
selectedOption = selectedOption[0];
var text = selectedOption.name;
let text = selectedOption.name;
if (selectedOption.autoText) {
if (state.PlayState && state.PlayState.PlayMethod !== 'Transcode') {
@ -126,8 +126,8 @@ function getQualitySecondaryText(player) {
function showAspectRatioMenu(player, btn) {
// each has a name and id
var currentId = playbackManager.getAspectRatio(player);
var menuItems = playbackManager.getSupportedAspectRatios(player).map(function (i) {
const currentId = playbackManager.getAspectRatio(player);
const menuItems = playbackManager.getSupportedAspectRatios(player).map(function (i) {
return {
id: i.id,
name: i.name,
@ -171,12 +171,12 @@ function showPlaybackRateMenu(player, btn) {
}
function showWithUser(options, player, user) {
var supportedCommands = playbackManager.getSupportedCommands(player);
const supportedCommands = playbackManager.getSupportedCommands(player);
var menuItems = [];
const menuItems = [];
if (supportedCommands.indexOf('SetAspectRatio') !== -1) {
var currentAspectRatioId = playbackManager.getAspectRatio(player);
var currentAspectRatio = playbackManager.getSupportedAspectRatios(player).filter(function (i) {
const currentAspectRatioId = playbackManager.getAspectRatio(player);
const currentAspectRatio = playbackManager.getSupportedAspectRatios(player).filter(function (i) {
return i.id === currentAspectRatioId;
})[0];
@ -199,7 +199,7 @@ function showWithUser(options, player, user) {
}
if (user && user.Policy.EnableVideoPlaybackTranscoding) {
var secondaryQualityText = getQualitySecondaryText(player);
const secondaryQualityText = getQualitySecondaryText(player);
menuItems.push({
name: globalize.translate('Quality'),
@ -208,7 +208,7 @@ function showWithUser(options, player, user) {
});
}
var repeatMode = playbackManager.getRepeatMode(player);
const repeatMode = playbackManager.getRepeatMode(player);
if (supportedCommands.indexOf('SetRepeatMode') !== -1 && playbackManager.currentMediaSource(player).RunTimeTicks) {
menuItems.push({
@ -243,14 +243,14 @@ function showWithUser(options, player, user) {
}
export function show(options) {
var player = options.player;
var currentItem = playbackManager.currentItem(player);
const player = options.player;
const currentItem = playbackManager.currentItem(player);
if (!currentItem || !currentItem.ServerId) {
return showWithUser(options, player, null);
}
var apiClient = window.connectionManager.getApiClient(currentItem.ServerId);
const apiClient = window.connectionManager.getApiClient(currentItem.ServerId);
return apiClient.getCurrentUser().then(function (user) {
return showWithUser(options, player, user);
});

View file

@ -5,15 +5,15 @@ import browser from 'browser';
import 'css!./iconosd';
import 'material-icons';
var currentPlayer;
var osdElement;
var iconElement;
var progressElement;
let currentPlayer;
let osdElement;
let iconElement;
let progressElement;
var enableAnimation;
let enableAnimation;
function getOsdElementHtml() {
var html = '';
let html = '';
html += '<span class="material-icons iconOsdIcon volume_up"></span>';
@ -23,7 +23,7 @@ function getOsdElementHtml() {
}
function ensureOsdElement() {
var elem = osdElement;
let elem = osdElement;
if (!elem) {
enableAnimation = browser.supportsCssAnimation();
@ -46,11 +46,11 @@ function onHideComplete() {
this.classList.add('hide');
}
var hideTimeout;
let hideTimeout;
function showOsd() {
clearHideTimeout();
var elem = osdElement;
const elem = osdElement;
dom.removeEventListener(elem, dom.whichTransitionEvent(), onHideComplete, {
once: true
@ -78,7 +78,7 @@ function clearHideTimeout() {
function hideOsd() {
clearHideTimeout();
var elem = osdElement;
const elem = osdElement;
if (elem) {
if (enableAnimation) {
// trigger reflow
@ -108,7 +108,7 @@ function updatePlayerVolumeState(isMuted, volume) {
}
function releaseCurrentPlayer() {
var player = currentPlayer;
const player = currentPlayer;
if (player) {
events.off(player, 'volumechange', onVolumeChanged);
@ -118,7 +118,7 @@ function releaseCurrentPlayer() {
}
function onVolumeChanged(e) {
var player = this;
const player = this;
ensureOsdElement();

View file

@ -413,7 +413,7 @@ import 'css!./playerstats';
name: 'Original Media Info'
});
var apiClient = window.connectionManager.getApiClient(playbackManager.currentItem(player).ServerId);
const apiClient = window.connectionManager.getApiClient(playbackManager.currentItem(player).ServerId);
if (syncPlayManager.isSyncPlayEnabled() && apiClient.isMinServerVersion('10.6.0')) {
categories.push({
stats: getSyncPlayStats(),

View file

@ -4,11 +4,11 @@ import playbackManager from 'playbackManager';
import globalize from 'globalize';
export function show(options) {
var item = options.item;
const item = options.item;
var resumePositionTicks = item.UserData ? item.UserData.PlaybackPositionTicks : null;
const resumePositionTicks = item.UserData ? item.UserData.PlaybackPositionTicks : null;
var playableItemId = item.Type === 'Program' ? item.ChannelId : item.Id;
const playableItemId = item.Type === 'Program' ? item.ChannelId : item.Id;
if (!resumePositionTicks || item.IsFolder) {
playbackManager.play({
@ -18,7 +18,7 @@ export function show(options) {
return;
}
var menuItems = [];
const menuItems = [];
menuItems.push({
name: globalize.translate('ResumeAt', datetime.getDisplayRunningTime(resumePositionTicks)),

View file

@ -3,7 +3,7 @@ import globalize from 'globalize';
/* eslint-disable indent */
// TODO: replace with each plugin version
var cacheParam = new Date().getTime();
const cacheParam = new Date().getTime();
class PluginManager {
pluginsList = [];
@ -13,7 +13,7 @@ import globalize from 'globalize';
}
#loadStrings(plugin) {
var strings = plugin.getTranslations ? plugin.getTranslations() : [];
const strings = plugin.getTranslations ? plugin.getTranslations() : [];
return globalize.loadStrings({
name: plugin.id || plugin.packageName,
strings: strings
@ -56,10 +56,10 @@ import globalize from 'globalize';
return new Promise((resolve, reject) => {
require([pluginSpec], (pluginFactory) => {
var plugin = pluginFactory.default ? new pluginFactory.default() : new pluginFactory();
const plugin = pluginFactory.default ? new pluginFactory.default() : new pluginFactory();
// See if it's already installed
var existing = this.pluginsList.filter(function (p) {
const existing = this.pluginsList.filter(function (p) {
return p.id === plugin.id;
})[0];
@ -69,10 +69,10 @@ import globalize from 'globalize';
plugin.installUrl = pluginSpec;
var separatorIndex = Math.max(pluginSpec.lastIndexOf('/'), pluginSpec.lastIndexOf('\\'));
const separatorIndex = Math.max(pluginSpec.lastIndexOf('/'), pluginSpec.lastIndexOf('\\'));
plugin.baseUrl = pluginSpec.substring(0, separatorIndex);
var paths = {};
const paths = {};
paths[plugin.id] = plugin.baseUrl;
requirejs.config({
@ -135,7 +135,7 @@ import globalize from 'globalize';
})[0];
}
var url = plugin.baseUrl + '/' + path;
let url = plugin.baseUrl + '/' + path;
if (addCacheParam) {
url += url.includes('?') ? '&' : '?';

View file

@ -1,9 +1,9 @@
import globalize from 'globalize';
export function getVideoQualityOptions(options) {
var maxStreamingBitrate = options.currentMaxBitrate;
var videoWidth = options.videoWidth;
var videoHeight = options.videoHeight;
const maxStreamingBitrate = options.currentMaxBitrate;
let videoWidth = options.videoWidth;
const videoHeight = options.videoHeight;
// If the aspect ratio is less than 16/9 (1.77), set the width as if it were pillarboxed.
// 4:3 1440x1080 -> 1920x1080
@ -11,9 +11,9 @@ export function getVideoQualityOptions(options) {
videoWidth = videoHeight * (16 / 9);
}
var maxAllowedWidth = videoWidth || 4096;
const maxAllowedWidth = videoWidth || 4096;
var qualityOptions = [];
const qualityOptions = [];
if (maxAllowedWidth >= 3800) {
qualityOptions.push({ name: '4K - 120 Mbps', maxHeight: 2160, bitrate: 120000000 });
@ -65,7 +65,7 @@ export function getVideoQualityOptions(options) {
qualityOptions.push({ name: '240p', maxHeight: 240, bitrate: 320000 });
qualityOptions.push({ name: '144p', maxHeight: 144, bitrate: 192000 });
var autoQualityOption = {
const autoQualityOption = {
name: globalize.translate('Auto'),
bitrate: 0,
selected: options.isAutomaticBitrateEnabled
@ -76,9 +76,9 @@ export function getVideoQualityOptions(options) {
}
if (maxStreamingBitrate) {
var selectedIndex = -1;
for (var i = 0, length = qualityOptions.length; i < length; i++) {
var option = qualityOptions[i];
let selectedIndex = -1;
for (let i = 0, length = qualityOptions.length; i < length; i++) {
const option = qualityOptions[i];
if (selectedIndex === -1 && option.bitrate <= maxStreamingBitrate) {
selectedIndex = i;
@ -89,7 +89,7 @@ export function getVideoQualityOptions(options) {
selectedIndex = qualityOptions.length - 1;
}
var currentQualityOption = qualityOptions[selectedIndex];
const currentQualityOption = qualityOptions[selectedIndex];
if (!options.isAutomaticBitrateEnabled) {
currentQualityOption.selected = true;
@ -102,9 +102,9 @@ export function getVideoQualityOptions(options) {
}
export function getAudioQualityOptions(options) {
var maxStreamingBitrate = options.currentMaxBitrate;
const maxStreamingBitrate = options.currentMaxBitrate;
var qualityOptions = [];
const qualityOptions = [];
qualityOptions.push({ name: '2 Mbps', bitrate: 2000000 });
qualityOptions.push({ name: '1.5 Mbps', bitrate: 1500000 });
@ -116,7 +116,7 @@ export function getAudioQualityOptions(options) {
qualityOptions.push({ name: '96 kbps', bitrate: 96000 });
qualityOptions.push({ name: '64 kbps', bitrate: 64000 });
var autoQualityOption = {
const autoQualityOption = {
name: globalize.translate('Auto'),
bitrate: 0,
selected: options.isAutomaticBitrateEnabled
@ -127,9 +127,9 @@ export function getAudioQualityOptions(options) {
}
if (maxStreamingBitrate) {
var selectedIndex = -1;
for (var i = 0, length = qualityOptions.length; i < length; i++) {
var option = qualityOptions[i];
let selectedIndex = -1;
for (let i = 0, length = qualityOptions.length; i < length; i++) {
const option = qualityOptions[i];
if (selectedIndex === -1 && option.bitrate <= maxStreamingBitrate) {
selectedIndex = i;
@ -140,7 +140,7 @@ export function getAudioQualityOptions(options) {
selectedIndex = qualityOptions.length - 1;
}
var currentQualityOption = qualityOptions[selectedIndex];
const currentQualityOption = qualityOptions[selectedIndex];
if (!options.isAutomaticBitrateEnabled) {
currentQualityOption.selected = true;

View file

@ -1,7 +1,7 @@
define(function () {
'use strict';
var requireCss = {};
const requireCss = {};
requireCss.normalize = function (name, normalize) {
if (name.substr(name.length - 4, 4) === '.css') {
@ -11,7 +11,7 @@ define(function () {
return normalize(name);
};
var importedCss = [];
let importedCss = [];
function isLoaded(url) {
return importedCss.indexOf(url) !== -1;
@ -27,14 +27,14 @@ define(function () {
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 = 'components/require/requirecss';
var index = cssId.indexOf(srch);
const srch = 'components/require/requirecss';
const index = cssId.indexOf(srch);
if (index !== -1) {
cssId = 'css' + cssId.substring(index + srch.length);
}
var url = cssId + '.css';
let url = cssId + '.css';
if (url.indexOf('://') === -1) {
url = config.baseUrl + url;
@ -43,13 +43,13 @@ define(function () {
if (!isLoaded(url)) {
importedCss.push(url);
var link = document.createElement('link');
const link = document.createElement('link');
link.setAttribute('rel', 'stylesheet');
link.setAttribute('type', 'text/css');
link.onload = load;
var linkUrl = url;
let linkUrl = url;
if (config.urlArgs) {
linkUrl += config.urlArgs(cssId, url);

View file

@ -2,7 +2,7 @@ define(function () {
'use strict';
// hack to work around the server's auto-redirection feature
var addRedirectPrevention = window.dashboardVersion != null && window.Dashboard && !window.AppInfo.isNativeApp;
const addRedirectPrevention = window.dashboardVersion != null && window.Dashboard && !window.AppInfo.isNativeApp;
return {
@ -25,7 +25,7 @@ define(function () {
url += 'r=0';
}
var xhr = new XMLHttpRequest();
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onload = function (e) {

View file

@ -2,7 +2,7 @@
(function () {
'use strict';
var connectionManager;
let connectionManager;
function getApiClient(serverId) {
if (connectionManager) {
@ -15,8 +15,7 @@
return getApiClient(serverId).then(function (apiClient) {
switch (action) {
case 'cancel-install':
var id = data.id;
return apiClient.cancelPackageInstallation(id);
return apiClient.cancelPackageInstallation(data.id);
case 'restart':
return apiClient.restartServer();
default:
@ -28,12 +27,12 @@
/* eslint-disable-next-line no-restricted-globals -- self is valid in a serviceworker environment */
self.addEventListener('notificationclick', function (event) {
var notification = event.notification;
const notification = event.notification;
notification.close();
var data = notification.data;
var serverId = data.serverId;
var action = event.action;
const data = notification.data;
const serverId = data.serverId;
const action = event.action;
if (!action) {
clients.openWindow('/');

View file

@ -12,7 +12,7 @@ import playbackPermissionManager from 'playbackPermissionManager';
* @returns {string} The player's id.
*/
function getActivePlayerId () {
var info = playbackManager.getPlayerInfo();
const info = playbackManager.getPlayerInfo();
return info ? info.id : null;
}
@ -38,7 +38,7 @@ function showNewJoinGroupSelection (button, user, apiClient) {
apiClient.getSyncPlayGroups().then(function (response) {
response.json().then(function (groups) {
var menuItems = groups.map(function (group) {
const menuItems = groups.map(function (group) {
return {
name: group.PlayingItemName,
icon: 'group',
@ -72,7 +72,7 @@ function showNewJoinGroupSelection (button, user, apiClient) {
return;
}
var menuOptions = {
const menuOptions = {
title: globalize.translate('HeaderSyncPlaySelectGroup'),
items: menuItems,
positionTo: button,
@ -129,7 +129,7 @@ function showLeaveGroupSelection (button, user, apiClient) {
secondaryText: globalize.translate('LabelSyncPlayLeaveGroupDescription')
}];
var menuOptions = {
const menuOptions = {
title: globalize.translate('HeaderSyncPlayEnabled'),
items: menuItems,
positionTo: button,

View file

@ -40,7 +40,7 @@ function waitForEventOnce(emitter, eventType, timeout) {
* @returns {string} The player's id.
*/
function getActivePlayerId() {
var info = playbackManager.getPlayerInfo();
const info = playbackManager.getPlayerInfo();
return info ? info.id : null;
}
@ -276,7 +276,7 @@ class SyncPlayManager {
* Removes the bindings to the current player's events.
*/
releaseCurrentPlayer () {
var player = this.currentPlayer;
const player = this.currentPlayer;
if (player) {
events.off(player, 'unpause', this._onPlayerUnpause);
events.off(player, 'pause', this._onPlayerPause);
@ -426,7 +426,7 @@ class SyncPlayManager {
serverId: serverId
}).then(() => {
waitForEventOnce(this, 'playbackstart', WaitForEventDefaultTimeout).then(() => {
var sessionId = getActivePlayerId();
const sessionId = getActivePlayerId();
if (!sessionId) {
console.error('Missing sessionId!');
toast({
@ -659,7 +659,7 @@ class SyncPlayManager {
* Overrides PlaybackManager's unpause method.
*/
playRequest (player) {
var apiClient = window.connectionManager.currentApiClient();
const apiClient = window.connectionManager.currentApiClient();
apiClient.requestSyncPlayStart();
}
@ -667,7 +667,7 @@ class SyncPlayManager {
* Overrides PlaybackManager's pause method.
*/
pauseRequest (player) {
var apiClient = window.connectionManager.currentApiClient();
const apiClient = window.connectionManager.currentApiClient();
apiClient.requestSyncPlayPause();
// Pause locally as well, to give the user some little control
playbackManager._localUnpause(player);
@ -677,7 +677,7 @@ class SyncPlayManager {
* Overrides PlaybackManager's seek method.
*/
seekRequest (PositionTicks, player) {
var apiClient = window.connectionManager.currentApiClient();
const apiClient = window.connectionManager.currentApiClient();
apiClient.requestSyncPlaySeek({
PositionTicks: PositionTicks
});

View file

@ -4,7 +4,7 @@ import layoutManager from 'layoutManager';
import 'emby-tabs';
function onViewDestroy(e) {
var tabControllers = this.tabControllers;
const tabControllers = this.tabControllers;
if (tabControllers) {
tabControllers.forEach(function (t) {
@ -32,9 +32,9 @@ class TabbedView {
this.view = view;
this.params = params;
var self = this;
const self = this;
var currentTabIndex = parseInt(params.tab || this.getDefaultTabIndex(params.parentId));
let currentTabIndex = parseInt(params.tab || this.getDefaultTabIndex(params.parentId));
this.initialTabIndex = currentTabIndex;
function validateTabLoad(index) {
@ -44,7 +44,7 @@ class TabbedView {
function loadTab(index, previousIndex) {
validateTabLoad(index).then(function () {
self.getTabController(index).then(function (controller) {
var refresh = !controller.refreshed;
const refresh = !controller.refreshed;
controller.onResume({
autoFocus: previousIndex == null && layoutManager.tv,
@ -64,10 +64,10 @@ class TabbedView {
}
function onTabChange(e) {
var newIndex = parseInt(e.detail.selectedTabIndex);
var previousIndex = e.detail.previousIndex;
const newIndex = parseInt(e.detail.selectedTabIndex);
const previousIndex = e.detail.previousIndex;
var previousTabController = previousIndex == null ? null : self.tabControllers[previousIndex];
const previousTabController = previousIndex == null ? null : self.tabControllers[previousIndex];
if (previousTabController && previousTabController.onPause) {
previousTabController.onPause();
}
@ -92,7 +92,7 @@ class TabbedView {
this.setTitle();
backdrop.clearBackdrop();
var currentTabController = this.currentTabController;
const currentTabController = this.currentTabController;
if (!currentTabController) {
mainTabsManager.selectedTabIndex(this.initialTabIndex);
@ -102,7 +102,7 @@ class TabbedView {
}
onPause() {
var currentTabController = this.currentTabController;
const currentTabController = this.currentTabController;
if (currentTabController && currentTabController.onPause) {
currentTabController.onPause();

View file

@ -1,9 +1,10 @@
.upNextDialog {
.upNextContainer {
position: fixed;
left: 0;
bottom: 0;
right: 0;
padding: 1%;
bottom: 0;
width: 30em;
padding: 1em;
margin: 0 2em 2em 0;
display: flex;
flex-direction: column;
will-change: transform, opacity;
@ -22,13 +23,18 @@
font-weight: 500;
}
.upNextDialog-poster {
max-width: 40%;
max-height: 15%;
position: relative;
margin-right: 1em;
flex-shrink: 0;
margin-bottom: 0.5em;
.upNextDialog-nextVideoText,
.upNextDialog-title {
width: 25.5em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.upNextDialog-buttons {
width: 29.75em;
justify-content: end;
align-content: flex-end;
}
.upNextDialog-button {
@ -40,34 +46,4 @@
.upNextDialog {
flex-direction: row;
}
.upNextDialog-poster {
max-width: initial;
max-height: initial;
width: 10%;
margin-bottom: 0;
}
}
@media all and (max-width: 50em) {
.upNextDialog-overview {
display: none !important;
}
}
.upNextDialog-poster-img {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: auto;
width: 100%;
box-shadow: 0 0.0725em 0.29em 0 rgba(0, 0, 0, 0.37);
user-drag: none;
border: 0;
user-select: none;
-moz-user-select: none;
-webkit-user-drag: none;
-webkit-user-select: none;
-ms-user-select: none;
}

View file

@ -14,84 +14,9 @@ import 'flexStyles';
const transitionEndEventName = dom.whichTransitionEvent();
function seriesImageUrl(item, options) {
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 window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options);
}
}
if (options.type === 'Thumb') {
if (item.SeriesThumbImageTag) {
options.tag = item.SeriesThumbImageTag;
return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options);
}
if (item.ParentThumbImageTag) {
options.tag = item.ParentThumbImageTag;
return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.ParentThumbItemId, options);
}
}
return null;
}
function imageUrl(item, options) {
options = options || {};
options.type = options.type || 'Primary';
if (item.ImageTags && item.ImageTags[options.type]) {
options.tag = item.ImageTags[options.type];
return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.PrimaryImageItemId || item.Id, options);
}
if (options.type === 'Primary') {
if (item.AlbumId && item.AlbumPrimaryImageTag) {
options.tag = item.AlbumPrimaryImageTag;
return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.AlbumId, options);
}
}
return null;
}
function setPoster(osdPoster, item, secondaryItem) {
if (item) {
let imgUrl = seriesImageUrl(item, { type: 'Primary' }) ||
seriesImageUrl(item, { type: 'Thumb' }) ||
imageUrl(item, { type: 'Primary' });
if (!imgUrl && secondaryItem) {
imgUrl = seriesImageUrl(secondaryItem, { type: 'Primary' }) ||
seriesImageUrl(secondaryItem, { type: 'Thumb' }) ||
imageUrl(secondaryItem, { type: 'Primary' });
}
if (imgUrl) {
osdPoster.innerHTML = '<img class="upNextDialog-poster-img" src="' + imgUrl + '" />';
return;
}
}
osdPoster.innerHTML = '';
}
function getHtml() {
let html = '';
html += '<div class="upNextDialog-poster">';
html += '</div>';
html += '<div class="flex flex-direction-column flex-grow">';
html += '<h2 class="upNextDialog-nextVideoText" style="margin:.25em 0;">&nbsp;</h2>';
@ -101,8 +26,6 @@ import 'flexStyles';
html += '<div class="flex flex-direction-row upNextDialog-mediainfo">';
html += '</div>';
html += '<div class="upNextDialog-overview" style="margin-top:1em;"></div>';
html += '<div class="flex flex-direction-row upNextDialog-buttons" style="margin-top:1em;">';
html += '<button type="button" is="emby-button" class="raised raised-mini btnStartNow upNextDialog-button">';
@ -145,11 +68,11 @@ import 'flexStyles';
const 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, {
criticRating: false,
originalAirDate: false,
starRating: false,
subtitles: false
});
let title = itemHelper.getDisplayName(item);

View file

@ -37,53 +37,56 @@ import 'css!components/viewManager/viewContainer';
const newViewInfo = normalizeNewView(options, isPluginpage);
const newView = newViewInfo.elem;
return new Promise((resolve) => {
const currentPage = allPages[pageIndex];
const currentPage = allPages[pageIndex];
if (currentPage) {
triggerDestroy(currentPage);
}
if (currentPage) {
triggerDestroy(currentPage);
}
let view = newView;
let view = newView;
if (typeof view == 'string') {
view = document.createElement('div');
view.innerHTML = newView;
}
if (typeof view == 'string') {
view = document.createElement('div');
view.innerHTML = newView;
}
view.classList.add('mainAnimatedPage');
view.classList.add('mainAnimatedPage');
if (currentPage) {
if (newViewInfo.hasScript && window.$) {
mainAnimatedPages.removeChild(currentPage);
view = $(view).appendTo(mainAnimatedPages)[0];
} else {
mainAnimatedPages.replaceChild(view, currentPage);
}
if (currentPage) {
if (newViewInfo.hasScript && window.$) {
mainAnimatedPages.removeChild(currentPage);
view = $(view).appendTo(mainAnimatedPages)[0];
} else {
if (newViewInfo.hasScript && window.$) {
view = $(view).appendTo(mainAnimatedPages)[0];
} else {
mainAnimatedPages.appendChild(view);
}
mainAnimatedPages.replaceChild(view, currentPage);
}
if (options.type) {
view.setAttribute('data-type', options.type);
} else {
if (newViewInfo.hasScript && window.$) {
view = $(view).appendTo(mainAnimatedPages)[0];
} else {
mainAnimatedPages.appendChild(view);
}
}
const properties = [];
if (options.type) {
view.setAttribute('data-type', options.type);
}
if (options.fullscreen) {
properties.push('fullscreen');
}
const properties = [];
if (properties.length) {
view.setAttribute('data-properties', properties.join(','));
}
if (options.fullscreen) {
properties.push('fullscreen');
}
allPages[pageIndex] = view;
setControllerClass(view, options).then(() => {
if (properties.length) {
view.setAttribute('data-properties', properties.join(','));
}
allPages[pageIndex] = view;
return setControllerClass(view, options)
// Timeout for polyfilled CustomElements (webOS 1.2)
.then(() => new Promise((resolve) => setTimeout(resolve, 0)))
.then(() => {
if (onBeforeChange) {
onBeforeChange(view, false, options);
}
@ -101,9 +104,8 @@ import 'css!components/viewManager/viewContainer';
$.mobile.activePage = view;
}
resolve(view);
return view;
});
});
}
}

View file

@ -1 +0,0 @@
config.template.json

46
src/config.json Normal file
View file

@ -0,0 +1,46 @@
{
"multiserver": false,
"themes": [
{
"name": "Apple TV",
"id": "appletv"
}, {
"name": "Blue Radiance",
"id": "blueradiance"
}, {
"name": "Dark",
"id": "dark",
"default": true
}, {
"name": "Light",
"id": "light"
}, {
"name": "Purple Haze",
"id": "purplehaze"
}, {
"name": "WMC",
"id": "wmc"
}
],
"fonts": [
"https://repo.jellyfin.org/releases/other/jellyfin-noto/font-faces.css",
"https://repo.jellyfin.org/releases/other/jellyfin-noto/css/KR.css",
"https://repo.jellyfin.org/releases/other/jellyfin-noto/css/JP.css",
"https://repo.jellyfin.org/releases/other/jellyfin-noto/css/SC.css"
],
"plugins": [
"plugins/playAccessValidation/plugin",
"plugins/experimentalWarnings/plugin",
"plugins/htmlAudioPlayer/plugin",
"plugins/htmlVideoPlayer/plugin",
"plugins/photoPlayer/plugin",
"plugins/comicsPlayer/plugin",
"plugins/bookPlayer/plugin",
"plugins/youtubePlayer/plugin",
"plugins/backdropScreensaver/plugin",
"plugins/pdfPlayer/plugin",
"plugins/logoScreensaver/plugin",
"plugins/sessionPlayer/plugin",
"plugins/chromecastPlayer/plugin"
]
}

View file

@ -1,39 +0,0 @@
{
"multiserver": false,
"themes": [
{
"name": "Apple TV",
"id": "appletv"
}, {
"name": "Blue Radiance",
"id": "blueradiance"
}, {
"name": "Dark",
"id": "dark",
"default": true
}, {
"name": "Light",
"id": "light"
}, {
"name": "Purple Haze",
"id": "purplehaze"
}, {
"name": "WMC",
"id": "wmc"
}
],
"plugins": [
"plugins/playAccessValidation/plugin",
"plugins/experimentalWarnings/plugin",
"plugins/htmlAudioPlayer/plugin",
"plugins/htmlVideoPlayer/plugin",
"plugins/photoPlayer/plugin",
"plugins/comicsPlayer/plugin",
"plugins/bookPlayer/plugin",
"plugins/youtubePlayer/plugin",
"plugins/backdropScreensaver/plugin",
"plugins/logoScreensaver/plugin",
"plugins/sessionPlayer/plugin",
"plugins/chromecastPlayer/plugin"
]
}

View file

@ -16,6 +16,9 @@
</div>
<div style="margin-top:1em;">
<button is="emby-button" type="button" class="raised btnRefresh">
<span>${ButtonScanAllLibraries}</span>
</button>
<button is="emby-button" type="button" id="btnRestartServer" class="raised" onclick="DashboardPage.restart(this);" style="margin-left:0;">
<span>${Restart}</span>
</button>

View file

@ -3,6 +3,7 @@ import events from 'events';
import itemHelper from 'itemHelper';
import serverNotifications from 'serverNotifications';
import dom from 'dom';
import taskButton from 'scripts/taskbutton';
import globalize from 'globalize';
import * as datefns from 'date-fns';
import dfnshelper from 'dfnshelper';
@ -550,13 +551,13 @@ import 'emby-itemscontainer';
row.classList.remove('playingSession');
}
if (session.ServerId && session.SupportedCommands.indexOf('DisplayMessage') !== -1 && session.DeviceId !== window.connectionManager.deviceId()) {
if (session.ServerId && session.SupportedCommands.indexOf('DisplayMessage') !== -1) {
row.querySelector('.btnSessionSendMessage').classList.remove('hide');
} else {
row.querySelector('.btnSessionSendMessage').classList.add('hide');
}
if (session.TranscodingInfo && session.TranscodingInfo.TranscodeReasons && session.TranscodingInfo && session.TranscodingInfo.TranscodeReasons.length) {
if (session.TranscodingInfo && session.TranscodingInfo.TranscodeReasons && session.TranscodingInfo) {
row.querySelector('.btnSessionInfo').classList.remove('hide');
} else {
row.querySelector('.btnSessionInfo').classList.add('hide');
@ -564,7 +565,7 @@ import 'emby-itemscontainer';
const btnSessionPlayPause = row.querySelector('.btnSessionPlayPause');
if (session.ServerId && nowPlayingItem && session.SupportsRemoteControl && session.DeviceId !== window.connectionManager.deviceId()) {
if (session.ServerId && nowPlayingItem && session.SupportsRemoteControl) {
btnSessionPlayPause.classList.remove('hide');
row.querySelector('.btnSessionStop').classList.remove('hide');
} else {
@ -827,9 +828,17 @@ import 'emby-itemscontainer';
refreshActiveRecordings(view, apiClient);
loading.hide();
}
taskButton({
mode: 'on',
taskKey: 'RefreshLibrary',
button: page.querySelector('.btnRefresh')
});
});
view.addEventListener('viewbeforehide', function () {
const apiClient = ApiClient;
const page = this;
events.off(serverNotifications, 'RestartRequired', onRestartRequired);
events.off(serverNotifications, 'ServerShuttingDown', onServerShuttingDown);
events.off(serverNotifications, 'ServerRestarting', onServerRestarting);
@ -841,6 +850,12 @@ import 'emby-itemscontainer';
if (apiClient) {
DashboardPage.stopInterval(apiClient);
}
taskButton({
mode: 'off',
taskKey: 'RefreshLibrary',
button: page.querySelector('.btnRefresh')
});
});
view.addEventListener('viewdestroy', function () {
const page = this;

View file

@ -5,6 +5,7 @@
<div class="sectionTitleContainer sectionTitleContainer-cards flex align-items-center">
<h2 class="sectionTitle sectionTitle-cards">${HeaderDevices}</h2>
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/devices.html">${Help}</a>
<button id="deviceDeleteAll" is="emby-button" type="button" class="raised button-alt">${DeleteAll}</button>
</div>
</div>
<div is="emby-itemscontainer" class="devicesList vertical-wrap" data-multiselect="false"></div>

View file

@ -10,10 +10,32 @@ import 'cardStyle';
/* eslint-disable indent */
// Local cache of loaded
let deviceIds = [];
function canDelete(deviceId) {
return deviceId !== ApiClient.deviceId();
}
function deleteAllDevices(page) {
const msg = globalize.translate('DeleteDevicesConfirmation');
require(['confirm'], async function (confirm) {
await confirm({
text: msg,
title: globalize.translate('HeaderDeleteDevices'),
confirmText: globalize.translate('ButtonDelete'),
primary: 'delete'
});
loading.show();
await Promise.all(
deviceIds.filter(canDelete).map((id) => ApiClient.deleteDevice(id))
);
loadData(page);
});
}
function deleteDevice(page, id) {
const msg = globalize.translate('DeleteDeviceConfirmation');
@ -23,16 +45,10 @@ import 'cardStyle';
title: globalize.translate('HeaderDeleteDevice'),
confirmText: globalize.translate('Delete'),
primary: 'delete'
}).then(function () {
}).then(async () => {
loading.show();
ApiClient.ajax({
type: 'DELETE',
url: ApiClient.getUrl('Devices', {
Id: id
})
}).then(function () {
loadData(page);
});
await ApiClient.deleteDevice(id);
loadData(page);
});
});
}
@ -129,6 +145,7 @@ import 'cardStyle';
loading.show();
ApiClient.getJSON(ApiClient.getUrl('Devices')).then(function (result) {
load(page, result.Items);
deviceIds = result.Items.map((device) => device.Id);
loading.hide();
});
}
@ -145,6 +162,9 @@ import 'cardStyle';
view.addEventListener('viewshow', function () {
loadData(this);
});
}
view.querySelector('#deviceDeleteAll').addEventListener('click', function() {
deleteAllDevices(view);
});
}
/* eslint-enable indent */

View file

@ -9,21 +9,21 @@ import 'flexStyles';
export default function(view, params) {
view.addEventListener('viewbeforeshow', function() {
loading.show();
var apiClient = ApiClient;
const apiClient = ApiClient;
apiClient.getJSON(apiClient.getUrl('System/Logs')).then(function(logs) {
var html = '';
let html = '';
html += '<div class="paperList">';
html += logs.map(function(log) {
var logUrl = apiClient.getUrl('System/Logs/Log', {
let logUrl = apiClient.getUrl('System/Logs/Log', {
name: log.Name
});
logUrl += '&api_key=' + apiClient.accessToken();
var logHtml = '';
let logHtml = '';
logHtml += '<a is="emby-linkbutton" href="' + logUrl + '" target="_blank" class="listItem listItem-border" style="color:inherit;">';
logHtml += '<div class="listItemBody two-line">';
logHtml += "<h3 class='listItemBodyText'>" + log.Name + '</h3>';
var date = datetime.parseISO8601Date(log.DateModified, true);
var text = datetime.toLocaleDateString(date);
const date = datetime.parseISO8601Date(log.DateModified, true);
let text = datetime.toLocaleDateString(date);
text += ' ' + datetime.getDisplayTime(date);
logHtml += '<div class="listItemBodyText secondary">' + text + '</div>';
logHtml += '</div>';

View file

@ -42,10 +42,10 @@ function saveList(page) {
}
function populateList(options) {
var html = '';
let html = '';
html += '<div class="paperList">';
for (var i = 0; i < options.repositories.length; i++) {
for (let i = 0; i < options.repositories.length; i++) {
html += getRepositoryHtml(options.repositories[i]);
}
@ -59,7 +59,7 @@ function populateList(options) {
}
function getRepositoryHtml(repository) {
var html = '';
let html = '';
html += '<div class="listItem listItem-border">';
html += `<a is="emby-linkbutton" style="margin:0;padding:0" class="clearLink listItemIconContainer" href="${repository.Url}">`;
@ -93,9 +93,9 @@ export default function(view, params) {
libraryMenu.setTabs('plugins', 2, getTabs);
reloadList(this);
var save = this;
const save = this;
$('#repositories', view).on('click', '.btnDelete', function() {
var button = this;
const button = this;
repositories = repositories.filter(function (r) {
return r.Url !== button.id;
});

View file

@ -140,32 +140,13 @@
</div>
<div class="fieldDescription">${OptionAllowRemoteSharedDevicesHelp}</div>
</div>
<div class="verticalSection">
<h2 class="checkboxListLabel">${HeaderDownloadSync}</h2>
<div class="checkboxList paperList" style="padding:.5em 1em;">
<label>
<input type="checkbox" is="emby-checkbox" id="chkEnableDownloading" />
<span>${OptionAllowContentDownloading}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" id="chkEnableSyncTranscoding" />
<span>${OptionAllowSyncTranscoding}</span>
</label>
</div>
</div>
<h2 class="checkboxListLabel">${Other}</h2>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" id="chkEnableConversion" />
<span>${AllowMediaConversion}</span>
<input type="checkbox" is="emby-checkbox" id="chkEnableDownloading" />
<span>${OptionAllowContentDownload}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${AllowMediaConversionHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" id="chkEnableSharing" />
<span>${OptionAllowLinkSharing}</span>
</label>
<div class="fieldDescription checkboxFieldDescription sharingHelp"></div>
<div class="fieldDescription checkboxFieldDescription">${OptionAllowContentDownloadHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription" id="fldIsEnabled">
<label>
@ -190,6 +171,14 @@
</div>
</div>
<br />
<div class=verticalSection>
<div class="inputContainer" id="fldMaxActiveSessions">
<input is="emby-input" type="number" id="txtMaxActiveSessions" min="0" step="1" label="${LabelUserMaxActiveSessions}"/>
<div class="fieldDescription">${OptionMaxActiveSessions}</div>
<div class="fieldDescription">${OptionMaxActiveSessionsHelp}</div>
</div>
</div>
<br />
<div>
<button is="emby-button" type="submit" class="raised button-submit block">
<span>${Save}</span>

View file

@ -97,11 +97,9 @@ import globalize from 'globalize';
$('#chkEnableVideoPlaybackRemuxing', page).prop('checked', user.Policy.EnablePlaybackRemuxing);
$('#chkForceRemoteSourceTranscoding', page).prop('checked', user.Policy.ForceRemoteSourceTranscoding);
$('#chkRemoteAccess', page).prop('checked', user.Policy.EnableRemoteAccess == null || user.Policy.EnableRemoteAccess);
$('#chkEnableSyncTranscoding', page).prop('checked', user.Policy.EnableSyncTranscoding);
$('#chkEnableConversion', page).prop('checked', user.Policy.EnableMediaConversion || false);
$('#chkEnableSharing', page).prop('checked', user.Policy.EnablePublicSharing);
$('#txtRemoteClientBitrateLimit', page).val(user.Policy.RemoteClientBitrateLimit / 1e6 || '');
$('#txtLoginAttemptsBeforeLockout', page).val(user.Policy.LoginAttemptsBeforeLockout || '0');
$('#txtMaxActiveSessions', page).val(user.Policy.MaxActiveSessions || '0');
if (ApiClient.isMinServerVersion('10.6.0')) {
$('#selectSyncPlayAccess').val(user.Policy.SyncPlayAccess);
}
@ -132,12 +130,10 @@ import globalize from 'globalize';
user.Policy.EnablePlaybackRemuxing = $('#chkEnableVideoPlaybackRemuxing', page).is(':checked');
user.Policy.ForceRemoteSourceTranscoding = $('#chkForceRemoteSourceTranscoding', page).is(':checked');
user.Policy.EnableContentDownloading = $('#chkEnableDownloading', page).is(':checked');
user.Policy.EnableSyncTranscoding = $('#chkEnableSyncTranscoding', page).is(':checked');
user.Policy.EnableMediaConversion = $('#chkEnableConversion', page).is(':checked');
user.Policy.EnablePublicSharing = $('#chkEnableSharing', page).is(':checked');
user.Policy.EnableRemoteAccess = $('#chkRemoteAccess', page).is(':checked');
user.Policy.RemoteClientBitrateLimit = parseInt(1e6 * parseFloat($('#txtRemoteClientBitrateLimit', page).val() || '0'));
user.Policy.LoginAttemptsBeforeLockout = parseInt($('#txtLoginAttemptsBeforeLockout', page).val() || '0');
user.Policy.MaxActiveSessions = parseInt($('#txtMaxActiveSessions', page).val() || '0');
user.Policy.AuthenticationProviderId = page.querySelector('.selectLoginProvider').value;
user.Policy.PasswordResetProviderId = page.querySelector('.selectPasswordResetProvider').value;
user.Policy.EnableContentDeletion = $('#chkEnableDeleteAllFolders', page).is(':checked');

View file

@ -51,7 +51,9 @@ import globalize from 'globalize';
$('.channelAccessContainer', page).hide();
}
$('#chkEnableAllChannels', page).prop('checked', user.Policy.EnableAllChannels);
const chkEnableAllChannels = page.querySelector('#chkEnableAllChannels');
chkEnableAllChannels.checked = user.Policy.EnableAllChannels;
triggerChange(chkEnableAllChannels);
}
function loadDevices(page, user, devices) {
@ -67,7 +69,9 @@ import globalize from 'globalize';
html += '</div>';
$('.deviceAccess', page).show().html(html);
$('#chkEnableAllDevices', page).prop('checked', user.Policy.EnableAllDevices);
const chkEnableAllDevices = page.querySelector('#chkEnableAllDevices');
chkEnableAllDevices.checked = user.Policy.EnableAllDevices;
triggerChange(chkEnableAllDevices);
if (user.Policy.IsAdministrator) {
page.querySelector('.deviceAccessContainer').classList.add('hide');

View file

@ -20,7 +20,7 @@ export default function (view, params) {
});
MetadataEditor.setCurrentItemId(null);
view.querySelector('.libraryTree').addEventListener('itemclicked', function (event) {
var data = event.detail;
const data = event.detail;
if (data.id != MetadataEditor.getCurrentItemId()) {
MetadataEditor.setCurrentItemId(data.id);

View file

@ -1,6 +1,6 @@
<div id="moviesPage" data-role="page" data-dom-cache="true" class="page libraryPage backdropPage collectionEditorPage pageWithAbsoluteTabs withTabs" data-backdroptype="movie">
<div class="pageTabContent" data-index="0">
<div class="pageTabContent" id="moviesTab" data-index="0">
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
<div class="paging"></div>
<button is="paper-icon-button-light" class="btnSelectView autoSize" title="${ButtonSelectView}"><span class="material-icons view_comfy"></span></button>
@ -17,7 +17,7 @@
<div class="paging"></div>
</div>
</div>
<div class="pageTabContent" data-index="1">
<div class="pageTabContent" id="suggestionsTab" data-index="1">
<div id="resumableSection" class="verticalSection hide">
<div class="sectionTitleContainer sectionTitleContainer-cards">
<h2 class="sectionTitle sectionTitle-cards padded-left">${HeaderContinueWatching}</h2>
@ -43,7 +43,7 @@
<p>${MessageNoMovieSuggestionsAvailable}</p>
</div>
</div>
<div class="pageTabContent" data-index="2">
<div class="pageTabContent" id="trailersTab" data-index="2">
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
<div class="paging"></div>
<button is="paper-icon-button-light" class="btnSort autoSize" title="${Sort}"><span class="material-icons sort_by_alpha"></span></button>
@ -59,7 +59,7 @@
<div class="paging"></div>
</div>
</div>
<div class="pageTabContent" data-index="3">
<div class="pageTabContent" id="favoritesTab" data-index="3">
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
<div class="paging"></div>
<button is="paper-icon-button-light" class="btnSelectView autoSize" title="${ButtonSelectView}"><span class="material-icons view_comfy"></span></button>
@ -71,7 +71,7 @@
<div class="paging"></div>
</div>
</div>
<div class="pageTabContent" data-index="4">
<div class="pageTabContent" id="collectionsTab" data-index="4">
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
<div class="paging"></div>
<button is="paper-icon-button-light" class="btnSelectView autoSize" title="${ButtonSelectView}"><span class="material-icons view_comfy"></span></button>
@ -85,9 +85,7 @@
<div class="paging"></div>
</div>
</div>
<div class="pageTabContent" data-index="5">
<div class="pageTabContent" id="genresTab" data-index="5">
<div id="items"></div>
</div>
<div class="pageTabContent" data-index="6">
</div>
</div>

View file

@ -320,11 +320,6 @@ import 'emby-button';
if (index === suggestionsTabIndex) {
controller = this;
} else if (index === 6) {
controller = new controllerFactory(view, tabContent, {
collectionType: 'movies',
parentId: params.topParentId
});
} else if (index == 0 || index == 3) {
controller = new controllerFactory(view, params, tabContent, {
mode: index ? 'favorites' : 'movies'
@ -395,7 +390,7 @@ import 'emby-button';
view.addEventListener('viewshow', function (e) {
initTabs();
if (!view.getAttribute('data-title')) {
var parentId = params.topParentId;
const parentId = params.topParentId;
if (parentId) {
ApiClient.getItem(ApiClient.getCurrentUserId(), parentId).then(function (item) {

View file

@ -8,7 +8,26 @@
}
}
</style>
<div class="pageTabContent pageTabContent" id="suggestionsTab" data-index="0">
<div class="pageTabContent pageTabContent" id="albumsTab" data-index="0">
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
<div class="paging"></div>
<button is="paper-icon-button-light" class="btnPlayAll musicglobalButton" title="${HeaderPlayAll}"><span class="material-icons play_arrow"></span></button>
<button is="paper-icon-button-light" class="btnShuffle musicglobalButton" title="${Shuffle}"><span class="material-icons shuffle"></span></button>
<button is="paper-icon-button-light" class="btnSelectView autoSize" title="${ButtonSelectView}"><span class="material-icons view_comfy"></span></button>
<button is="paper-icon-button-light" class="btnSort autoSize" title="${Sort}"><span class="material-icons sort_by_alpha"></span></button>
<button is="paper-icon-button-light" class="btnFilter autoSize" title="${Filter}"><span class="material-icons filter_list"></span></button>
</div>
<div class="alphaPicker alphaPicker-fixed alphaPicker-vertical">
</div>
<div is="emby-itemscontainer" class="itemsContainer padded-left padded-right">
</div>
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
<div class="paging"></div>
</div>
</div>
<div class="pageTabContent pageTabContent" id="suggestionsTab" data-index="1">
<div class="verticalSection">
@ -34,25 +53,6 @@
<div class="favoriteSections verticalSection"></div>
</div>
<div class="pageTabContent pageTabContent" id="albumsTab" data-index="1">
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
<div class="paging"></div>
<button is="paper-icon-button-light" class="btnPlayAll musicglobalButton" title="${HeaderPlayAll}"><span class="material-icons play_arrow"></span></button>
<button is="paper-icon-button-light" class="btnShuffle musicglobalButton" title="${Shuffle}"><span class="material-icons shuffle"></span></button>
<button is="paper-icon-button-light" class="btnSelectView autoSize" title="${ButtonSelectView}"><span class="material-icons view_comfy"></span></button>
<button is="paper-icon-button-light" class="btnSort autoSize" title="${Sort}"><span class="material-icons sort_by_alpha"></span></button>
<button is="paper-icon-button-light" class="btnFilter autoSize" title="${Filter}"><span class="material-icons filter_list"></span></button>
</div>
<div class="alphaPicker alphaPicker-fixed alphaPicker-vertical">
</div>
<div is="emby-itemscontainer" class="itemsContainer padded-left padded-right">
</div>
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
<div class="paging"></div>
</div>
</div>
<div class="pageTabContent" id="albumArtistsTab" data-index="2">
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
<div class="paging"></div>
@ -85,7 +85,7 @@
<div class="paging"></div>
</div>
</div>
<div class="pageTabContent" data-index="4">
<div class="pageTabContent" id="playlistsTab" data-index="4">
<div is="emby-itemscontainer" id="items" class="itemsContainer padded-left padded-right padded-top vertical-wrap centered"></div>
</div>
@ -105,6 +105,4 @@
<div class="pageTabContent" id="genresTab" data-index="6">
<div is="emby-itemscontainer" id="items" class="itemsContainer padded-left padded-right padded-top vertical-wrap"></div>
</div>
<div class="pageTabContent" data-index="7">
</div>
</div>

View file

@ -56,7 +56,7 @@ import 'flexStyles';
EnableTotalRecordCount: false
};
ApiClient.getJSON(ApiClient.getUrl('Users/' + userId + '/Items/Latest', options)).then(function (items) {
var elem = page.querySelector('#recentlyAddedSongs');
const elem = page.querySelector('#recentlyAddedSongs');
elem.innerHTML = cardBuilder.getCardsHtml({
items: items,
showUnplayedIndicator: false,
@ -103,7 +103,7 @@ import 'flexStyles';
elem.classList.add('hide');
}
var itemsContainer = elem.querySelector('.itemsContainer');
const itemsContainer = elem.querySelector('.itemsContainer');
itemsContainer.innerHTML = cardBuilder.getCardsHtml({
items: result.Items,
showUnplayedIndicator: false,
@ -145,7 +145,7 @@ import 'flexStyles';
elem.classList.add('hide');
}
var itemsContainer = elem.querySelector('.itemsContainer');
const itemsContainer = elem.querySelector('.itemsContainer');
itemsContainer.innerHTML = cardBuilder.getCardsHtml({
items: result.Items,
showUnplayedIndicator: false,
@ -177,9 +177,9 @@ import 'flexStyles';
function getTabs() {
return [{
name: globalize.translate('Suggestions')
}, {
name: globalize.translate('Albums')
}, {
name: globalize.translate('Suggestions')
}, {
name: globalize.translate('HeaderAlbumArtists')
}, {
@ -195,7 +195,7 @@ import 'flexStyles';
function getDefaultTabIndex(folderId) {
switch (userSettings.get('landing-' + folderId)) {
case 'albums':
case 'suggestions':
return 1;
case 'albumartists':
@ -221,7 +221,7 @@ import 'flexStyles';
export default function (view, params) {
function reload() {
loading.show();
const tabContent = view.querySelector(".pageTabContent[data-index='0']");
const tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
loadSuggestionsTab(view, tabContent, params.topParentId);
}
@ -268,11 +268,11 @@ import 'flexStyles';
switch (index) {
case 0:
depends = 'controllers/music/musicrecommended';
depends = 'controllers/music/musicalbums';
break;
case 1:
depends = 'controllers/music/musicalbums';
depends = 'controllers/music/musicrecommended';
break;
case 2:
@ -296,7 +296,7 @@ import 'flexStyles';
import(depends).then(({default: controllerFactory}) => {
let tabContent;
if (index == 0) {
if (index == 1) {
tabContent = view.querySelector(".pageTabContent[data-index='" + index + "']");
this.tabContent = tabContent;
}
@ -306,13 +306,8 @@ import 'flexStyles';
if (!controller) {
tabContent = view.querySelector(".pageTabContent[data-index='" + index + "']");
if (index === 0) {
if (index === 1) {
controller = this;
} else if (index === 7) {
controller = new controllerFactory(view, tabContent, {
collectionType: 'music',
parentId: params.topParentId
});
} else {
controller = new controllerFactory(view, params, tabContent);
}
@ -360,9 +355,10 @@ import 'flexStyles';
}
let currentTabIndex = parseInt(params.tab || getDefaultTabIndex(params.topParentId));
const suggestionsTabIndex = 1;
this.initTab = function () {
const tabContent = view.querySelector(".pageTabContent[data-index='0']");
const tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
const containers = tabContent.querySelectorAll('.itemsContainer');
for (let i = 0, length = containers.length; i < length; i++) {

View file

@ -1,11 +1,9 @@
<div id="videoOsdPage" data-role="page" class="page libraryPage" data-backbutton="true">
<div class="upNextContainer hide"></div>
<div class="videoOsdBottom videoOsdBottom-maincontrols">
<div class="osdPoster"></div>
<div class="osdControls">
<div class="osdTextContainer osdMainTextContainer">
<h3 class="osdTitle"></h3>
<div class="osdMediaInfo"></div>
<div class="osdMediaStatus hide">
<span class="material-icons animate autorenew"></span>
<span>${FetchingData}</span>
@ -16,7 +14,7 @@
<div class="flex flex-direction-row align-items-center">
<div class="osdTextContainer startTimeText" style="margin: 0 .25em 0 0;"></div>
<div class="sliderContainer flex-grow" style="margin: .5em .5em .25em;">
<div class="sliderContainer flex-grow" style="margin: .5em 0 .25em;">
<input type="range" step=".01" min="0" max="100" value="0" is="emby-slider" class="osdPositionSlider" data-slider-keep-progress="true" />
</div>
<div class="osdTextContainer endTimeText" style="margin: 0 0 0 .25em;"></div>
@ -47,32 +45,18 @@
<span class="xlargePaperIconButton material-icons skip_next"></span>
</button>
<button is="paper-icon-button-light" class="btnAudio hide autoSize" title="${Audio}">
<span class="xlargePaperIconButton material-icons audiotrack"></span>
</button>
<button is="paper-icon-button-light" class="btnSubtitles hide autoSize" title="${Subtitles}">
<span class="xlargePaperIconButton material-icons closed_caption"></span>
</button>
<button is="paper-icon-button-light" class="btnVideoOsdSettings hide autoSize" title="${Settings}">
<span class="largePaperIconButton material-icons settings"></span>
</button>
<button is="paper-icon-button-light" class="btnFullscreen hide autoSize" title="${Fullscreen} (f)">
<span class="xlargePaperIconButton material-icons fullscreen"></span>
</button>
<button is="paper-icon-button-light" class="btnPip hide autoSize" title="${PictureInPicture}">
<span class="xlargePaperIconButton material-icons picture_in_picture_alt"></span>
</button>
<button is="paper-icon-button-light" class="btnAirPlay hide autoSize" title="${AirPlay}">
<span class="xlargePaperIconButton material-icons airplay"></span>
</button>
<div class="osdTimeText">
<span class="osdPositionText"></span>
<span class="osdDurationText"></span>
<span class="endsAtText"></span>
</div>
<button is="paper-icon-button-light" class="btnSubtitles hide autoSize" title="${Subtitles}">
<span class="xlargePaperIconButton material-icons closed_caption"></span>
</button>
<button is="paper-icon-button-light" class="btnAudio hide autoSize" title="${Audio}">
<span class="xlargePaperIconButton material-icons audiotrack"></span>
</button>
<div class="volumeButtons hide-mouse-idle-tv">
<button is="paper-icon-button-light" class="buttonMute autoSize" title="${Mute} (m)">
<span class="xlargePaperIconButton material-icons volume_up"></span>
@ -81,6 +65,18 @@
<input is="emby-slider" type="range" step="1" min="0" max="100" value="0" class="osdVolumeSlider" />
</div>
</div>
<button is="paper-icon-button-light" class="btnVideoOsdSettings hide autoSize" title="${Settings}">
<span class="largePaperIconButton material-icons settings"></span>
</button>
<button is="paper-icon-button-light" class="btnAirPlay hide autoSize" title="${AirPlay}">
<span class="xlargePaperIconButton material-icons airplay"></span>
</button>
<button is="paper-icon-button-light" class="btnPip hide autoSize" title="${PictureInPicture}">
<span class="xlargePaperIconButton material-icons picture_in_picture_alt"></span>
</button>
<button is="paper-icon-button-light" class="btnFullscreen hide autoSize" title="${Fullscreen} (f)">
<span class="xlargePaperIconButton material-icons fullscreen"></span>
</button>
</div>
</div>
</div>

View file

@ -20,50 +20,6 @@ import 'css!assets/css/videoosd';
/* eslint-disable indent */
function seriesImageUrl(item, options) {
if (item.Type !== 'Episode') {
return null;
}
options = options || {};
options.type = options.type || 'Primary';
if (options.type === 'Primary' && item.SeriesPrimaryImageTag) {
options.tag = item.SeriesPrimaryImageTag;
return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options);
}
if (options.type === 'Thumb') {
if (item.SeriesThumbImageTag) {
options.tag = item.SeriesThumbImageTag;
return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options);
}
if (item.ParentThumbImageTag) {
options.tag = item.ParentThumbImageTag;
return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.ParentThumbItemId, options);
}
}
return null;
}
function imageUrl(item, options) {
options = options || {};
options.type = options.type || 'Primary';
if (item.ImageTags && item.ImageTags[options.type]) {
options.tag = item.ImageTags[options.type];
return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.PrimaryImageItemId || item.Id, options);
}
if (options.type === 'Primary' && item.AlbumId && item.AlbumPrimaryImageTag) {
options.tag = item.AlbumPrimaryImageTag;
return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.AlbumId, options);
}
return null;
}
function getOpenedDialog() {
return document.querySelector('.dialogContainer .dialog.opened');
}
@ -163,7 +119,6 @@ import 'css!assets/css/videoosd';
currentItem = item;
const displayItem = itemInfo.displayItem || item;
updateRecordingButton(displayItem);
setPoster(displayItem, item);
let parentName = displayItem.SeriesName || displayItem.Album;
if (displayItem.EpisodeTitle || displayItem.IsSeries) {
@ -171,42 +126,6 @@ import 'css!assets/css/videoosd';
}
setTitle(displayItem, parentName);
const titleElement = view.querySelector('.osdTitle');
let displayName = itemHelper.getDisplayName(displayItem, {
includeParentInfo: displayItem.Type !== 'Program',
includeIndexNumber: displayItem.Type !== 'Program'
});
if (!displayName) {
displayName = displayItem.Type;
}
titleElement.innerHTML = displayName;
if (displayName) {
titleElement.classList.remove('hide');
} else {
titleElement.classList.add('hide');
}
const mediaInfoHtml = mediaInfo.getPrimaryMediaInfoHtml(displayItem, {
runtime: false,
subtitles: false,
tomatoes: false,
endsAt: false,
episodeTitle: false,
originalAirDate: displayItem.Type !== 'Program',
episodeTitleIndexNumber: displayItem.Type !== 'Program',
programIndicator: false
});
const osdMediaInfo = view.querySelector('.osdMediaInfo');
osdMediaInfo.innerHTML = mediaInfoHtml;
if (mediaInfoHtml) {
osdMediaInfo.classList.remove('hide');
} else {
osdMediaInfo.classList.add('hide');
}
const secondaryMediaInfo = view.querySelector('.osdSecondaryMediaInfo');
const secondaryMediaInfoHtml = mediaInfo.getSecondaryMediaInfoHtml(displayItem, {
@ -221,12 +140,6 @@ import 'css!assets/css/videoosd';
secondaryMediaInfo.classList.add('hide');
}
if (displayName) {
view.querySelector('.osdMainTextContainer').classList.remove('hide');
} else {
view.querySelector('.osdMainTextContainer').classList.add('hide');
}
if (enableProgressByTimeOfDay) {
setDisplayTime(startTimeText, displayItem.StartDate);
setDisplayTime(endTimeText, displayItem.EndDate);
@ -276,7 +189,6 @@ import 'css!assets/css/videoosd';
currentItem = item;
if (!item) {
setPoster(null);
updateRecordingButton(null);
Emby.Page.setTitle('');
nowPlayingVolumeSlider.disabled = true;
@ -313,7 +225,20 @@ import 'css!assets/css/videoosd';
}
function setTitle(item, parentName) {
Emby.Page.setTitle(parentName || '');
let itemName = itemHelper.getDisplayName(item, {
includeParentInfo: item.Type !== 'Program',
includeIndexNumber: item.Type !== 'Program'
});
if (itemName && parentName) {
itemName = `${parentName} - ${itemName}`;
}
if (!itemName) {
itemName = parentName || '';
}
Emby.Page.setTitle(itemName);
const documentTitle = parentName || (item ? item.Name : null);
@ -322,38 +247,6 @@ import 'css!assets/css/videoosd';
}
}
function setPoster(item, secondaryItem) {
const osdPoster = view.querySelector('.osdPoster');
if (item) {
let imgUrl = seriesImageUrl(item, {
maxWidth: osdPoster.clientWidth,
type: 'Primary'
}) || seriesImageUrl(item, {
maxWidth: osdPoster.clientWidth,
type: 'Thumb'
}) || imageUrl(item, {
maxWidth: osdPoster.clientWidth,
type: 'Primary'
});
if (!imgUrl && secondaryItem && (imgUrl = seriesImageUrl(secondaryItem, {
maxWidth: osdPoster.clientWidth,
type: 'Primary'
}) || seriesImageUrl(secondaryItem, {
maxWidth: osdPoster.clientWidth,
type: 'Thumb'
}) || imageUrl(secondaryItem, {
maxWidth: osdPoster.clientWidth,
type: 'Primary'
})), imgUrl) {
return void (osdPoster.innerHTML = '<img src="' + imgUrl + '" />');
}
}
osdPoster.innerHTML = '';
}
let mouseIsDown = false;
function showOsd() {

View file

@ -16,7 +16,7 @@ import 'emby-checkbox';
function authenticateUserByName(page, apiClient, username, password) {
loading.show();
apiClient.authenticateUserByName(username, password).then(function (result) {
var user = result.User;
const user = result.User;
loading.hide();
onLoginSuccessful(user.Id, result.AccessToken, apiClient);

View file

@ -1,68 +0,0 @@
import loading from 'loading';
import groupedcards from 'components/groupedcards';
import cardBuilder from 'cardBuilder';
import imageLoader from 'imageLoader';
/* eslint-disable indent */
function getLatestPromise(context, params) {
loading.show();
const userId = ApiClient.getCurrentUserId();
const parentId = params.topParentId;
const options = {
IncludeItemTypes: 'Episode',
Limit: 30,
Fields: 'PrimaryImageAspectRatio,BasicSyncInfo',
ParentId: parentId,
ImageTypeLimit: 1,
EnableImageTypes: 'Primary,Backdrop,Thumb'
};
return ApiClient.getJSON(ApiClient.getUrl('Users/' + userId + '/Items/Latest', options));
}
function loadLatest(context, params, promise) {
promise.then(function (items) {
let html = '';
html += cardBuilder.getCardsHtml({
items: items,
shape: 'backdrop',
preferThumb: true,
showTitle: true,
showSeriesYear: true,
showParentTitle: true,
overlayText: false,
cardLayout: false,
showUnplayedIndicator: false,
showChildCountIndicator: true,
centerText: true,
lazy: true,
overlayPlayButton: true,
lines: 2
});
const elem = context.querySelector('#latestEpisodes');
elem.innerHTML = html;
imageLoader.lazyChildren(elem);
loading.hide();
import('autoFocuser').then(({default: autoFocuser}) => {
autoFocuser.autoFocus(context);
});
});
}
export default function (view, params, tabContent) {
const self = this;
let latestPromise;
self.preRender = function () {
latestPromise = getLatestPromise(view, params);
};
self.renderTab = function () {
loadLatest(tabContent, params, latestPromise);
};
tabContent.querySelector('#latestEpisodes').addEventListener('click', groupedcards);
}
/* eslint-enable indent */

View file

@ -23,8 +23,15 @@
<div is="emby-itemscontainer" id="resumableItems" class="itemsContainer padded-left padded-right"></div>
</div>
<div id="latestItemsSection" class="hide verticalSection">
<div class="sectionTitleContainer sectionTitleContainer-cards">
<h2 class="sectionTitle sectionTitle-cards padded-left">${HeaderLatestEpisodes}</h2>
</div>
<div class="verticalSection">
<div is="emby-itemscontainer" id="latestEpisodesItems" class="itemsContainer padded-left padded-right"></div>
</div>
<div id="nextUpItemsSection" class="hide verticalSection">
<div class="sectionTitleContainer sectionTitleContainer-cards">
<h2 class="sectionTitle sectionTitle-cards padded-left nextUpHeader">${NextUp}</h2>
</div>
@ -33,16 +40,7 @@
</div>
<p class="noNextUpItems" style="display: none;">${MessageNoNextUpItems}</p>
</div>
<div class="pageTabContent" id="latestTab" data-index="2">
<div class="verticalSection">
<div class="sectionTitleContainer sectionTitleContainer-cards">
<h2 class="sectionTitle sectionTitle-cards padded-left">${HeaderLatestEpisodes}</h2>
</div>
<div is="emby-itemscontainer" id="latestEpisodes" class="itemsContainer vertical-wrap padded-left padded-right">
</div>
</div>
</div>
<div class="pageTabContent" id="upcomingTab" data-index="3">
<div class="pageTabContent" id="upcomingTab" data-index="2">
<div id="upcomingItems">
</div>
<div class="noItemsMessage centerMessage" style="display: none;">
@ -50,13 +48,13 @@
<p>${MessagePleaseEnsureInternetMetadata}</p>
</div>
</div>
<div class="pageTabContent" id="genresTab" data-index="4">
<div class="pageTabContent" id="genresTab" data-index="3">
<div id="items"></div>
</div>
<div class="pageTabContent" id="studiosTab" data-index="5">
<div class="pageTabContent" id="studiosTab" data-index="4">
<div is="emby-itemscontainer" id="items" class="itemsContainer padded-left padded-right padded-top vertical-wrap" style="text-align: center;"></div>
</div>
<div class="pageTabContent" data-index="6">
<div class="pageTabContent" id="episodesTab" data-index="5">
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
<div class="paging"></div>
<button is="paper-icon-button-light" class="btnSelectView autoSize" title="${ButtonSelectView}"><span class="material-icons view_comfy"></span></button>
@ -69,6 +67,4 @@
<div class="paging"></div>
</div>
</div>
<div class="pageTabContent" data-index="7">
</div>
</div>

View file

@ -20,8 +20,6 @@ import 'emby-button';
name: globalize.translate('Shows')
}, {
name: globalize.translate('Suggestions')
}, {
name: globalize.translate('TabLatest')
}, {
name: globalize.translate('TabUpcoming')
}, {
@ -38,15 +36,18 @@ import 'emby-button';
case 'suggestions':
return 1;
case 'latest':
case 'upcoming':
return 2;
case 'favorites':
return 1;
case 'genres':
return 3;
case 'networks':
return 4;
case 'episodes':
return 5;
default:
return 0;
}
@ -70,102 +71,159 @@ import 'emby-button';
}
}
function initSuggestedTab(page, tabContent) {
const containers = tabContent.querySelectorAll('.itemsContainer');
for (let i = 0, length = containers.length; i < length; i++) {
setScrollClasses(containers[i], enableScrollX());
}
}
function loadSuggestionsTab(view, params, tabContent) {
const parentId = params.topParentId;
const userId = ApiClient.getCurrentUserId();
console.debug('loadSuggestionsTab');
loadResume(tabContent, userId, parentId);
loadLatest(tabContent, userId, parentId);
loadNextUp(tabContent, userId, parentId);
}
function loadResume(view, userId, parentId) {
const screenWidth = dom.getWindowSize().innerWidth;
const options = {
SortBy: 'DatePlayed',
SortOrder: 'Descending',
IncludeItemTypes: 'Episode',
Filters: 'IsResumable',
Limit: screenWidth >= 1920 ? 5 : screenWidth >= 1600 ? 5 : 3,
Recursive: true,
Fields: 'PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo',
CollapseBoxSetItems: false,
ParentId: parentId,
ImageTypeLimit: 1,
EnableImageTypes: 'Primary,Backdrop,Banner,Thumb',
EnableTotalRecordCount: false
};
ApiClient.getItems(userId, options).then(function (result) {
if (result.Items.length) {
view.querySelector('#resumableSection').classList.remove('hide');
} else {
view.querySelector('#resumableSection').classList.add('hide');
}
const allowBottomPadding = !enableScrollX();
const container = view.querySelector('#resumableItems');
cardBuilder.buildCards(result.Items, {
itemsContainer: container,
preferThumb: true,
shape: getThumbShape(),
scalable: true,
overlayPlayButton: true,
allowBottomPadding: allowBottomPadding,
cardLayout: false,
showTitle: true,
showYear: true,
centerText: true
});
loading.hide();
import('autoFocuser').then(({default: autoFocuser}) => {
autoFocuser.autoFocus(view);
});
});
}
function loadLatest(view, userId, parentId) {
const options = {
userId: userId,
IncludeItemTypes: 'Episode',
Limit: 30,
Fields: 'PrimaryImageAspectRatio,BasicSyncInfo',
ParentId: parentId,
ImageTypeLimit: 1,
EnableImageTypes: 'Primary,Backdrop,Thumb'
};
ApiClient.getLatestItems(options).then(function (items) {
const section = view.querySelector('#latestItemsSection');
const allowBottomPadding = !enableScrollX();
const container = section.querySelector('#latestEpisodesItems');
cardBuilder.buildCards(items, {
parentContainer: section,
itemsContainer: container,
items: items,
shape: 'backdrop',
preferThumb: true,
showTitle: true,
showSeriesYear: true,
showParentTitle: true,
overlayText: false,
cardLayout: false,
allowBottomPadding: allowBottomPadding,
showUnplayedIndicator: false,
showChildCountIndicator: true,
centerText: true,
lazy: true,
overlayPlayButton: true,
lines: 2
});
loading.hide();
import('autoFocuser').then(({default: autoFocuser}) => {
autoFocuser.autoFocus(view);
});
});
}
function loadNextUp(view, userId, parentId) {
const query = {
userId: userId,
Limit: 24,
Fields: 'PrimaryImageAspectRatio,SeriesInfo,DateCreated,BasicSyncInfo',
ParentId: parentId,
ImageTypeLimit: 1,
EnableImageTypes: 'Primary,Backdrop,Thumb',
EnableTotalRecordCount: false
};
query.ParentId = libraryMenu.getTopParentId();
ApiClient.getNextUpEpisodes(query).then(function (result) {
if (result.Items.length) {
view.querySelector('.noNextUpItems').classList.add('hide');
} else {
view.querySelector('.noNextUpItems').classList.remove('hide');
}
const section = view.querySelector('#nextUpItemsSection');
const container = section.querySelector('#nextUpItems');
cardBuilder.buildCards(result.Items, {
parentContainer: section,
itemsContainer: container,
preferThumb: true,
shape: 'backdrop',
scalable: true,
showTitle: true,
showParentTitle: true,
overlayText: false,
centerText: true,
overlayPlayButton: true,
cardLayout: false
});
loading.hide();
import('autoFocuser').then(({default: autoFocuser}) => {
autoFocuser.autoFocus(view);
});
});
}
function enableScrollX() {
return !layoutManager.desktop;
}
function getThumbShape() {
return enableScrollX() ? 'overflowBackdrop' : 'backdrop';
}
export default function (view, params) {
function reload() {
loading.show();
loadResume();
loadNextUp();
}
function loadNextUp() {
const query = {
Limit: 24,
Fields: 'PrimaryImageAspectRatio,SeriesInfo,DateCreated,BasicSyncInfo',
UserId: ApiClient.getCurrentUserId(),
ImageTypeLimit: 1,
EnableImageTypes: 'Primary,Backdrop,Thumb',
EnableTotalRecordCount: false
};
query.ParentId = libraryMenu.getTopParentId();
ApiClient.getNextUpEpisodes(query).then(function (result) {
if (result.Items.length) {
view.querySelector('.noNextUpItems').classList.add('hide');
} else {
view.querySelector('.noNextUpItems').classList.remove('hide');
}
const container = view.querySelector('#nextUpItems');
cardBuilder.buildCards(result.Items, {
itemsContainer: container,
preferThumb: true,
shape: 'backdrop',
scalable: true,
showTitle: true,
showParentTitle: true,
overlayText: false,
centerText: true,
overlayPlayButton: true,
cardLayout: false
});
loading.hide();
import('autoFocuser').then(({default: autoFocuser}) => {
autoFocuser.autoFocus(view);
});
});
}
function enableScrollX() {
return !layoutManager.desktop;
}
function getThumbShape() {
return enableScrollX() ? 'overflowBackdrop' : 'backdrop';
}
function loadResume() {
const parentId = libraryMenu.getTopParentId();
const screenWidth = dom.getWindowSize().innerWidth;
const limit = screenWidth >= 1600 ? 5 : 6;
const options = {
SortBy: 'DatePlayed',
SortOrder: 'Descending',
IncludeItemTypes: 'Episode',
Filters: 'IsResumable',
Limit: limit,
Recursive: true,
Fields: 'PrimaryImageAspectRatio,SeriesInfo,UserData,BasicSyncInfo',
ExcludeLocationTypes: 'Virtual',
ParentId: parentId,
ImageTypeLimit: 1,
EnableImageTypes: 'Primary,Backdrop,Thumb',
EnableTotalRecordCount: false
};
ApiClient.getItems(ApiClient.getCurrentUserId(), options).then(function (result) {
if (result.Items.length) {
view.querySelector('#resumableSection').classList.remove('hide');
} else {
view.querySelector('#resumableSection').classList.add('hide');
}
const allowBottomPadding = !enableScrollX();
const container = view.querySelector('#resumableItems');
cardBuilder.buildCards(result.Items, {
itemsContainer: container,
preferThumb: true,
shape: getThumbShape(),
scalable: true,
showTitle: true,
showParentTitle: true,
overlayText: false,
centerText: true,
overlayPlayButton: true,
allowBottomPadding: allowBottomPadding,
cardLayout: false
});
});
}
function onBeforeTabChange(e) {
preLoadTab(view, parseInt(e.detail.selectedTabIndex));
}
@ -196,22 +254,18 @@ import 'emby-button';
break;
case 2:
depends = 'controllers/shows/tvlatest';
break;
case 3:
depends = 'controllers/shows/tvupcoming';
break;
case 4:
case 3:
depends = 'controllers/shows/tvgenres';
break;
case 5:
case 4:
depends = 'controllers/shows/tvstudios';
break;
case 6:
case 5:
depends = 'controllers/shows/episodes';
break;
}
@ -231,11 +285,6 @@ import 'emby-button';
if (index === 1) {
controller = self;
} else if (index === 7) {
controller = new controllerFactory(view, tabContent, {
collectionType: 'tvshows',
parentId: params.topParentId
});
} else {
controller = new controllerFactory(view, params, tabContent);
}
@ -294,19 +343,20 @@ import 'emby-button';
const self = this;
let currentTabIndex = parseInt(params.tab || getDefaultTabIndex(params.topParentId));
const suggestionsTabIndex = 1;
self.initTab = function () {
const tabContent = self.tabContent;
setScrollClasses(tabContent.querySelector('#resumableItems'), enableScrollX());
const tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
initSuggestedTab(view, tabContent);
};
self.renderTab = function () {
reload();
const tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
loadSuggestionsTab(view, params, tabContent);
};
const tabControllers = [];
let renderedTabs = [];
setScrollClasses(view.querySelector('#resumableItems'), enableScrollX());
view.addEventListener('viewshow', function (e) {
initTabs();
if (!view.getAttribute('data-title')) {

View file

@ -14,14 +14,14 @@ import 'webcomponents';
if (window.MutationObserver) {
// create an observer instance
var observer = new MutationObserver(function (mutations) {
const 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 };
const config = { attributes: true, childList: false, characterData: false };
// pass in the target node, as well as the observer options
observer.observe(instance, config);

View file

@ -54,7 +54,7 @@ import 'emby-input';
// Snap to step
if (range.step !== 'any') {
var step = range.step || 1;
const step = range.step || 1;
value = Math.round(value / step) * step;
}

View file

@ -2,16 +2,16 @@
if (HTMLElement.prototype.nativeFocus === undefined) {
(function () {
var supportsPreventScrollOption = false;
let supportsPreventScrollOption = false;
try {
var focusElem = document.createElement('div');
const focusElem = document.createElement('div');
focusElem.addEventListener('focus', function(event) {
event.preventDefault();
event.stopPropagation();
}, true);
var opts = Object.defineProperty({}, 'preventScroll', {
const opts = Object.defineProperty({}, 'preventScroll', {
// eslint-disable-next-line getter-return
get: function () {
supportsPreventScrollOption = true;
@ -27,8 +27,8 @@ if (HTMLElement.prototype.nativeFocus === undefined) {
HTMLElement.prototype.nativeFocus = HTMLElement.prototype.focus;
HTMLElement.prototype.focus = function(options) {
var scrollX = window.scrollX;
var scrollY = window.scrollY;
const scrollX = window.scrollX;
const scrollY = window.scrollY;
this.nativeFocus();

View file

@ -2,6 +2,7 @@
* and will be replaced soon by a Vue component.
*/
/* eslint-disable no-var */
import browser from 'browser';
import dom from 'dom';
import 'css!./navdrawer';
@ -14,26 +15,26 @@ export default function (options) {
function onMenuTouchStart(e) {
options.target.classList.remove('transition');
var touches = getTouches(e);
var touch = touches[0] || {};
const touches = getTouches(e);
const touch = touches[0] || {};
menuTouchStartX = touch.clientX;
menuTouchStartY = touch.clientY;
menuTouchStartTime = new Date().getTime();
}
function setVelocity(deltaX) {
var time = new Date().getTime() - (menuTouchStartTime || 0);
const time = new Date().getTime() - (menuTouchStartTime || 0);
velocity = Math.abs(deltaX) / time;
}
function onMenuTouchMove(e) {
var isOpen = self.visible;
var touches = getTouches(e);
var touch = touches[0] || {};
var endX = touch.clientX || 0;
var endY = touch.clientY || 0;
var deltaX = endX - (menuTouchStartX || 0);
var deltaY = endY - (menuTouchStartY || 0);
const isOpen = self.visible;
const touches = getTouches(e);
const touch = touches[0] || {};
const endX = touch.clientX || 0;
const endY = touch.clientY || 0;
const deltaX = endX - (menuTouchStartX || 0);
const deltaY = endY - (menuTouchStartY || 0);
setVelocity(deltaX);
if (isOpen && dragMode !== 1 && deltaX > 0) {
@ -58,12 +59,12 @@ export default function (options) {
options.target.classList.add('transition');
scrollContainer.removeEventListener('scroll', disableEvent);
dragMode = 0;
var touches = getTouches(e);
var touch = touches[0] || {};
var endX = touch.clientX || 0;
var endY = touch.clientY || 0;
var deltaX = endX - (menuTouchStartX || 0);
var deltaY = endY - (menuTouchStartY || 0);
const touches = getTouches(e);
const touch = touches[0] || {};
const endX = touch.clientX || 0;
const endY = touch.clientY || 0;
const deltaX = endX - (menuTouchStartX || 0);
const deltaY = endY - (menuTouchStartY || 0);
currentPos = deltaX;
self.checkMenuState(deltaX, deltaY);
}
@ -105,20 +106,20 @@ export default function (options) {
}
function onBackgroundTouchStart(e) {
var touches = getTouches(e);
var touch = touches[0] || {};
const touches = getTouches(e);
const touch = touches[0] || {};
backgroundTouchStartX = touch.clientX;
backgroundTouchStartTime = new Date().getTime();
}
function onBackgroundTouchMove(e) {
var touches = getTouches(e);
var touch = touches[0] || {};
var endX = touch.clientX || 0;
const touches = getTouches(e);
const touch = touches[0] || {};
const endX = touch.clientX || 0;
if (endX <= options.width && self.isVisible) {
countStart++;
var deltaX = endX - (backgroundTouchStartX || 0);
const deltaX = endX - (backgroundTouchStartX || 0);
if (countStart == 1) {
startPoint = deltaX;
@ -127,7 +128,7 @@ export default function (options) {
dragMode = 1;
newPos = deltaX - startPoint + options.width;
self.changeMenuPos();
var time = new Date().getTime() - (backgroundTouchStartTime || 0);
const time = new Date().getTime() - (backgroundTouchStartTime || 0);
velocity = Math.abs(deltaX) / time;
}
}
@ -137,25 +138,25 @@ export default function (options) {
}
function onBackgroundTouchEnd(e) {
var touches = getTouches(e);
var touch = touches[0] || {};
var endX = touch.clientX || 0;
var deltaX = endX - (backgroundTouchStartX || 0);
const touches = getTouches(e);
const touch = touches[0] || {};
const endX = touch.clientX || 0;
const deltaX = endX - (backgroundTouchStartX || 0);
self.checkMenuState(deltaX);
countStart = 0;
}
function onMaskTransitionEnd() {
var classList = mask.classList;
const classList = mask.classList;
if (!classList.contains('backdrop')) {
classList.add('hide');
}
}
var self;
var defaults;
var mask;
let self;
let defaults;
let mask;
var newPos = 0;
var currentPos = 0;
var startPoint = 0;
@ -166,7 +167,7 @@ export default function (options) {
var scrollContainer = options.target.querySelector('.mainDrawer-scrollContainer');
scrollContainer.classList.add('scrollY');
var TouchMenuLA = function () {
const TouchMenuLA = function () {
self = this;
defaults = {
width: 260,
@ -193,9 +194,9 @@ export default function (options) {
}
};
var menuTouchStartX;
var menuTouchStartY;
var menuTouchStartTime;
let menuTouchStartX;
let menuTouchStartY;
let menuTouchStartTime;
var edgeContainer = document.querySelector('.mainDrawerHandle');
var isPeeking = false;
@ -261,8 +262,8 @@ export default function (options) {
}
};
var backgroundTouchStartX;
var backgroundTouchStartTime;
let backgroundTouchStartX;
let backgroundTouchStartTime;
TouchMenuLA.prototype.showMask = function () {
mask.classList.remove('hide');
@ -280,7 +281,7 @@ export default function (options) {
}
};
var _edgeSwipeEnabled;
let _edgeSwipeEnabled;
TouchMenuLA.prototype.setEdgeSwipeEnabled = function (enabled) {
if (!options.disableEdgeSwipe) {
@ -355,3 +356,4 @@ export default function (options) {
return new TouchMenuLA();
}
/* eslint-enable no-var */

View file

@ -56,14 +56,14 @@ function within(number, min, max) {
}
// 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'];
const dragMouseEvents = ['mousemove', 'mouseup'];
const dragTouchEvents = ['touchmove', 'touchend'];
const wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel' : 'mousewheel');
const interactiveElements = ['INPUT', 'SELECT', 'TEXTAREA'];
var scrollerFactory = function (frame, options) {
const scrollerFactory = function (frame, options) {
// Extend options
var o = Object.assign({}, {
const o = Object.assign({}, {
slidee: null, // Selector, DOM element, or jQuery object with DOM element representing SLIDEE.
horizontal: false, // Switch to horizontal mode.
@ -83,7 +83,7 @@ var scrollerFactory = function (frame, options) {
}, options);
var isSmoothScrollSupported = 'scrollBehavior' in document.documentElement.style;
const 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
@ -106,11 +106,11 @@ var scrollerFactory = function (frame, options) {
}
// Private variables
var self = this;
const self = this;
self.options = o;
// Frame
var slideeElement = o.slidee ? o.slidee : sibling(frame.firstChild)[0];
const slideeElement = o.slidee ? o.slidee : sibling(frame.firstChild)[0];
self._pos = {
start: 0,
center: 0,
@ -119,15 +119,15 @@ var scrollerFactory = function (frame, options) {
dest: 0
};
var transform = !options.enableNativeScroll;
const transform = !options.enableNativeScroll;
// Miscellaneous
var scrollSource = frame;
var dragSourceElement = o.dragSource ? o.dragSource : frame;
var dragging = {
const scrollSource = frame;
const dragSourceElement = o.dragSource ? o.dragSource : frame;
const dragging = {
released: 1
};
var scrolling = {
const scrolling = {
last: 0,
delta: 0,
resetTime: 200
@ -139,10 +139,10 @@ var scrollerFactory = function (frame, options) {
self.options = o;
self.dragging = dragging;
var nativeScrollElement = frame;
const nativeScrollElement = frame;
function sibling(n, elem) {
var matched = [];
const matched = [];
for (; n; n = n.nextSibling) {
if (n.nodeType === 1 && n !== elem) {
@ -152,10 +152,10 @@ var scrollerFactory = function (frame, options) {
return matched;
}
var requiresReflow = true;
let requiresReflow = true;
var frameSize = 0;
var slideeSize = 0;
let frameSize = 0;
let slideeSize = 0;
function ensureSizeInfo() {
if (requiresReflow) {
requiresReflow = false;
@ -185,13 +185,13 @@ var scrollerFactory = function (frame, options) {
ensureSizeInfo();
// Fix possible overflowing
var pos = self._pos;
const pos = self._pos;
self.slideTo(within(pos.dest, pos.start, pos.end));
}
}
function initFrameResizeObserver() {
var observerOptions = {};
const observerOptions = {};
self.frameResizeObserver = new ResizeObserver(onResize, observerOptions);
@ -242,7 +242,7 @@ var scrollerFactory = function (frame, options) {
}
}
var lastAnimate;
let lastAnimate;
/**
* Animate to a position.
@ -254,7 +254,7 @@ var scrollerFactory = function (frame, options) {
*/
self.slideTo = function (newPos, immediate, fullItemPos) {
ensureSizeInfo();
var pos = self._pos;
const pos = self._pos;
if (layoutManager.tv) {
newPos = within(newPos, pos.start);
@ -268,10 +268,10 @@ var scrollerFactory = function (frame, options) {
}
// Update the animation object
var from = pos.cur;
const from = pos.cur;
immediate = immediate || dragging.init || !o.speed;
var now = new Date().getTime();
const now = new Date().getTime();
if (o.autoImmediate) {
if (!immediate && (now - (lastAnimate || 0)) <= 50) {
@ -291,7 +291,7 @@ var scrollerFactory = function (frame, options) {
};
function setStyleProperty(elem, name, value, speed, resetTransition) {
var style = elem.style;
const style = elem.style;
if (resetTransition || browser.edge) {
style.transition = 'none';
@ -312,7 +312,7 @@ var scrollerFactory = function (frame, options) {
}
function renderAnimateWithTransform(fromPosition, toPosition, immediate) {
var speed = o.speed;
let speed = o.speed;
if (immediate) {
speed = o.immediateSpeed || 50;
@ -346,18 +346,18 @@ var scrollerFactory = function (frame, options) {
* @return {Object}
*/
self.getPos = function (item) {
var scrollElement = transform ? slideeElement : nativeScrollElement;
var slideeOffset = getBoundingClientRect(scrollElement);
var itemOffset = getBoundingClientRect(item);
const scrollElement = transform ? slideeElement : nativeScrollElement;
const slideeOffset = getBoundingClientRect(scrollElement);
const itemOffset = getBoundingClientRect(item);
var offset = o.horizontal ? itemOffset.left - slideeOffset.left : itemOffset.top - slideeOffset.top;
let offset = o.horizontal ? itemOffset.left - slideeOffset.left : itemOffset.top - slideeOffset.top;
var size = o.horizontal ? itemOffset.width : itemOffset.height;
let size = o.horizontal ? itemOffset.width : itemOffset.height;
if (!size && size !== 0) {
size = item[o.horizontal ? 'offsetWidth' : 'offsetHeight'];
}
var centerOffset = o.centerOffset || 0;
let centerOffset = o.centerOffset || 0;
if (!transform) {
centerOffset = 0;
@ -370,11 +370,11 @@ var scrollerFactory = function (frame, options) {
ensureSizeInfo();
var currentStart = self._pos.cur;
var currentEnd = currentStart + frameSize;
const currentStart = self._pos.cur;
const currentEnd = currentStart + frameSize;
console.debug('offset:' + offset + ' currentStart:' + currentStart + ' currentEnd:' + currentEnd);
var isVisible = offset >= currentStart && (offset + size) <= currentEnd;
const isVisible = offset >= currentStart && (offset + size) <= currentEnd;
return {
start: offset,
@ -388,12 +388,12 @@ var scrollerFactory = function (frame, options) {
self.getCenterPosition = function (item) {
ensureSizeInfo();
var pos = self.getPos(item);
const pos = self.getPos(item);
return within(pos.center, pos.start, pos.end);
};
function dragInitSlidee(event) {
var isTouch = event.type === 'touchstart';
const isTouch = event.type === 'touchstart';
// Ignore when already in progress, or interactive element in non-touch navivagion
if (dragging.init || !isTouch && isInteractive(event.target)) {
@ -417,7 +417,7 @@ var scrollerFactory = function (frame, options) {
dragging.init = 0;
dragging.source = event.target;
dragging.touch = isTouch;
var pointer = isTouch ? event.touches[0] : event;
const pointer = isTouch ? event.touches[0] : event;
dragging.initX = pointer.pageX;
dragging.initY = pointer.pageY;
dragging.initPos = self._pos.cur;
@ -455,7 +455,7 @@ var scrollerFactory = function (frame, options) {
*/
function dragHandler(event) {
dragging.released = event.type === 'mouseup' || event.type === 'touchend';
var pointer = dragging.touch ? event[dragging.released ? 'changedTouches' : 'touches'][0] : event;
const pointer = dragging.touch ? event[dragging.released ? 'changedTouches' : 'touches'][0] : event;
dragging.pathX = pointer.pageX - dragging.initX;
dragging.pathY = pointer.pageY - dragging.initY;
dragging.path = Math.sqrt(Math.pow(dragging.pathX, 2) + Math.pow(dragging.pathY, 2));
@ -570,12 +570,12 @@ var scrollerFactory = function (frame, options) {
*/
function scrollHandler(event) {
ensureSizeInfo();
var pos = self._pos;
const pos = self._pos;
// Ignore if there is no scrolling to be done
if (!o.scrollBy || pos.start === pos.end) {
return;
}
var delta = normalizeWheelDelta(event);
let delta = normalizeWheelDelta(event);
if (transform) {
// Trap scrolling only when necessary and/or requested
@ -635,13 +635,13 @@ var scrollerFactory = function (frame, options) {
return self;
};
var contentRect = {};
let contentRect = {};
function onResize(entries) {
var entry = entries[0];
const entry = entries[0];
if (entry) {
var newRect = entry.contentRect;
const newRect = entry.contentRect;
// handle element being hidden
if (newRect.width === 0 || newRect.height === 0) {
@ -666,7 +666,7 @@ var scrollerFactory = function (frame, options) {
function onFrameClick(e) {
if (e.which === 1) {
var focusableParent = focusManager.focusableParent(e.target);
const focusableParent = focusManager.focusableParent(e.target);
if (focusableParent && focusableParent !== document.activeElement) {
focusableParent.focus();
}
@ -838,7 +838,7 @@ scrollerFactory.prototype.to = function (location, item, immediate) {
if (item === undefined) {
this.slideTo(this._pos[location], immediate);
} else {
var itemPos = this.getPos(item);
const itemPos = this.getPos(item);
if (itemPos) {
this.slideTo(itemPos[location], immediate, itemPos);
@ -883,7 +883,7 @@ scrollerFactory.prototype.toCenter = function (item, immediate) {
};
scrollerFactory.create = function (frame, options) {
var instance = new scrollerFactory(frame, options);
const instance = new scrollerFactory(frame, options);
return Promise.resolve(instance);
};

View file

@ -1,3 +1,4 @@
import browser from 'browser';
import loading from 'loading';
import keyboardnavigation from 'keyboardnavigation';
import dialogHelper from 'dialogHelper';
@ -18,12 +19,15 @@ export class BookPlayer {
this.onDialogClosed = this.onDialogClosed.bind(this);
this.openTableOfContents = this.openTableOfContents.bind(this);
this.prevChapter = this.prevChapter.bind(this);
this.nextChapter = this.nextChapter.bind(this);
this.onWindowKeyUp = this.onWindowKeyUp.bind(this);
}
play(options) {
this._progress = 0;
this._loaded = false;
this.progress = 0;
this.cancellationToken = false;
this.loaded = false;
loading.show();
const elem = this.createMediaElement();
@ -33,35 +37,35 @@ export class BookPlayer {
stop() {
this.unbindEvents();
const elem = this._mediaElement;
const tocElement = this._tocElement;
const rendition = this._rendition;
const elem = this.mediaElement;
const tocElement = this.tocElement;
const rendition = this.rendition;
if (elem) {
dialogHelper.close(elem);
this._mediaElement = null;
this.mediaElement = null;
}
if (tocElement) {
tocElement.destroy();
this._tocElement = null;
this.tocElement = null;
}
if (rendition) {
rendition.destroy();
}
// Hide loader in case player was not fully loaded yet
// hide loader in case player was not fully loaded yet
loading.hide();
this._cancellationToken.shouldCancel = true;
this.cancellationToken = true;
}
currentItem() {
return this._currentItem;
return this.item;
}
currentTime() {
return this._progress * 1000;
return this.progress * 1000;
}
duration() {
@ -93,12 +97,10 @@ export class BookPlayer {
onWindowKeyUp(e) {
const key = keyboardnavigation.getKeyName(e);
// TODO: depending on the event this can be the document or the rendition itself
const rendition = this._rendition || this;
const rendition = this.rendition;
const book = rendition.book;
if (this._loaded === false) return;
if (!this.loaded) return;
switch (key) {
case 'l':
case 'ArrowRight':
@ -111,9 +113,9 @@ export class BookPlayer {
book.package.metadata.direction === 'rtl' ? rendition.next() : rendition.prev();
break;
case 'Escape':
if (this._tocElement) {
if (this.tocElement) {
// Close table of contents on ESC if it is open
this._tocElement.destroy();
this.tocElement.destroy();
} else {
// Otherwise stop the entire book player
this.stop();
@ -122,79 +124,73 @@ export class BookPlayer {
}
}
onTouchStart(e) {
// TODO: depending on the event this can be the document or the rendition itself
const rendition = this._rendition || this;
const book = rendition.book;
// check that the event is from the book or the document
if (!book || this._loaded === false) return;
// epubjs stores pages off the screen or something for preloading
// get the modulus of the touch event to account for the increased width
if (!e.touches || e.touches.length === 0) return;
const touch = e.touches[0].clientX % dom.getWindowSize().innerWidth;
if (touch < dom.getWindowSize().innerWidth / 2) {
book.package.metadata.direction === 'rtl' ? rendition.next() : rendition.prev();
} else {
book.package.metadata.direction === 'rtl' ? rendition.prev() : rendition.next();
}
}
onDialogClosed() {
this.stop();
}
bindMediaElementEvents() {
const elem = this._mediaElement;
const elem = this.mediaElement;
elem.addEventListener('close', this.onDialogClosed, {once: true});
elem.querySelector('.btnBookplayerExit').addEventListener('click', this.onDialogClosed, {once: true});
elem.querySelector('.btnBookplayerToc').addEventListener('click', this.openTableOfContents);
elem.querySelector('#btnBookplayerExit').addEventListener('click', this.onDialogClosed, {once: true});
elem.querySelector('#btnBookplayerToc').addEventListener('click', this.openTableOfContents);
if (browser.mobile) {
elem.querySelector('#btnBookplayerPrev').addEventListener('click', this.prevChapter);
elem.querySelector('#btnBookplayerNext').addEventListener('click', this.nextChapter);
}
}
bindEvents() {
this.bindMediaElementEvents();
document.addEventListener('keyup', this.onWindowKeyUp);
document.addEventListener('touchstart', this.onTouchStart);
// FIXME: I don't really get why document keyup event is not triggered when epub is in focus
this._rendition.on('keyup', this.onWindowKeyUp);
this._rendition.on('touchstart', this.onTouchStart);
this.rendition.on('keyup', this.onWindowKeyUp);
}
unbindMediaElementEvents() {
const elem = this._mediaElement;
const elem = this.mediaElement;
elem.removeEventListener('close', this.onDialogClosed);
elem.querySelector('.btnBookplayerExit').removeEventListener('click', this.onDialogClosed);
elem.querySelector('.btnBookplayerToc').removeEventListener('click', this.openTableOfContents);
elem.querySelector('#btnBookplayerExit').removeEventListener('click', this.onDialogClosed);
elem.querySelector('#btnBookplayerToc').removeEventListener('click', this.openTableOfContents);
if (browser.mobile) {
elem.querySelector('#btnBookplayerPrev').removeEventListener('click', this.prevChapter);
elem.querySelector('#btnBookplayerNext').removeEventListener('click', this.nextChapter);
}
}
unbindEvents() {
if (this._mediaElement) {
if (this.mediaElement) {
this.unbindMediaElementEvents();
}
document.removeEventListener('keyup', this.onWindowKeyUp);
document.removeEventListener('touchstart', this.onTouchStart);
if (this._rendition) {
this._rendition.off('keyup', this.onWindowKeyUp);
this._rendition.off('touchstart', this.onTouchStart);
if (this.rendition) {
this.rendition.off('keyup', this.onWindowKeyUp);
}
}
openTableOfContents() {
if (this._loaded) {
this._tocElement = new TableOfContents(this);
if (this.loaded) {
this.tocElement = new TableOfContents(this);
}
}
prevChapter(e) {
this._rendition.prev();
e.preventDefault();
}
nextChapter(e) {
this._rendition.next();
e.preventDefault();
}
createMediaElement() {
let elem = this._mediaElement;
let elem = this.mediaElement;
if (elem) {
return elem;
}
@ -210,29 +206,51 @@ export class BookPlayer {
removeOnClose: true
});
elem.id = 'bookPlayer';
let html = '';
html += '<div class="topRightActionButtons">';
html += '<button is="paper-icon-button-light" class="autoSize bookplayerButton btnBookplayerExit hide-mouse-idle-tv" tabindex="-1"><i class="material-icons bookplayerButtonIcon close"></i></button>';
if (browser.mobile) {
html += '<div class="button-wrapper top-button"><button id="btnBookplayerPrev" is="paper-icon-button-light" class="autoSize bookplayerButton hide-mouse-idle-tv"><i class="material-icons bookplayerButtonIcon navigate_before"></i> Prev</button></div>';
}
html += '<div id="viewer">';
html += '<div class="topButtons">';
html += '<button is="paper-icon-button-light" id="btnBookplayerToc" class="autoSize bookplayerButton hide-mouse-idle-tv" tabindex="-1"><i class="material-icons bookplayerButtonIcon toc"></i></button>';
html += '<button is="paper-icon-button-light" id="btnBookplayerExit" class="autoSize bookplayerButton hide-mouse-idle-tv" tabindex="-1"><i class="material-icons bookplayerButtonIcon close"></i></button>';
html += '</div>';
html += '<div class="topLeftActionButtons">';
html += '<button is="paper-icon-button-light" class="autoSize bookplayerButton btnBookplayerToc hide-mouse-idle-tv" tabindex="-1"><i class="material-icons bookplayerButtonIcon toc"></i></button>';
html += '</div>';
if (browser.mobile) {
html += '<div class="button-wrapper bottom-button"><button id="btnBookplayerNext" is="paper-icon-button-light" class="autoSize bookplayerButton hide-mouse-idle-tv">Next <i class="material-icons bookplayerButtonIcon navigate_next"></i></button></div>';
}
elem.id = 'bookPlayer';
elem.innerHTML = html;
dialogHelper.open(elem);
}
this._mediaElement = elem;
this.mediaElement = elem;
return elem;
}
render(elem, book) {
if (browser.mobile) {
return book.renderTo(elem, {
width: '100%',
height: '100%',
flow: 'scrolled-doc'
});
} else {
return book.renderTo(elem, {
width: '100%',
height: '100%'
});
}
}
setCurrentSrc(elem, options) {
const item = options.items[0];
this._currentItem = item;
this.item = item;
this.streamInfo = {
started: true,
ended: false,
@ -248,15 +266,10 @@ export class BookPlayer {
import('epubjs').then(({default: epubjs}) => {
const downloadHref = apiClient.getItemDownloadUrl(item.Id);
const book = epubjs(downloadHref, {openAs: 'epub'});
const rendition = book.renderTo(elem, {width: '100%', height: '97%'});
const rendition = this.render('viewer', book);
this._currentSrc = downloadHref;
this._rendition = rendition;
const cancellationToken = {
shouldCancel: false
};
this._cancellationToken = cancellationToken;
this.currentSrc = downloadHref;
this.rendition = rendition;
return rendition.display().then(() => {
const epubElem = document.querySelector('.epub-container');
@ -264,10 +277,8 @@ export class BookPlayer {
this.bindEvents();
return this._rendition.book.locations.generate(1024).then(async () => {
if (cancellationToken.shouldCancel) {
return reject();
}
return this.rendition.book.locations.generate(1024).then(async () => {
if (this.cancellationToken) reject();
const percentageTicks = options.startPositionTicks / 10000000;
if (percentageTicks !== 0.0) {
@ -275,15 +286,14 @@ export class BookPlayer {
await rendition.display(resumeLocation);
}
this._loaded = true;
this.loaded = true;
epubElem.style.display = 'block';
rendition.on('relocated', (locations) => {
this._progress = book.locations.percentageFromCfi(locations.start.cfi);
this.progress = book.locations.percentageFromCfi(locations.start.cfi);
events.trigger(this, 'timeupdate');
});
loading.hide();
return resolve();
});
}, () => {
@ -299,7 +309,7 @@ export class BookPlayer {
}
canPlayItem(item) {
if (item.Path && (item.Path.endsWith('epub'))) {
if (item.Path && item.Path.endsWith('epub')) {
return true;
}

View file

@ -7,18 +7,20 @@
background: #fff;
}
.topRightActionButtons {
right: 0.5vh;
.topButtons {
top: 0.5vh;
z-index: 1002;
position: absolute;
position: sticky;
}
.topLeftActionButtons {
left: 0.5vh;
top: 0.5vh;
z-index: 1002;
position: absolute;
#btnBookplayerToc {
float: left;
margin-left: 2vw;
}
#btnBookplayerExit {
float: right;
margin-right: 2vw;
}
.bookplayerButtonIcon {
@ -37,3 +39,31 @@
.bookplayerErrorMsg {
text-align: center;
}
#viewer {
align-items: flex-start;
}
#btnBookplayerPrev {
margin: 0.5vh 0.5vh;
color: black;
}
#btnBookplayerNext {
margin: 0.5vh 0.5vh;
color: black;
}
.button-wrapper {
text-align: center;
position: relative;
height: 0;
}
.top-button {
margin: 0.5vh 2em;
}
.bottom-button {
margin: 2em 0.5vh;
}

View file

@ -2,8 +2,8 @@ import dialogHelper from 'dialogHelper';
export default class TableOfContents {
constructor(bookPlayer) {
this._bookPlayer = bookPlayer;
this._rendition = bookPlayer._rendition;
this.bookPlayer = bookPlayer;
this.rendition = bookPlayer.rendition;
this.onDialogClosed = this.onDialogClosed.bind(this);
@ -11,24 +11,24 @@ export default class TableOfContents {
}
destroy() {
const elem = this._elem;
const elem = this.elem;
if (elem) {
this.unbindEvents();
dialogHelper.close(elem);
}
this._bookPlayer._tocElement = null;
this.bookPlayer.tocElement = null;
}
bindEvents() {
const elem = this._elem;
const elem = this.elem;
elem.addEventListener('close', this.onDialogClosed, {once: true});
elem.querySelector('.btnBookplayerTocClose').addEventListener('click', this.onDialogClosed, {once: true});
}
unbindEvents() {
const elem = this._elem;
const elem = this.elem;
elem.removeEventListener('close', this.onDialogClosed);
elem.querySelector('.btnBookplayerTocClose').removeEventListener('click', this.onDialogClosed);
@ -52,7 +52,7 @@ export default class TableOfContents {
}
createMediaElement() {
const rendition = this._rendition;
const rendition = this.rendition;
const elem = dialogHelper.createDialog({
size: 'small',
@ -68,7 +68,8 @@ export default class TableOfContents {
tocHtml += '<ul class="toc">';
rendition.book.navigation.forEach((chapter) => {
tocHtml += '<li>';
// Remove '../' from href
// remove parent directory reference from href to fix certain books
const link = chapter.href.startsWith('../') ? chapter.href.substr(3) : chapter.href;
tocHtml += `<a href="${rendition.book.path.directory + link}">${chapter.label}</a>`;
tocHtml += '</li>';
@ -83,7 +84,7 @@ export default class TableOfContents {
this.destroy();
});
this._elem = elem;
this.elem = elem;
this.bindEvents();
dialogHelper.open(elem);

View file

@ -103,7 +103,7 @@ export class ComicsPlayer {
const downloadUrl = apiClient.getItemDownloadUrl(item.Id);
const archiveSource = new ArchiveSource(downloadUrl);
var instance = this;
const instance = this;
import('swiper').then(({default: Swiper}) => {
archiveSource.load().then(() => {
loading.hide();

View file

@ -114,7 +114,6 @@ function tryRemoveElement(elem) {
return new Promise(resolve => {
const duration = 240;
elem.style.animation = `htmlvideoplayer-zoomin ${duration}ms ease-in normal`;
hidePrePlaybackPage();
dom.addEventListener(elem, dom.whichAnimationEvent(), resolve, {
once: true
});
@ -1329,17 +1328,24 @@ function tryRemoveElement(elem) {
this.#videoDialog = dlg;
this.#mediaElement = videoElement;
if (options.fullscreen) {
hidePrePlaybackPage();
}
// don't animate on smart tv's, too slow
if (options.fullscreen && browser.supportsCssAnimation() && !browser.slow) {
return zoomIn(dlg).then(function () {
return videoElement;
});
} else {
hidePrePlaybackPage();
return videoElement;
}
});
} else {
if (options.fullscreen) {
hidePrePlaybackPage();
}
return Promise.resolve(dlg.querySelector('video'));
}
}

View file

@ -17,7 +17,11 @@
order: -1;
}
video::-webkit-media-controls {
/* Controls are enabled for devices that don't support autoplay. They will be hidden when playback starts.
In Tizen 2.3 (and probably other old web engines), subtitles are located under '-webkit-media-controls' tree.
Therefore, we hide controls only if they are enabled.
*/
video[controls]::-webkit-media-controls {
display: none !important;
}

View file

@ -0,0 +1,306 @@
import loading from 'loading';
import keyboardnavigation from 'keyboardnavigation';
import dialogHelper from 'dialogHelper';
import dom from 'dom';
import appRouter from 'appRouter';
import events from 'events';
import 'css!./style';
import 'material-icons';
import 'paper-icon-button-light';
export class PdfPlayer {
constructor() {
this.name = 'PDF Player';
this.type = 'mediaplayer';
this.id = 'pdfplayer';
this.priority = 1;
this.onDialogClosed = this.onDialogClosed.bind(this);
this.onWindowKeyUp = this.onWindowKeyUp.bind(this);
this.onTouchStart = this.onTouchStart.bind(this);
}
play(options) {
this.progress = 0;
this.loaded = false;
this.cancellationToken = false;
this.pages = {};
loading.show();
const elem = this.createMediaElement();
return this.setCurrentSrc(elem, options);
}
stop() {
this.unbindEvents();
const elem = this.mediaElement;
if (elem) {
dialogHelper.close(elem);
this.mediaElement = null;
}
// hide loading animation
loading.hide();
// cancel page render
this.cancellationToken = true;
}
currentItem() {
return this.item;
}
currentTime() {
return this.progress;
}
duration() {
return this.book ? this.book.numPages : 0;
}
volume() {
return 100;
}
isMuted() {
return false;
}
paused() {
return false;
}
seekable() {
return true;
}
onWindowKeyUp(e) {
const key = keyboardnavigation.getKeyName(e);
if (!this.loaded) return;
switch (key) {
case 'l':
case 'ArrowRight':
case 'Right':
this.next();
break;
case 'j':
case 'ArrowLeft':
case 'Left':
this.previous();
break;
case 'Escape':
this.stop();
break;
}
}
onTouchStart(e) {
if (!this.loaded || !e.touches || e.touches.length === 0) return;
if (e.touches[0].clientX < dom.getWindowSize().innerWidth / 2) {
this.previous();
} else {
this.next();
}
}
onDialogClosed() {
this.stop();
}
bindMediaElementEvents() {
const elem = this.mediaElement;
elem.addEventListener('close', this.onDialogClosed, {once: true});
elem.querySelector('.btnExit').addEventListener('click', this.onDialogClosed, {once: true});
}
bindEvents() {
this.bindMediaElementEvents();
document.addEventListener('keyup', this.onWindowKeyUp);
document.addEventListener('touchstart', this.onTouchStart);
}
unbindMediaElementEvents() {
const elem = this.mediaElement;
elem.removeEventListener('close', this.onDialogClosed);
elem.querySelector('.btnExit').removeEventListener('click', this.onDialogClosed);
}
unbindEvents() {
if (this.mediaElement) {
this.unbindMediaElementEvents();
}
document.removeEventListener('keyup', this.onWindowKeyUp);
document.removeEventListener('touchstart', this.onTouchStart);
}
createMediaElement() {
let elem = this.mediaElement;
if (elem) {
return elem;
}
elem = document.getElementById('pdfPlayer');
if (!elem) {
elem = dialogHelper.createDialog({
exitAnimationDuration: 400,
size: 'fullscreen',
autoFocus: false,
scrollY: false,
exitAnimation: 'fadeout',
removeOnClose: true
});
let html = '';
html += '<canvas id="canvas"></canvas>';
html += '<div class="actionButtons">';
html += '<button is="paper-icon-button-light" class="autoSize btnExit" tabindex="-1"><i class="material-icons actionButtonIcon close"></i></button>';
html += '</div>';
elem.id = 'pdfPlayer';
elem.innerHTML = html;
dialogHelper.open(elem);
}
this.mediaElement = elem;
return elem;
}
setCurrentSrc(elem, options) {
const item = options.items[0];
this.item = item;
this.streamInfo = {
started: true,
ended: false,
mediaSource: {
Id: item.Id
}
};
const serverId = item.ServerId;
const apiClient = window.connectionManager.getApiClient(serverId);
return new Promise((resolve, reject) => {
import('pdfjs').then(({default: pdfjs}) => {
const downloadHref = apiClient.getItemDownloadUrl(item.Id);
this.bindEvents();
pdfjs.GlobalWorkerOptions.workerSrc = appRouter.baseUrl() + '/libraries/pdf.worker.js';
const downloadTask = pdfjs.getDocument(downloadHref);
downloadTask.promise.then(book => {
if (this.cancellationToken) return;
this.book = book;
this.loaded = true;
const percentageTicks = options.startPositionTicks / 10000;
if (percentageTicks !== 0) {
this.loadPage(percentageTicks);
this.progress = percentageTicks;
} else {
this.loadPage(1);
}
return resolve();
});
});
});
}
next() {
if (this.progress === this.duration() - 1) return;
this.loadPage(this.progress + 2);
this.progress = this.progress + 1;
}
previous() {
if (this.progress === 0) return;
this.loadPage(this.progress);
this.progress = this.progress - 1;
}
replaceCanvas(canvas) {
const old = document.getElementById('canvas');
canvas.id = 'canvas';
old.parentNode.replaceChild(canvas, old);
}
loadPage(number) {
const prefix = 'page';
const pad = 2;
// generate list of cached pages by padding the requested page on both sides
const pages = [prefix + number];
for (let i = 1; i <= pad; i++) {
if (number - i > 0) pages.push(prefix + (number - i));
if (number + i < this.duration()) pages.push(prefix + (number + i));
}
// load any missing pages in the cache
for (const page of pages) {
if (!this.pages[page]) {
this.pages[page] = document.createElement('canvas');
this.renderPage(this.pages[page], parseInt(page.substr(4)));
}
}
// show the requested page
this.replaceCanvas(this.pages[prefix + number], number);
// delete all pages outside the cache area
for (const page in this.pages) {
if (!pages.includes(page)) {
delete this.pages[page];
}
}
}
renderPage(canvas, number) {
this.book.getPage(number).then(page => {
events.trigger(this, 'timeupdate');
const original = page.getViewport({ scale: 1 });
const context = canvas.getContext('2d');
const widthRatio = dom.getWindowSize().innerWidth / original.width;
const heightRatio = dom.getWindowSize().innerHeight / original.height;
const scale = Math.min(heightRatio, widthRatio);
const viewport = page.getViewport({ scale: scale });
canvas.width = viewport.width;
canvas.height = viewport.height;
const renderContext = {
canvasContext: context,
viewport: viewport
};
const renderTask = page.render(renderContext);
renderTask.promise.then(() => {
loading.hide();
});
});
}
canPlayMediaType(mediaType) {
return (mediaType || '').toLowerCase() === 'book';
}
canPlayItem(item) {
if (item.Path && item.Path.endsWith('pdf')) {
return true;
}
return false;
}
}
export default PdfPlayer;

View file

@ -0,0 +1,25 @@
#pdfPlayer {
position: relative;
height: 100%;
width: 100%;
overflow: none;
z-index: 100;
background: #fff;
}
#canvas {
display: block;
margin: auto;
}
.actionButtons {
right: 0.5vh;
top: 0.5vh;
z-index: 1002;
position: absolute;
}
.actionButtonIcon {
color: black;
opacity: 0.7;
}

View file

@ -10,11 +10,11 @@ export default class PhotoPlayer {
play(options) {
return new Promise(function (resolve, reject) {
import('slideshow').then(({default: slideshow}) => {
var index = options.startIndex || 0;
const index = options.startIndex || 0;
var apiClient = window.connectionManager.currentApiClient();
const apiClient = window.connectionManager.currentApiClient();
apiClient.getCurrentUser().then(function(result) {
var newSlideShow = new slideshow({
const newSlideShow = new slideshow({
showTitle: false,
cover: false,
items: options.items,

View file

@ -1,15 +1,26 @@
import * as userSettings from 'userSettings';
import * as webSettings from 'webSettings';
import skinManager from 'skinManager';
import events from 'events';
// Set the default theme when loading
// set the default theme when loading
skinManager.setTheme(userSettings.theme())
/* This keeps the scrollbar always present in all pages, so we avoid clipping while switching between pages
/* this keeps the scrollbar always present in all pages, so we avoid clipping while switching between pages
that need the scrollbar and pages that don't.
*/
.then(() => document.body.classList.add('force-scroll'));
// Set the user's prefered theme when signing in
// set the saved theme once a user authenticates
events.on(window.connectionManager, 'localusersignedin', function (e, user) {
skinManager.setTheme(userSettings.theme());
});
webSettings.getFonts().then(fonts => {
for (const font of fonts) {
const link = document.createElement('link');
link.setAttribute('rel', 'stylesheet');
link.href = font;
document.getElementsByTagName('head')[0].appendChild(link);
}
});

View file

@ -32,7 +32,7 @@ function onOpen() {
const playerId = localStorage.getItem('autocastPlayerId');
playbackManager.getTargets().then(function (targets) {
for (var i = 0; i < targets.length; i++) {
for (let i = 0; i < targets.length; i++) {
if (targets[i].id == playerId) {
playbackManager.trySetActivePlayer(targets[i].playerName, targets[i]);
break;

View file

@ -23,7 +23,7 @@ define(['browser'], function (browser) {
videoTestElement.canPlayType('video/mp4; codecs="hev1.1.0.L120"').replace(/no/, ''));
}
var _supportsTextTracks;
let _supportsTextTracks;
function supportsTextTracks() {
if (browser.tizen) {
return true;
@ -37,7 +37,7 @@ define(['browser'], function (browser) {
return _supportsTextTracks;
}
var _canPlayHls;
let _canPlayHls;
function canPlayHls() {
if (_canPlayHls == null) {
_canPlayHls = canPlayNativeHls() || canPlayHlsWithMSE();
@ -51,7 +51,7 @@ define(['browser'], function (browser) {
return true;
}
var media = document.createElement('video');
const media = document.createElement('video');
if (media.canPlayType('application/x-mpegURL').replace(/no/, '') ||
media.canPlayType('application/vnd.apple.mpegURL').replace(/no/, '')) {
return true;
@ -107,7 +107,7 @@ define(['browser'], function (browser) {
}
function canPlayAudioFormat(format) {
var typeString;
let typeString;
if (format === 'flac') {
if (browser.tizen || browser.web0s || browser.edgeUwp) {
@ -192,9 +192,9 @@ define(['browser'], function (browser) {
}
function getDirectPlayProfileForVideoContainer(container, videoAudioCodecs, videoTestElement, options) {
var supported = false;
var profileContainer = container;
var videoCodecs = [];
let supported = false;
let profileContainer = container;
const videoCodecs = [];
switch (container) {
case 'asf':
@ -277,10 +277,10 @@ define(['browser'], function (browser) {
}
function getGlobalMaxVideoBitrate() {
var isTizenFhd = false;
let isTizenFhd = false;
if (browser.tizen) {
try {
var isTizenUhd = webapis.productinfo.isUdPanelSupported();
const isTizenUhd = webapis.productinfo.isUdPanelSupported();
isTizenFhd = !isTizenUhd;
console.debug('isTizenFhd = ' + isTizenFhd);
} catch (error) {
@ -297,19 +297,19 @@ define(['browser'], function (browser) {
return function (options) {
options = options || {};
var physicalAudioChannels = options.audioChannels || (browser.tv || browser.ps4 || browser.xboxOne ? 6 : 2);
const physicalAudioChannels = options.audioChannels || (browser.tv || browser.ps4 || browser.xboxOne ? 6 : 2);
var bitrateSetting = getMaxBitrate();
const bitrateSetting = getMaxBitrate();
var videoTestElement = document.createElement('video');
const 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'];
const canPlayVp8 = videoTestElement.canPlayType('video/webm; codecs="vp8"').replace(/no/, '');
const canPlayVp9 = videoTestElement.canPlayType('video/webm; codecs="vp9"').replace(/no/, '');
const webmAudioCodecs = ['vorbis'];
var canPlayMkv = testCanPlayMkv(videoTestElement);
const canPlayMkv = testCanPlayMkv(videoTestElement);
var profile = {};
const profile = {};
profile.MaxStreamingBitrate = bitrateSetting;
profile.MaxStaticBitrate = 100000000;
@ -317,18 +317,18 @@ define(['browser'], function (browser) {
profile.DirectPlayProfiles = [];
var videoAudioCodecs = [];
var hlsVideoAudioCodecs = [];
let videoAudioCodecs = [];
let hlsVideoAudioCodecs = [];
var supportsMp3VideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.69"').replace(/no/, '')
const supportsMp3VideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.69"').replace(/no/, '')
|| videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.6B"').replace(/no/, '')
|| videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp3"').replace(/no/, '');
// Not sure how to test for this
var supportsMp2VideoAudio = browser.edgeUwp || browser.tizen || browser.web0s;
const supportsMp2VideoAudio = browser.edgeUwp || browser.tizen || browser.web0s;
/* eslint-disable compat/compat */
var maxVideoWidth = browser.xboxOne ?
let maxVideoWidth = browser.xboxOne ?
(window.screen ? window.screen.width : null) :
null;
@ -337,7 +337,7 @@ define(['browser'], function (browser) {
maxVideoWidth = options.maxVideoWidth;
}
var canPlayAacVideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.2"').replace(/no/, '');
const canPlayAacVideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.2"').replace(/no/, '');
// Only put mp3 first if mkv support is there
// Otherwise with HLS and mp3 audio we're seeing some browsers
@ -345,7 +345,7 @@ define(['browser'], function (browser) {
if (supportsAc3(videoTestElement)) {
videoAudioCodecs.push('ac3');
var eAc3 = supportsEac3(videoTestElement);
const eAc3 = supportsEac3(videoTestElement);
if (eAc3) {
videoAudioCodecs.push('eac3');
}
@ -394,7 +394,7 @@ define(['browser'], function (browser) {
videoAudioCodecs.push('mp2');
}
var supportsDts = browser.tizen || browser.web0s || options.supportsDts || videoTestElement.canPlayType('video/mp4; codecs="dts-"').replace(/no/, '') || videoTestElement.canPlayType('video/mp4; codecs="dts+"').replace(/no/, '');
let supportsDts = browser.tizen || browser.web0s || options.supportsDts || videoTestElement.canPlayType('video/mp4; codecs="dts-"').replace(/no/, '') || videoTestElement.canPlayType('video/mp4; codecs="dts+"').replace(/no/, '');
// DTS audio not supported in 2018 models (Tizen 4.0)
if (browser.tizenVersion >= 4) {
@ -437,9 +437,9 @@ define(['browser'], function (browser) {
return (options.disableHlsVideoAudioCodecs || []).indexOf(c) === -1;
});
var mp4VideoCodecs = [];
var webmVideoCodecs = [];
var hlsVideoCodecs = [];
const mp4VideoCodecs = [];
const webmVideoCodecs = [];
const hlsVideoCodecs = [];
if (canPlayH264(videoTestElement)) {
mp4VideoCodecs.push('h264');
@ -555,7 +555,7 @@ define(['browser'], function (browser) {
profile.TranscodingProfiles = [];
var hlsBreakOnNonKeyFrames = browser.iOS || browser.osx || browser.edge || !canPlayNativeHls() ? true : false;
const hlsBreakOnNonKeyFrames = browser.iOS || browser.osx || browser.edge || !canPlayNativeHls() ? true : false;
if (canPlayHls() && browser.enableHlsAudio !== false) {
profile.TranscodingProfiles.push({
@ -661,9 +661,9 @@ define(['browser'], function (browser) {
profile.CodecProfiles = [];
var supportsSecondaryAudio = browser.tizen || videoTestElement.audioTracks;
const supportsSecondaryAudio = browser.tizen || videoTestElement.audioTracks;
var aacCodecProfileConditions = [];
const aacCodecProfileConditions = [];
// Handle he-aac not supported
if (!videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.5"').replace(/no/, '')) {
@ -706,8 +706,8 @@ define(['browser'], function (browser) {
});
}
var maxH264Level = 42;
var h264Profiles = 'high|main|baseline|constrained baseline';
let maxH264Level = 42;
let h264Profiles = 'high|main|baseline|constrained baseline';
if (browser.tizen || browser.web0s ||
videoTestElement.canPlayType('video/mp4; codecs="avc1.640833"').replace(/no/, '')) {
@ -766,9 +766,9 @@ define(['browser'], function (browser) {
});
}
var globalMaxVideoBitrate = (getGlobalMaxVideoBitrate() || '').toString();
const globalMaxVideoBitrate = (getGlobalMaxVideoBitrate() || '').toString();
var h264MaxVideoBitrate = globalMaxVideoBitrate;
const h264MaxVideoBitrate = globalMaxVideoBitrate;
if (h264MaxVideoBitrate) {
h264CodecProfileConditions.push({
@ -806,7 +806,7 @@ define(['browser'], function (browser) {
Conditions: h264CodecProfileConditions
});
var globalVideoConditions = [];
const globalVideoConditions = [];
if (globalMaxVideoBitrate) {
globalVideoConditions.push({

View file

@ -5,8 +5,8 @@ import 'material-icons';
/* eslint-disable indent */
function getNode(item, folderState, selected) {
var htmlName = getNodeInnerHtml(item);
var node = {
const htmlName = getNodeInnerHtml(item);
const node = {
id: item.Id,
text: htmlName,
state: {
@ -37,14 +37,14 @@ import 'material-icons';
}
function getNodeInnerHtml(item) {
var name = item.Name;
let name = item.Name;
if (item.Number) {
name = item.Number + ' - ' + name;
}
if (item.IndexNumber != null && item.Type != 'Season') {
name = item.IndexNumber + ' - ' + name;
}
var htmlName = "<div class='editorNode'>";
let htmlName = "<div class='editorNode'>";
if (item.IsFolder) {
htmlName += '<span class="material-icons metadataSidebarIcon folder"></span>';
} else if (item.MediaType === 'Video') {
@ -70,7 +70,7 @@ import 'material-icons';
ApiClient.getLiveTvChannels({
limit: 0
}).then(function (result) {
var nodes = [];
const nodes = [];
nodes.push({
id: 'MediaFolders',
text: globalize.translate('HeaderMediaFolders'),
@ -110,8 +110,8 @@ import 'material-icons';
ServiceName: service,
AddCurrentProgram: false
}).then(function (result) {
var nodes = result.Items.map(function (i) {
var state = openItems.indexOf(i.Id) == -1 ? 'closed' : 'open';
const nodes = result.Items.map(function (i) {
const state = openItems.indexOf(i.Id) == -1 ? 'closed' : 'open';
return getNode(i, state, false);
});
callback(nodes);
@ -120,12 +120,12 @@ import 'material-icons';
function loadMediaFolders(page, scope, openItems, callback) {
ApiClient.getJSON(ApiClient.getUrl('Library/MediaFolders')).then(function (result) {
var nodes = result.Items.map(function (n) {
var state = openItems.indexOf(n.Id) == -1 ? 'closed' : 'open';
const nodes = result.Items.map(function (n) {
const state = openItems.indexOf(n.Id) == -1 ? 'closed' : 'open';
return getNode(n, state, false);
});
callback.call(scope, nodes);
for (var i = 0, length = nodes.length; i < length; i++) {
for (let i = 0, length = nodes.length; i < length; i++) {
if (nodes[i].state.opened) {
nodesToLoad.push(nodes[i].id);
}
@ -134,7 +134,7 @@ import 'material-icons';
}
function loadNode(page, scope, node, openItems, selectedId, currentUser, callback) {
var id = node.id;
const id = node.id;
if (id == '#') {
loadChildrenOfRootNode(page, scope, callback);
return;
@ -147,7 +147,7 @@ import 'material-icons';
loadMediaFolders(page, scope, openItems, callback);
return;
}
var query = {
const query = {
ParentId: id,
Fields: 'Settings',
IsVirtualUnaired: false,
@ -156,17 +156,17 @@ import 'material-icons';
EnableImages: false,
EnableUserData: false
};
var itemtype = node.li_attr.itemtype;
const itemtype = node.li_attr.itemtype;
if (itemtype != 'Season' && itemtype != 'Series') {
query.SortBy = 'SortName';
}
ApiClient.getItems(Dashboard.getCurrentUserId(), query).then(function (result) {
var nodes = result.Items.map(function (n) {
var state = openItems.indexOf(n.Id) == -1 ? 'closed' : 'open';
const nodes = result.Items.map(function (n) {
const state = openItems.indexOf(n.Id) == -1 ? 'closed' : 'open';
return getNode(n, state, n.Id == selectedId);
});
callback.call(scope, nodes);
for (var i = 0, length = nodes.length; i < length; i++) {
for (let i = 0, length = nodes.length; i < length; i++) {
if (nodes[i].state.opened) {
nodesToLoad.push(nodes[i].id);
}
@ -175,7 +175,7 @@ import 'material-icons';
}
function scrollToNode(id) {
var elem = $('#' + id)[0];
const elem = $('#' + id)[0];
if (elem) {
elem.scrollIntoView();
}
@ -188,8 +188,8 @@ import 'material-icons';
}
function onNodeSelect(event, data) {
var node = data.node;
var eventData = {
const node = data.node;
const eventData = {
id: node.id,
itemType: node.li_attr.itemtype,
serverItemType: node.li_attr.serveritemtype,
@ -210,8 +210,8 @@ import 'material-icons';
}
function onNodeOpen(event, data) {
var page = $(this).parents('.page')[0];
var node = data.node;
const page = $(this).parents('.page')[0];
const node = data.node;
if (node.children) {
loadNodesToLoad(page, node);
}
@ -222,8 +222,8 @@ import 'material-icons';
}
function onNodeLoad(event, data) {
var page = $(this).parents('.page')[0];
var node = data.node;
const page = $(this).parents('.page')[0];
const node = data.node;
if (node.children) {
loadNodesToLoad(page, node);
}
@ -252,9 +252,9 @@ import 'material-icons';
}
function loadNodesToLoad(page, node) {
var children = node.children;
for (var i = 0, length = children.length; i < length; i++) {
var child = children[i];
const children = node.children;
for (let i = 0, length = children.length; i < length; i++) {
const child = children[i];
if (nodesToLoad.indexOf(child) != -1) {
nodesToLoad = nodesToLoad.filter(function (n) {
return n != child;
@ -273,15 +273,15 @@ import 'material-icons';
}
function updateEditorNode(page, item) {
var elem = $('#' + item.Id + '>a', page)[0];
const elem = $('#' + item.Id + '>a', page)[0];
if (elem == null) {
return;
}
$('.editorNode', elem).remove();
$(elem).append(getNodeInnerHtml(item));
if (item.IsFolder) {
var tree = jQuery.jstree._reference('.libraryTree');
var currentNode = tree._get_node(null, false);
const tree = jQuery.jstree._reference('.libraryTree');
const currentNode = tree._get_node(null, false);
tree.refresh(currentNode);
}
}
@ -294,23 +294,23 @@ import 'material-icons';
if (itemId) {
return itemId;
}
var url = window.location.hash || window.location.href;
const url = window.location.hash || window.location.href;
return getParameterByName('id', url);
}
var nodesToLoad = [];
var selectedNodeId;
let nodesToLoad = [];
let selectedNodeId;
$(document).on('itemsaved', '.metadataEditorPage', function (e, item) {
updateEditorNode(this, item);
}).on('pagebeforeshow', '.metadataEditorPage', function () {
/* eslint-disable-next-line @babel/no-unused-expressions */
import('css!assets/css/metadataeditor.css');
}).on('pagebeforeshow', '.metadataEditorPage', function () {
var page = this;
const page = this;
Dashboard.getCurrentUser().then(function (user) {
var id = getCurrentItemId();
const id = getCurrentItemId();
if (id) {
ApiClient.getAncestorItems(id, user.Id).then(function (ancestors) {
var ids = ancestors.map(function (i) {
const ids = ancestors.map(function (i) {
return i.Id;
});
initializeTree(page, user, ids, id);
@ -320,13 +320,13 @@ import 'material-icons';
}
});
}).on('pagebeforehide', '.metadataEditorPage', function () {
var page = this;
const page = this;
$('.libraryTree', page).off('select_node.jstree', onNodeSelect).off('open_node.jstree', onNodeOpen).off('load_node.jstree', onNodeLoad);
});
var itemId;
let itemId;
window.MetadataEditor = {
getItemPromise: function () {
var currentItemId = getCurrentItemId();
const currentItemId = getCurrentItemId();
if (currentItemId) {
return ApiClient.getItem(Dashboard.getCurrentUserId(), currentItemId);
}

View file

@ -1,11 +1,8 @@
import multiDownload from 'multi-download';
import shell from 'shell';
export function download(items) {
if (window.NativeShell) {
items.map(function (item) {
window.NativeShell.downloadFile(item);
});
} else {
if (!shell.downloadFiles(items)) {
multiDownload(items.map(function (item) {
return item.url;
}));

View file

@ -22,47 +22,47 @@
import appHost from 'apphost';
var _GAMEPAD_A_BUTTON_INDEX = 0;
var _GAMEPAD_B_BUTTON_INDEX = 1;
var _GAMEPAD_DPAD_UP_BUTTON_INDEX = 12;
var _GAMEPAD_DPAD_DOWN_BUTTON_INDEX = 13;
var _GAMEPAD_DPAD_LEFT_BUTTON_INDEX = 14;
var _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX = 15;
var _GAMEPAD_A_KEY = 'GamepadA';
var _GAMEPAD_B_KEY = 'GamepadB';
var _GAMEPAD_DPAD_UP_KEY = 'GamepadDPadUp';
var _GAMEPAD_DPAD_DOWN_KEY = 'GamepadDPadDown';
var _GAMEPAD_DPAD_LEFT_KEY = 'GamepadDPadLeft';
var _GAMEPAD_DPAD_RIGHT_KEY = 'GamepadDPadRight';
var _GAMEPAD_LEFT_THUMBSTICK_UP_KEY = 'GamepadLeftThumbStickUp';
var _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEY = 'GamepadLeftThumbStickDown';
var _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEY = 'GamepadLeftThumbStickLeft';
var _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEY = 'GamepadLeftThumbStickRight';
var _GAMEPAD_A_KEYCODE = 0;
var _GAMEPAD_B_KEYCODE = 27;
var _GAMEPAD_DPAD_UP_KEYCODE = 38;
var _GAMEPAD_DPAD_DOWN_KEYCODE = 40;
var _GAMEPAD_DPAD_LEFT_KEYCODE = 37;
var _GAMEPAD_DPAD_RIGHT_KEYCODE = 39;
var _GAMEPAD_LEFT_THUMBSTICK_UP_KEYCODE = 38;
var _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEYCODE = 40;
var _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEYCODE = 37;
var _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEYCODE = 39;
var _THUMB_STICK_THRESHOLD = 0.75;
const _GAMEPAD_A_BUTTON_INDEX = 0;
const _GAMEPAD_B_BUTTON_INDEX = 1;
const _GAMEPAD_DPAD_UP_BUTTON_INDEX = 12;
const _GAMEPAD_DPAD_DOWN_BUTTON_INDEX = 13;
const _GAMEPAD_DPAD_LEFT_BUTTON_INDEX = 14;
const _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX = 15;
const _GAMEPAD_A_KEY = 'GamepadA';
const _GAMEPAD_B_KEY = 'GamepadB';
const _GAMEPAD_DPAD_UP_KEY = 'GamepadDPadUp';
const _GAMEPAD_DPAD_DOWN_KEY = 'GamepadDPadDown';
const _GAMEPAD_DPAD_LEFT_KEY = 'GamepadDPadLeft';
const _GAMEPAD_DPAD_RIGHT_KEY = 'GamepadDPadRight';
const _GAMEPAD_LEFT_THUMBSTICK_UP_KEY = 'GamepadLeftThumbStickUp';
const _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEY = 'GamepadLeftThumbStickDown';
const _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEY = 'GamepadLeftThumbStickLeft';
const _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEY = 'GamepadLeftThumbStickRight';
const _GAMEPAD_A_KEYCODE = 0;
const _GAMEPAD_B_KEYCODE = 27;
const _GAMEPAD_DPAD_UP_KEYCODE = 38;
const _GAMEPAD_DPAD_DOWN_KEYCODE = 40;
const _GAMEPAD_DPAD_LEFT_KEYCODE = 37;
const _GAMEPAD_DPAD_RIGHT_KEYCODE = 39;
const _GAMEPAD_LEFT_THUMBSTICK_UP_KEYCODE = 38;
const _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEYCODE = 40;
const _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEYCODE = 37;
const _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEYCODE = 39;
const _THUMB_STICK_THRESHOLD = 0.75;
var _leftThumbstickUpPressed = false;
var _leftThumbstickDownPressed = false;
var _leftThumbstickLeftPressed = false;
var _leftThumbstickRightPressed = false;
var _dPadUpPressed = false;
var _dPadDownPressed = false;
var _dPadLeftPressed = false;
var _dPadRightPressed = false;
var _gamepadAPressed = false;
var _gamepadBPressed = false;
let _leftThumbstickUpPressed = false;
let _leftThumbstickDownPressed = false;
let _leftThumbstickLeftPressed = false;
let _leftThumbstickRightPressed = false;
let _dPadUpPressed = false;
let _dPadDownPressed = false;
let _dPadLeftPressed = false;
let _dPadRightPressed = false;
let _gamepadAPressed = false;
let _gamepadBPressed = false;
// The set of buttons on the gamepad we listen for.
var ProcessedButtons = [
const ProcessedButtons = [
_GAMEPAD_DPAD_UP_BUTTON_INDEX,
_GAMEPAD_DPAD_DOWN_BUTTON_INDEX,
_GAMEPAD_DPAD_LEFT_BUTTON_INDEX,
@ -71,7 +71,7 @@ var ProcessedButtons = [
_GAMEPAD_B_BUTTON_INDEX
];
var _ButtonPressedState = {};
const _ButtonPressedState = {};
_ButtonPressedState.getgamepadA = function () {
return _gamepadAPressed;
};
@ -162,11 +162,11 @@ _ButtonPressedState.setdPadRight = function (newPressedState) {
_dPadRightPressed = newPressedState;
};
var times = {};
const times = {};
function throttle(key) {
var time = times[key] || 0;
var now = new Date().getTime();
const time = times[key] || 0;
const now = new Date().getTime();
if ((now - time) >= 200) {
//times[key] = now;
@ -180,7 +180,7 @@ function resetThrottle(key) {
times[key] = new Date().getTime();
}
var isElectron = navigator.userAgent.toLowerCase().indexOf('electron') !== -1;
const isElectron = navigator.userAgent.toLowerCase().indexOf('electron') !== -1;
function allowInput() {
// This would be nice but always seems to return true with electron
if (!isElectron && document.hidden) { /* eslint-disable-line compat/compat */
@ -199,7 +199,7 @@ function raiseEvent(name, key, keyCode) {
return;
}
var event = document.createEvent('Event');
const event = document.createEvent('Event');
event.initEvent(name, true, true);
event.key = key;
event.keyCode = keyCode;
@ -218,7 +218,7 @@ function raiseKeyEvent(oldPressedState, newPressedState, key, keyCode, enableRep
// No-op if oldPressedState === newPressedState
if (newPressedState === true) {
// button down
var fire = false;
let fire = false;
// always fire if this is the initial down press
if (oldPressedState === false) {
@ -244,19 +244,19 @@ function raiseKeyEvent(oldPressedState, newPressedState, key, keyCode, enableRep
}
}
var inputLoopTimer;
let inputLoopTimer;
function runInputLoop() {
// Get the latest gamepad state.
var gamepads = navigator.getGamepads(); /* eslint-disable-line compat/compat */
for (var i = 0, len = gamepads.length; i < len; i++) {
var gamepad = gamepads[i];
const gamepads = navigator.getGamepads(); /* eslint-disable-line compat/compat */
for (let i = 0, len = gamepads.length; i < len; i++) {
const gamepad = gamepads[i];
if (!gamepad) {
continue;
}
// Iterate through the axes
var axes = gamepad.axes;
var leftStickX = axes[0];
var leftStickY = axes[1];
const axes = gamepad.axes;
const leftStickX = axes[0];
const leftStickY = axes[1];
if (leftStickX > _THUMB_STICK_THRESHOLD) { // Right
_ButtonPressedState.setleftThumbstickRight(true);
} else if (leftStickX < -_THUMB_STICK_THRESHOLD) { // Left
@ -272,8 +272,8 @@ function runInputLoop() {
_ButtonPressedState.setleftThumbstickDown(false);
}
// Iterate through the buttons to see if Left thumbstick, DPad, A and B are pressed.
var buttons = gamepad.buttons;
for (var j = 0, len = buttons.length; j < len; j++) {
const buttons = gamepad.buttons;
for (let j = 0, len = buttons.length; j < len; j++) {
if (ProcessedButtons.indexOf(j) !== -1) {
if (buttons[j].pressed) {
switch (j) {
@ -355,9 +355,9 @@ function stopInputLoop() {
}
function isGamepadConnected() {
var gamepads = navigator.getGamepads(); /* eslint-disable-line compat/compat */
for (var i = 0, len = gamepads.length; i < len; i++) {
var gamepad = gamepads[i];
const gamepads = navigator.getGamepads(); /* eslint-disable-line compat/compat */
for (let i = 0, len = gamepads.length; i < len; i++) {
const gamepad = gamepads[i];
if (gamepad && gamepad.connected) {
return true;
}

View file

@ -2,7 +2,7 @@
/* eslint-disable indent */
export function getDeviceIcon(device) {
var baseUrl = 'assets/img/devices/';
const baseUrl = 'assets/img/devices/';
switch (device.AppName || device.Client) {
case 'Samsung Smart TV':
return baseUrl + 'samsung.svg';

View file

@ -6,7 +6,7 @@ export function getSavedQueryKey(modifier) {
}
export function loadSavedQueryValues(key, query) {
var values = userSettings.get(key);
let values = userSettings.get(key);
if (values) {
values = JSON.parse(values);
@ -17,7 +17,7 @@ export function loadSavedQueryValues(key, query) {
}
export function saveQueryValues(key, query) {
var values = {};
const values = {};
if (query.SortBy) {
values.SortBy = query.SortBy;
@ -39,7 +39,7 @@ export function getSavedView (key) {
}
export function showLayoutMenu (button, currentLayout, views) {
var dispatchEvent = true;
let dispatchEvent = true;
if (!views) {
dispatchEvent = false;
@ -47,7 +47,7 @@ export function showLayoutMenu (button, currentLayout, views) {
views = views ? views.split(',') : ['List', 'Poster', 'PosterCard', 'Thumb', 'ThumbCard'];
}
var menuItems = views.map(function (v) {
const menuItems = views.map(function (v) {
return {
name: globalize.translate(v),
id: v,
@ -79,12 +79,12 @@ export function showLayoutMenu (button, currentLayout, views) {
}
export function getQueryPagingHtml (options) {
var startIndex = options.startIndex;
var limit = options.limit;
var totalRecordCount = options.totalRecordCount;
var html = '';
var recordsEnd = Math.min(startIndex + limit, totalRecordCount);
var showControls = limit < totalRecordCount;
const startIndex = options.startIndex;
const limit = options.limit;
const totalRecordCount = options.totalRecordCount;
let html = '';
const recordsEnd = Math.min(startIndex + limit, totalRecordCount);
const showControls = limit < totalRecordCount;
if (html += '<div class="listPaging">', showControls) {
html += '<span style="vertical-align:middle;">';
@ -124,10 +124,10 @@ export function showSortMenu (options) {
import('emby-radio')
]).then(([{default: dialogHelper}]) => {
function onSortByChange() {
var newValue = this.value;
const newValue = this.value;
if (this.checked) {
var changed = options.query.SortBy != newValue;
const changed = options.query.SortBy != newValue;
options.query.SortBy = newValue.replace('_', ',');
options.query.StartIndex = 0;
@ -138,10 +138,10 @@ export function showSortMenu (options) {
}
function onSortOrderChange() {
var newValue = this.value;
const newValue = this.value;
if (this.checked) {
var changed = options.query.SortOrder != newValue;
const changed = options.query.SortOrder != newValue;
options.query.SortOrder = newValue;
options.query.StartIndex = 0;
@ -151,7 +151,7 @@ export function showSortMenu (options) {
}
}
var dlg = dialogHelper.createDialog({
const dlg = dialogHelper.createDialog({
removeOnClose: true,
modal: false,
entryAnimationDuration: 160,
@ -160,18 +160,18 @@ export function showSortMenu (options) {
dlg.classList.add('ui-body-a');
dlg.classList.add('background-theme-a');
dlg.classList.add('formDialog');
var html = '';
let html = '';
html += '<div style="margin:0;padding:1.25em 1.5em 1.5em;">';
html += '<h2 style="margin:0 0 .5em;">';
html += globalize.translate('HeaderSortBy');
html += '</h2>';
var i;
var length;
var isChecked;
let i;
let length;
let isChecked;
html += '<div>';
for (i = 0, length = options.items.length; i < length; i++) {
var option = options.items[i];
var radioValue = option.id.replace(',', '_');
const option = options.items[i];
const radioValue = option.id.replace(',', '_');
isChecked = (options.query.SortBy || '').replace(',', '_') == radioValue ? ' checked' : '';
html += '<label class="radio-label-block"><input type="radio" is="emby-radio" name="SortBy" data-id="' + option.id + '" value="' + radioValue + '" class="menuSortBy" ' + isChecked + ' /><span>' + option.name + '</span></label>';
}
@ -189,13 +189,13 @@ export function showSortMenu (options) {
html += '</div>';
dlg.innerHTML = html;
dialogHelper.open(dlg);
var sortBys = dlg.querySelectorAll('.menuSortBy');
const sortBys = dlg.querySelectorAll('.menuSortBy');
for (i = 0, length = sortBys.length; i < length; i++) {
sortBys[i].addEventListener('change', onSortByChange);
}
var sortOrders = dlg.querySelectorAll('.menuSortOrder');
const sortOrders = dlg.querySelectorAll('.menuSortOrder');
for (i = 0, length = sortOrders.length; i < length; i++) {
sortOrders[i].addEventListener('change', onSortOrderChange);

View file

@ -29,10 +29,10 @@ import 'flexStyles';
html += '</div>';
html += '<div class="headerRight">';
html += '<span class="headerSelectedPlayer"></span>';
html += `<button is="paper-icon-button-light" class="headerSyncButton syncButton headerButton headerButtonRight hide" title="${globalize.translate('ButtonSyncPlay')}"><span class="material-icons sync_disabled"></span></button>`;
html += `<button is="paper-icon-button-light" class="headerAudioPlayerButton audioPlayerButton headerButton headerButtonRight hide" title="${globalize.translate('ButtonPlayer')}"><span class="material-icons music_note"></span></button>`;
html += `<button is="paper-icon-button-light" class="headerCastButton castButton headerButton headerButtonRight hide" title="${globalize.translate('ButtonCast')}"><span class="material-icons cast"></span></button>`;
html += `<button type="button" is="paper-icon-button-light" class="headerButton headerButtonRight headerSearchButton hide" title="${globalize.translate('Search')}"><span class="material-icons search"></span></button>`;
html += '<button is="paper-icon-button-light" class="headerSyncButton syncButton headerButton headerButtonRight hide"><span class="material-icons sync_disabled"></span></button>';
html += '<button is="paper-icon-button-light" class="headerAudioPlayerButton audioPlayerButton headerButton headerButtonRight hide"><span class="material-icons music_note"></span></button>';
html += '<button is="paper-icon-button-light" class="headerCastButton castButton headerButton headerButtonRight hide"><span class="material-icons cast"></span></button>';
html += '<button type="button" is="paper-icon-button-light" class="headerButton headerButtonRight headerSearchButton hide"><span class="material-icons search"></span></button>';
html += '<button is="paper-icon-button-light" class="headerButton headerButtonRight headerUserButton hide"><span class="material-icons person"></span></button>';
html += '</div>';
html += '</div>';
@ -43,13 +43,16 @@ import 'flexStyles';
skinHeader.classList.add('skinHeader-blurred');
skinHeader.innerHTML = html;
headerBackButton = skinHeader.querySelector('.headerBackButton');
headerHomeButton = skinHeader.querySelector('.headerHomeButton');
mainDrawerButton = skinHeader.querySelector('.mainDrawerButton');
headerUserButton = skinHeader.querySelector('.headerUserButton');
headerCastButton = skinHeader.querySelector('.headerCastButton');
headerAudioPlayerButton = skinHeader.querySelector('.headerAudioPlayerButton');
headerSearchButton = skinHeader.querySelector('.headerSearchButton');
headerSyncButton = skinHeader.querySelector('.headerSyncButton');
retranslateUi();
lazyLoadViewMenuBarImages();
bindMenuEvents();
updateCastIcon();
@ -73,8 +76,26 @@ import 'flexStyles';
appRouter.back();
}
function retranslateUi() {
if (headerSyncButton) {
headerSyncButton.title = globalize.translate('ButtonSyncPlay');
}
if (headerAudioPlayerButton) {
headerAudioPlayerButton.title = globalize.translate('ButtonPlayer');
}
if (headerCastButton) {
headerCastButton.title = globalize.translate('ButtonCast');
}
if (headerSearchButton) {
headerSearchButton.title = globalize.translate('Search');
}
}
function updateUserInHeader(user) {
renderHeader();
retranslateUi();
let hasImage;
@ -153,14 +174,10 @@ import 'flexStyles';
}
function bindMenuEvents() {
mainDrawerButton = document.querySelector('.mainDrawerButton');
if (mainDrawerButton) {
mainDrawerButton.addEventListener('click', toggleMainDrawer);
}
const headerBackButton = skinHeader.querySelector('.headerBackButton');
if (headerBackButton) {
headerBackButton.addEventListener('click', onBackClick);
}
@ -772,10 +789,6 @@ import 'flexStyles';
}
function updateBackButton(page) {
if (!headerBackButton) {
headerBackButton = document.querySelector('.headerBackButton');
}
if (headerBackButton) {
if (page.getAttribute('data-backbutton') !== 'false' && appRouter.canGoBack()) {
headerBackButton.classList.remove('hide');

View file

@ -1,16 +1,16 @@
import browser from 'browser';
function fallback(urls) {
var i = 0;
let i = 0;
(function createIframe() {
var frame = document.createElement('iframe');
const 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 () {
const interval = setInterval(function () {
if (frame.contentWindow.document.readyState === 'complete' || frame.contentWindow.document.readyState === 'interactive') {
clearInterval(interval);
@ -28,14 +28,14 @@ function fallback(urls) {
}
function sameDomain(url) {
var a = document.createElement('a');
const a = document.createElement('a');
a.href = url;
return window.location.hostname === a.hostname && window.location.protocol === a.protocol;
}
function download(url) {
var a = document.createElement('a');
const a = document.createElement('a');
a.download = '';
a.href = url;
// firefox doesn't support `a.click()`...
@ -51,7 +51,7 @@ export default function (urls) {
return fallback(urls);
}
var delay = 0;
let delay = 0;
urls.forEach(function (url) {
// the download init has to be sequential for firefox if the urls are not on the same domain

View file

@ -3,7 +3,7 @@ define(['listView'], function (listView) {
function getFetchPlaylistItemsFn(itemId) {
return function () {
var query = {
const query = {
Fields: 'PrimaryImageAspectRatio,UserData',
EnableImageTypes: 'Primary,Backdrop,Banner,Thumb',
UserId: ApiClient.getCurrentUserId()
@ -28,7 +28,7 @@ define(['listView'], function (listView) {
}
function init(page, item) {
var elem = page.querySelector('#childrenContent .itemsContainer');
const elem = page.querySelector('#childrenContent .itemsContainer');
elem.classList.add('vertical-list');
elem.classList.remove('vertical-wrap');
elem.enableDragReordering(true);

View file

@ -17,7 +17,7 @@ import 'detailtablecss';
console.groupCollapsed('defining core routes');
function defineRoute(newRoute) {
var path = newRoute.alias ? newRoute.alias : newRoute.path;
const path = newRoute.alias ? newRoute.alias : newRoute.path;
console.debug('defining route: ' + path);
newRoute.dictionary = 'core';
Emby.Page.addRoute(path, newRoute);
@ -159,10 +159,11 @@ import 'detailtablecss';
});
defineRoute({
path: '/quickconnect.html',
alias: '/quickConnect.html',
path: '/controllers/dashboard/quickConnect.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/quickconnect'
controller: 'dashboard/quickConnect'
});
defineRoute({

View file

@ -209,7 +209,7 @@ export class UserSettings {
}
val = this.get('enableBackdrops', false);
return val !== 'false';
return val === 'true';
}
/**

View file

@ -1,9 +1,48 @@
let data;
const urlResolver = document.createElement('a');
// `fetch` with `file:` support
// Recent browsers seem to support `file` protocol under some conditions.
// Based on https://github.com/github/fetch/pull/92#issuecomment-174730593
// https://github.com/github/fetch/pull/92#issuecomment-512187452
async function fetchLocal(url, options) {
urlResolver.href = url;
const requestURL = urlResolver.href;
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest;
xhr.onload = () => {
// `file` protocol has invalid OK status
let status = xhr.status;
if (requestURL.startsWith('file:') && status === 0) {
status = 200;
}
/* eslint-disable-next-line compat/compat */
resolve(new Response(xhr.responseText, {status: status}));
};
xhr.onerror = () => {
reject(new TypeError('Local request failed'));
};
xhr.open('GET', url);
if (options && options.cache) {
xhr.setRequestHeader('Cache-Control', options.cache);
}
xhr.send(null);
});
}
async function getConfig() {
if (data) return Promise.resolve(data);
try {
const response = await fetch('config.json', {
const response = await fetchLocal('config.json', {
cache: 'no-cache'
});
@ -22,7 +61,7 @@ async function getConfig() {
async function getDefaultConfig() {
try {
const response = await fetch('config.template.json', {
const response = await fetchLocal('config.template.json', {
cache: 'no-cache'
});
@ -63,3 +102,12 @@ export function getPlugins() {
return [];
});
}
export function getFonts() {
return getConfig().then(config => {
return config.fonts;
}).catch(error => {
console.log('cannot get web config:', error);
return [];
});
}

View file

@ -1,20 +1,43 @@
// TODO: This seems like a good candidate for deprecation
export default {
openUrl: function (url, target) {
enableFullscreen: function() {
window.NativeShell?.enableFullscreen();
},
disableFullscreen: function() {
window.NativeShell?.disableFullscreen();
},
openUrl: function(url, target) {
if (window.NativeShell) {
window.NativeShell.openUrl(url, target);
} else {
window.open(url, target || '_blank');
}
},
enableFullscreen: function () {
if (window.NativeShell) {
window.NativeShell.enableFullscreen();
}
updateMediaSession(mediaInfo) {
window.NativeShell?.updateMediaSession(mediaInfo);
},
disableFullscreen: function () {
hideMediaSession() {
window.NativeShell?.hideMediaSession();
},
/**
* Notify the NativeShell about volume level changes.
* Useful for e.g. remote playback.
*/
updateVolumeLevel(volume) {
window.NativeShell?.updateVolumeLevel(volume);
},
/**
* Download specified files with NativeShell if possible
*
* @returns true on success
*/
downloadFiles(items) {
if (window.NativeShell) {
window.NativeShell.disableFullscreen();
items.map(function(item) {
window.NativeShell.downloadFile(item);
});
return true;
}
return false;
}
};

View file

@ -1,10 +1,10 @@
window.getWindowLocationSearch = function(win) {
'use strict';
var search = (win || window).location.search;
let search = (win || window).location.search;
if (!search) {
var index = window.location.href.indexOf('?');
const index = window.location.href.indexOf('?');
if (index != -1) {
search = window.location.href.substring(index);
@ -18,9 +18,9 @@ window.getParameterByName = function(name, url) {
'use strict';
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
var regexS = '[\\?&]' + name + '=([^&#]*)';
var regex = new RegExp(regexS, 'i');
var results = regex.exec(url || getWindowLocationSearch());
const regexS = '[\\?&]' + name + '=([^&#]*)';
const regex = new RegExp(regexS, 'i');
const results = regex.exec(url || getWindowLocationSearch());
if (results == null) {
return '';
@ -33,7 +33,7 @@ window.pageClassOn = function(eventName, className, fn) {
'use strict';
document.addEventListener(eventName, function (event) {
var target = event.target;
const target = event.target;
if (target.classList.contains(className)) {
fn.call(target, event);
@ -45,7 +45,7 @@ window.pageIdOn = function(eventName, id, fn) {
'use strict';
document.addEventListener(eventName, function (event) {
var target = event.target;
const target = event.target;
if (target.id === id) {
fn.call(target, event);
@ -53,7 +53,7 @@ window.pageIdOn = function(eventName, id, fn) {
});
};
var AppInfo = {};
const AppInfo = {};
function initClient() {
function bindConnectionManagerEvents(connectionManager, events, userSettings) {
@ -61,7 +61,7 @@ function initClient() {
window.connectionManager.currentApiClient = function () {
if (!localApiClient) {
var server = window.connectionManager.getLastUsedServer();
const server = window.connectionManager.getLastUsedServer();
if (server) {
localApiClient = window.connectionManager.getApiClient(server.Id);
@ -86,11 +86,11 @@ function initClient() {
return require(['connectionManagerFactory', 'apphost', 'credentialprovider', 'events', 'userSettings'], function (ConnectionManager, appHost, credentialProvider, events, userSettings) {
appHost = appHost.default || appHost;
var credentialProviderInstance = new credentialProvider();
var promises = [appHost.init()];
const credentialProviderInstance = new credentialProvider();
const promises = [appHost.init()];
return Promise.all(promises).then(function (responses) {
var capabilities = Dashboard.capabilities(appHost);
const capabilities = Dashboard.capabilities(appHost);
window.connectionManager = new ConnectionManager(credentialProviderInstance, appHost.appName(), appHost.appVersion(), appHost.deviceName(), appHost.deviceId(), capabilities);
@ -102,7 +102,7 @@ function initClient() {
return require(['apiclient', 'clientUtils'], function (apiClientFactory, clientUtils) {
console.debug('creating ApiClient singleton');
var apiClient = new apiClientFactory(Dashboard.serverAddress(), appHost.appName(), appHost.appVersion(), appHost.deviceName(), appHost.deviceId());
const apiClient = new apiClientFactory(Dashboard.serverAddress(), appHost.appName(), appHost.appVersion(), appHost.deviceName(), appHost.deviceId());
apiClient.enableAutomaticNetworking = false;
apiClient.manualAddressOnly = true;
@ -194,7 +194,7 @@ function initClient() {
require(['clientUtils']);
var promises = [];
const promises = [];
if (!window.fetch) {
promises.push(require(['fetch']));
}
@ -203,7 +203,7 @@ function initClient() {
createConnectionManager().then(function () {
console.debug('initAfterDependencies promises resolved');
require(['globalize', 'browser'], function (globalize, browser) {
require(['globalize', 'browser'], function (globalize, {default: browser}) {
window.Globalize = globalize;
loadCoreDictionary(globalize).then(function () {
onGlobalizeInit(browser, globalize);
@ -226,8 +226,8 @@ function initClient() {
}
function loadCoreDictionary(globalize) {
var languages = ['ar', 'be-by', 'bg-bg', 'ca', 'cs', 'da', 'de', 'el', 'en-gb', 'en-us', 'es', 'es-ar', 'es-mx', 'fa', 'fi', 'fr', 'fr-ca', 'gsw', 'he', 'hi-in', 'hr', 'hu', 'id', 'it', 'kk', 'ko', 'lt-lt', 'ms', 'nb', 'nl', 'pl', 'pt-br', 'pt-pt', 'ro', 'ru', 'sk', 'sl-si', 'sv', 'tr', 'uk', 'vi', 'zh-cn', 'zh-hk', 'zh-tw'];
var translations = languages.map(function (language) {
const languages = ['ar', 'be-by', 'bg-bg', 'ca', 'cs', 'da', 'de', 'el', 'en-gb', 'en-us', 'es', 'es-ar', 'es-mx', 'fa', 'fi', 'fr', 'fr-ca', 'gsw', 'he', 'hi-in', 'hr', 'hu', 'id', 'it', 'ja', 'kk', 'ko', 'lt-lt', 'ms', 'nb', 'nl', 'pl', 'pt-br', 'pt-pt', 'ro', 'ru', 'sk', 'sl-si', 'sv', 'tr', 'uk', 'vi', 'zh-cn', 'zh-hk', 'zh-tw'];
const translations = languages.map(function (language) {
return {
lang: language,
path: 'strings/' + language + '.json'
@ -367,7 +367,7 @@ function initClient() {
require(['playerSelectionMenu']);
var apiClient = window.connectionManager && window.connectionManager.currentApiClient();
const apiClient = window.connectionManager && window.connectionManager.currentApiClient();
if (apiClient) {
fetch(apiClient.getUrl('Branding/Css'))
.then(function(response) {
@ -410,8 +410,8 @@ function initClient() {
}
function onWebComponentsReady() {
var componentsPath = getComponentsPath();
var scriptsPath = getScriptsPath();
const componentsPath = getComponentsPath();
const scriptsPath = getScriptsPath();
define('filesystem', [scriptsPath + '/filesystem'], returnFirstDependency);
@ -441,18 +441,18 @@ function initClient() {
init();
}
var promise;
var localApiClient;
let promise;
let localApiClient;
function initRequireJs() {
var urlArgs = 'v=' + (window.dashboardVersion || new Date().getDate());
const urlArgs = 'v=' + (window.dashboardVersion || new Date().getDate());
var bowerPath = getBowerPath();
var componentsPath = getComponentsPath();
var elementsPath = getElementsPath();
var scriptsPath = getScriptsPath();
const bowerPath = getBowerPath();
const componentsPath = getComponentsPath();
const elementsPath = getElementsPath();
const scriptsPath = getScriptsPath();
var paths = {
const paths = {
browserdeviceprofile: 'scripts/browserDeviceProfile',
browser: 'scripts/browser',
libraryBrowser: 'scripts/libraryBrowser',
@ -495,6 +495,7 @@ function initClient() {
'flvjs',
'jstree',
'epubjs',
'pdfjs',
'jQuery',
'hlsjs',
'howler',
@ -505,7 +506,6 @@ function initClient() {
'sortable',
'webcomponents',
'material-icons',
'jellyfin-noto',
'date-fns',
'page',
'polyfill',
@ -528,7 +528,7 @@ function initClient() {
});
promise = require(['fetch'])
.then(() => require(['jQuery', 'polyfill', 'fast-text-encoding', 'intersection-observer', 'classlist-polyfill', 'css!assets/css/site', 'jellyfin-noto'], (jQuery) => {
.then(() => require(['jQuery', 'polyfill', 'fast-text-encoding', 'intersection-observer', 'classlist-polyfill', 'css!assets/css/site'], (jQuery) => {
// Expose jQuery globally
window.$ = jQuery;
window.jQuery = jQuery;

View file

@ -13,7 +13,7 @@ export default function (options) {
function updateTasks(tasks) {
const task = tasks.filter(function (t) {
return t.ScheduledTask.Key == options.taskKey;
return t.Key == options.taskKey;
})[0];
if (options.panel) {

View file

@ -1,10 +1,10 @@
import * as webSettings from 'webSettings';
var themeStyleElement = document.querySelector('#cssTheme');
var currentThemeId;
let themeStyleElement = document.querySelector('#cssTheme');
let currentThemeId;
function unloadTheme() {
var elem = themeStyleElement;
const elem = themeStyleElement;
if (elem) {
elem.removeAttribute('href');
currentThemeId = null;
@ -17,7 +17,7 @@ function getThemes() {
function getThemeStylesheetInfo(id) {
return getThemes().then(themes => {
var theme = themes.find(theme => {
const theme = themes.find(theme => {
return id ? theme.id === id : theme.default;
});
@ -41,7 +41,7 @@ function setTheme(id) {
return;
}
var linkUrl = info.stylesheetPath;
const linkUrl = info.stylesheetPath;
unloadTheme();
let link = themeStyleElement;

Binary file not shown.

View file

@ -350,5 +350,152 @@
"GroupVersions": "Groepeer weergawes",
"Guide": "Gids",
"GuestStar": "Gaskunstenaar",
"GuideProviderLogin": "Skakel In"
"GuideProviderLogin": "Skakel In",
"HeaderSelectMetadataPath": "Kies Metadata Lêerpad",
"HeaderSelectCertificatePath": "Kies Sertifikaat Lêerpad",
"HeaderSecondsValue": "{0} Sekondes",
"HeaderSeasons": "Reekse",
"HeaderScenes": "Tonele",
"HeaderRunningTasks": "Aktiewe Take",
"HeaderRevisionHistory": "Terugvoer Geskiedenis",
"HeaderResponseProfileHelp": "Terugvoer profiele verleen 'n metode om spesiale inligting te kies wat aan die teostel gestuur word wanneer spesifieke media-tipes gespeel word.",
"HeaderResponseProfile": "Terugvoer Profiel",
"HeaderRemoveMediaLocation": "Verwyder Media Lokasie",
"HeaderRemoveMediaFolder": "Verwyder Media Gidslêer",
"HeaderRemoteControl": "Afstandbeheer",
"HeaderRemoteAccessSettings": "Eksterne Toegang Verstellings",
"HeaderRecordingPostProcessing": "Opname Naverwerking",
"HeaderRecordingOptions": "Opname Opsies",
"HeaderRecentlyPlayed": "Onlangs Gespeel",
"HeaderProfileServerSettingsHelp": "Hierdie waardes beheer hoe die bediener homself aan kliënte voorstel.",
"HeaderProfileInformation": "Profiel Inligting",
"HeaderPreferredMetadataLanguage": "Voorkeur Metadata Taal",
"HeaderPluginInstallation": "Inprop-toepassing Installasie",
"HeaderPleaseSignIn": "Skakel asseblief in",
"HeaderPlaybackError": "Terugspeel Fout",
"HeaderPlayback": "Media Terugspeel",
"HeaderPlayOn": "Speel Aan",
"HeaderPlayAll": "Speel Alles",
"HeaderPinCodeReset": "Herstel PIN Kode",
"HeaderPhotoAlbums": "Foto Albums",
"HeaderPaths": "Lêerpaaie",
"HeaderPasswordReset": "Herstel Wagwoord",
"HeaderPassword": "Wagwoord",
"HeaderParentalRatings": "Ouer Beperkings",
"HeaderOtherItems": "Ander Items",
"HeaderOnNow": "Nou Aan",
"HeaderNextVideoPlayingInValue": "Volgende Video Begin Speel in {0}",
"HeaderNextEpisodePlayingInValue": "Volgende Epsiode Begin Speel in {0}",
"HeaderNewDevices": "Nuwe Toestelle",
"HeaderNewApiKey": "Nuwe API Sleutel",
"HeaderNavigation": "Navigasie",
"HeaderMyMediaSmall": "My Media (klein)",
"HeaderMyMedia": "My Media",
"HeaderMyDevice": "My Toestel",
"HeaderMusicQuality": "Musiek Gehalte",
"HeaderMoreLikeThis": "Meer Soos Hierdie",
"HeaderMetadataSettings": "Metadata Verstellings",
"HeaderMediaFolders": "Media Gidslêers",
"HeaderMedia": "Media",
"HeaderLoginFailure": "Aansluit Fout",
"HeaderLiveTvTunerSetup": "Lewendige TV Inskakelaar Konfigurasie",
"HeaderLibrarySettings": "Versameling Verstellings",
"HeaderLibraryOrder": "Versameling Volgorde",
"HeaderLibraryFolders": "Versameling Gidslêers",
"HeaderLibraryAccess": "Versameling Toegang",
"HeaderLibraries": "Versamelings",
"HeaderLatestRecordings": "Nuutste Opnames",
"HeaderLatestMusic": "Nuutste Musiek",
"HeaderLatestMovies": "Nuutste Flieks",
"HeaderLatestMedia": "Nuutste Media",
"HeaderLatestEpisodes": "Nuutste Episodes",
"HeaderKodiMetadataHelp": "Om NFO metadata te aktiveer of deaktiveer, verander 'n versameling en spoor die metadata-berger afdeling.",
"HeaderKeepSeries": "Hou Reeks",
"HeaderKeepRecording": "Hou aan Opneem",
"HeaderInstantMix": "Onmiddelike Mengsel",
"HeaderInstall": "Installeer",
"HeaderImageSettings": "Beeld Verstellinge",
"HeaderImageOptions": "Beel Opsies",
"HeaderIdentifyItemHelp": "Sleutel een of meer soek kriteria in. Verwyder kriteris om soek-resultate te vermeerder.",
"HeaderIdentificationHeader": "Identifikasie Kopstukke",
"HeaderIdentificationCriteriaHelp": "Sleutel minstens een identifikasie kriterium in.",
"HeaderIdentification": "Identifikasie",
"HeaderHttpsSettings": "HTTPS Verstellinge",
"HeaderHttpHeaders": "HTTP Kopstukke",
"HeaderGuideProviders": "TV Gids Data Verskaffers",
"HeaderFrequentlyPlayed": "Gereeld Gespeel",
"HeaderForKids": "Vir Kinders",
"HeaderFetcherSettings": "Soeker Verstellings",
"HeaderFetchImages": "Soek Beelde:",
"HeaderFeatureAccess": "Funksie Toegang",
"HeaderExternalIds": "Eksterne IDs:",
"HeaderError": "Fout",
"HeaderEnabledFieldsHelp": "Ontkies 'n veld om dit te sluit en te verhoed dat die veld se data verander kan word.",
"HeaderEnabledFields": "Geaktiveerde Velde",
"HeaderEditImages": "Wysig Beelde",
"HeaderEasyPinCode": "Maklike PIN Kode",
"HeaderDVR": "DVO",
"HeaderDownloadSync": "Laai af en Synkroniseer",
"HeaderDirectPlayProfileHelp": "Voeg direkte speel profile om aan te dui watter formate die toestelle natuurlik kan hanteer.",
"HeaderDirectPlayProfile": "Direkte Speel Profiel",
"HeaderDevices": "Toestelle",
"HeaderDeviceAccess": "Toestel Toegang",
"HeaderDeveloperInfo": "Ontwikkelaar Inligting",
"HeaderDetectMyDevices": "Tel My Toestelle Op",
"HeaderDeleteTaskTrigger": "Verwyder Taak Sneller",
"HeaderDeleteProvider": "Verwyder Verskaffer",
"HeaderDeleteItems": "Verwyder Items",
"HeaderDeleteItem": "Verwyder Item",
"HeaderDeleteDevice": "Verwyder Toestel",
"HeaderDefaultRecordingSettings": "Standaard Opname Verstellings",
"HeaderDateIssued": "Datum Uitgereik",
"HeaderCustomDlnaProfiles": "Eie Profile",
"HeaderContinueListening": "Hou Aan Luister",
"HeaderContainerProfileHelp": "Houer profiele dui die beperkinge van enige toestelle aan wanneer spesifieke formate gespeel word. Indien 'n beperking van toepassing is sal die media getranskodeer word, selfs as die formaat gestel is vir direkte spel.",
"HeaderContainerProfile": "Houer Profiel",
"HeaderConnectionFailure": "Konneksie Fout",
"HeaderConnectToServer": "Konnekteer aan Bediener",
"HeaderConfirmRevokeApiKey": "Herroep API Sleutel",
"HeaderConfirmProfileDeletion": "Bevestig Profiel Verwydering",
"HeaderConfirmPluginInstallation": "Bevestig Inprop-Toepassing Installasie",
"HeaderConfigureRemoteAccess": "Stel Eksterne Toegang Op",
"HeaderCodecProfileHelp": "Codec profiele dui die beperkinge van 'n toestel aan wanneer spesifieke codecs gespeel word. As 'n beperking van toepassing is sal die media getranskodeer word, selfs al is die codec verstel vir direkte spel.",
"HeaderCodecProfile": "Codec Profiel",
"HeaderChapterImages": "Hoofstuk Beelde",
"HeaderChannelAccess": "Kanaal Toegang",
"HeaderCastAndCrew": "Rolverdeling en Bemanning",
"HeaderCancelSeries": "Kanselleer Reeks",
"HeaderCancelRecording": "Kanselleer Opname",
"HeaderBranding": "Handelsmerk",
"HeaderBlockItemsWithNoRating": "Blok items met geen of onherkenbare beperkingsinligting:",
"HeaderAudioSettings": "Klank Verstellings",
"HeaderAudioBooks": "Klankboeke",
"HeaderAppearsOn": "Verskyn Op",
"HeaderApp": "Toep",
"ApiKeysCaption": "Lys aktiewe API sleutels",
"HeaderApiKeysHelp": "Eksterne toepassings word vereis om 'n API sleutel te hê om te kommunikeer met die bediener. Sleutels word utigereik deur in te sluit in die normale verbruikers rekening of deur self 'n sleutel toe te staan aan die toepassing.",
"HeaderApiKeys": "API Sleutels",
"HeaderApiKey": "API Sleutel",
"HeaderAllowMediaDeletionFrom": "Laat Media Verwydering Toe Van",
"HeaderAlert": "Waarskuwing",
"HeaderAdmin": "Admin",
"HeaderAdditionalParts": "Addisionele Dele",
"HeaderAddUpdateImage": "Voeg/Dateer Beeld Op",
"HeaderAddToPlaylist": "Voeg tot Snitlys",
"HeaderAddToCollection": "Voeg by Versameling",
"HeaderActivity": "Aktiwiteit",
"HeaderActiveRecordings": "Aktiewe Opnames",
"HeaderActiveDevices": "Aktiewe Toestelle",
"HeaderAccessScheduleHelp": "Skep 'n toegangskedule om toegang te beperk tydens sekere tydgleuwe.",
"HeaderAccessSchedule": "Toegangsskedule",
"HardwareAccelerationWarning": "Die aktivering van hardeware versnelling mag stabiliteit belemmer in sekere omgewings. Verseker dat u bedryfstelsel en video drywers ten volle op datum is. As u probleme ervaar in die terugspeel van video nadat hierdie geaktiveer is sal u die verstelling terug moet stel na Geen.",
"HDPrograms": "HD programme",
"EncoderPresetHelp": "Kies 'n vinniger waarde om optrede te verbeter, of 'n stadiger waarde om gehalte te verbeter.",
"H264CrfHelp": "Die Konstante Tempo Faktor (KTF) is die standaard kwaliteit verstelling vir die x264 enkodeerder. Jy kan die waardes stel tussen 0 en 51, waar laer waardes beter gehalte lewer (ten koste van hoër lêer-groottes). Sinvolle waardes is tussen 18 en 28. Die standaard vir x264 is 23, gebruik dit dus as 'n beginpunt.",
"GuideProviderSelectListings": "Kies Gids",
"HeaderSelectMetadataPathHelp": "Soek of sleutel lêerpad in wat u vir metadata wil gebruik. Die gidslêer moet skryfbaar wees.",
"HeaderSelectPath": "Kies Lêerpad",
"HeaderSelectTranscodingPath": "Kies Transkodering Tydelike Lêerpad",
"HeaderSelectServerCachePathHelp": "Soek of sleutel die lêerpad in om te gebruik vir bediener kasgeheue. Die gidslêer moet skryfbaar wees.",
"HeaderSelectServerCachePath": "Kies Bediener Kasgeheue Lêerpad"
}

Some files were not shown because too many files have changed in this diff Show more