1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Merge branch 'es6' into es6-migration-5

This commit is contained in:
dkanada 2020-07-19 15:04:07 +09:00 committed by GitHub
commit c17dfafbd9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
150 changed files with 6551 additions and 3268 deletions

View file

@ -121,6 +121,11 @@ div[data-role=page] {
transform: translateY(-100%);
}
.drawerContent {
/* make sure the bottom of the drawer is visible when music is playing */
padding-bottom: 4em;
}
.force-scroll {
overflow-y: scroll;
}

View file

@ -31,7 +31,7 @@ define(['browser', 'dialog', 'globalize'], function (browser, dialog, globalize)
options.buttons = items;
return dialog(options).then(function (result) {
return dialog.show(options).then(function (result) {
if (result === 'ok') {
return Promise.resolve();
}

View file

@ -1,11 +1,22 @@
define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-button-light', 'material-icons'], function (focusManager, layoutManager, dom) {
'use strict';
/* eslint-disable indent */
var selectedButtonClass = 'alphaPickerButton-selected';
/**
* Module alphaPicker.
* @module components/alphaPicker/alphaPicker
*/
import focusManager from 'focusManager';
import layoutManager from 'layoutManager';
import dom from 'dom';
import 'css!./style.css';
import 'paper-icon-button-light';
import 'material-icons';
const selectedButtonClass = 'alphaPickerButton-selected';
function focus() {
var scope = this;
var selected = scope.querySelector('.' + selectedButtonClass);
const scope = this;
const selected = scope.querySelector(`.${selectedButtonClass}`);
if (selected) {
focusManager.focus(selected);
@ -16,7 +27,7 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
function getAlphaPickerButtonClassName(vertical) {
var alphaPickerButtonClassName = 'alphaPickerButton';
let alphaPickerButtonClassName = 'alphaPickerButton';
if (layoutManager.tv) {
alphaPickerButtonClassName += ' alphaPickerButton-tv';
@ -30,12 +41,12 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
}
function getLetterButton(l, vertical) {
return '<button data-value="' + l + '" class="' + getAlphaPickerButtonClassName(vertical) + '">' + l + '</button>';
return `<button data-value="${l}" class="${getAlphaPickerButtonClassName(vertical)}">${l}</button>`;
}
function mapLetters(letters, vertical) {
return letters.map(function (l) {
return letters.map(l => {
return getLetterButton(l, vertical);
});
}
@ -48,26 +59,26 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
element.classList.add('alphaPicker-tv');
}
var vertical = element.classList.contains('alphaPicker-vertical');
const vertical = element.classList.contains('alphaPicker-vertical');
if (!vertical) {
element.classList.add('focuscontainer-x');
}
var html = '';
var letters;
let html = '';
let letters;
var alphaPickerButtonClassName = getAlphaPickerButtonClassName(vertical);
const alphaPickerButtonClassName = getAlphaPickerButtonClassName(vertical);
var rowClassName = 'alphaPickerRow';
let rowClassName = 'alphaPickerRow';
if (vertical) {
rowClassName += ' alphaPickerRow-vertical';
}
html += '<div class="' + rowClassName + '">';
html += `<div class="${rowClassName}">`;
if (options.mode === 'keyboard') {
html += '<button data-value=" " is="paper-icon-button-light" class="' + alphaPickerButtonClassName + '"><span class="material-icons alphaPickerButtonIcon space_bar"></span></button>';
html += `<button data-value=" " is="paper-icon-button-light" class="${alphaPickerButtonClassName}"><span class="material-icons alphaPickerButtonIcon space_bar"></span></button>`;
} else {
letters = ['#'];
html += mapLetters(letters, vertical).join('');
@ -77,11 +88,11 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
html += mapLetters(letters, vertical).join('');
if (options.mode === 'keyboard') {
html += '<button data-value="backspace" is="paper-icon-button-light" class="' + alphaPickerButtonClassName + '"><span class="material-icons alphaPickerButtonIcon backspace"></span></button>';
html += `<button data-value="backspace" is="paper-icon-button-light" class="${alphaPickerButtonClassName}"><span class="material-icons alphaPickerButtonIcon backspace"></span></button>`;
html += '</div>';
letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
html += '<div class="' + rowClassName + '">';
html += `<div class="${rowClassName}">`;
html += '<br/>';
html += mapLetters(letters, vertical).join('');
html += '</div>';
@ -95,227 +106,228 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
element.focus = focus;
}
function AlphaPicker(options) {
export class AlphaPicker {
constructor(options) {
var self = this;
this.options = options;
this.options = options;
var element = options.element;
var itemsContainer = options.itemsContainer;
var itemClass = options.itemClass;
const element = options.element;
const itemsContainer = options.itemsContainer;
const itemClass = options.itemClass;
var itemFocusValue;
var itemFocusTimeout;
let itemFocusValue;
let itemFocusTimeout;
function onItemFocusTimeout() {
itemFocusTimeout = null;
self.value(itemFocusValue);
}
var alphaFocusedElement;
var alphaFocusTimeout;
function onAlphaFocusTimeout() {
alphaFocusTimeout = null;
if (document.activeElement === alphaFocusedElement) {
var value = alphaFocusedElement.getAttribute('data-value');
self.value(value, true);
function onItemFocusTimeout() {
itemFocusTimeout = null;
this.value(itemFocusValue);
}
}
function onAlphaPickerInKeyboardModeClick(e) {
let alphaFocusedElement;
let alphaFocusTimeout;
var alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton');
function onAlphaFocusTimeout() {
if (alphaPickerButton) {
var value = alphaPickerButton.getAttribute('data-value');
element.dispatchEvent(new CustomEvent('alphavalueclicked', {
cancelable: false,
detail: {
value: value
}
}));
}
}
function onAlphaPickerClick(e) {
var alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton');
if (alphaPickerButton) {
var value = alphaPickerButton.getAttribute('data-value');
if ((this._currentValue || '').toUpperCase() === value.toUpperCase()) {
self.value(null, true);
} else {
self.value(value, true);
}
}
}
function onAlphaPickerFocusIn(e) {
if (alphaFocusTimeout) {
clearTimeout(alphaFocusTimeout);
alphaFocusTimeout = null;
if (document.activeElement === alphaFocusedElement) {
const value = alphaFocusedElement.getAttribute('data-value');
this.value(value, true);
}
}
var alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton');
function onAlphaPickerInKeyboardModeClick(e) {
if (alphaPickerButton) {
alphaFocusedElement = alphaPickerButton;
alphaFocusTimeout = setTimeout(onAlphaFocusTimeout, 600);
const alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton');
if (alphaPickerButton) {
const value = alphaPickerButton.getAttribute('data-value');
element.dispatchEvent(new CustomEvent('alphavalueclicked', {
cancelable: false,
detail: {
value
}
}));
}
}
}
function onItemsFocusIn(e) {
function onAlphaPickerClick(e) {
var item = dom.parentWithClass(e.target, itemClass);
const alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton');
if (item) {
var prefix = item.getAttribute('data-prefix');
if (prefix && prefix.length) {
itemFocusValue = prefix[0];
if (itemFocusTimeout) {
clearTimeout(itemFocusTimeout);
if (alphaPickerButton) {
const value = alphaPickerButton.getAttribute('data-value');
if ((this._currentValue || '').toUpperCase() === value.toUpperCase()) {
this.value(null, true);
} else {
this.value(value, true);
}
itemFocusTimeout = setTimeout(onItemFocusTimeout, 100);
}
}
}
self.enabled = function (enabled) {
function onAlphaPickerFocusIn(e) {
if (enabled) {
if (itemsContainer) {
itemsContainer.addEventListener('focus', onItemsFocusIn, true);
if (alphaFocusTimeout) {
clearTimeout(alphaFocusTimeout);
alphaFocusTimeout = null;
}
if (options.mode === 'keyboard') {
element.addEventListener('click', onAlphaPickerInKeyboardModeClick);
}
const alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton');
if (alphaPickerButton) {
alphaFocusedElement = alphaPickerButton;
alphaFocusTimeout = setTimeout(onAlphaFocusTimeout, 600);
}
}
function onItemsFocusIn(e) {
const item = dom.parentWithClass(e.target, itemClass);
if (item) {
const prefix = item.getAttribute('data-prefix');
if (prefix && prefix.length) {
itemFocusValue = prefix[0];
if (itemFocusTimeout) {
clearTimeout(itemFocusTimeout);
}
itemFocusTimeout = setTimeout(onItemFocusTimeout, 100);
}
}
}
this.enabled = function (enabled) {
if (enabled) {
if (itemsContainer) {
itemsContainer.addEventListener('focus', onItemsFocusIn, true);
}
if (options.mode === 'keyboard') {
element.addEventListener('click', onAlphaPickerInKeyboardModeClick);
}
if (options.valueChangeEvent !== 'click') {
element.addEventListener('focus', onAlphaPickerFocusIn, true);
} else {
element.addEventListener('click', onAlphaPickerClick.bind(this));
}
if (options.valueChangeEvent !== 'click') {
element.addEventListener('focus', onAlphaPickerFocusIn, true);
} else {
element.addEventListener('click', onAlphaPickerClick.bind(this));
}
} else {
if (itemsContainer) {
itemsContainer.removeEventListener('focus', onItemsFocusIn, true);
}
element.removeEventListener('click', onAlphaPickerInKeyboardModeClick);
element.removeEventListener('focus', onAlphaPickerFocusIn, true);
element.removeEventListener('click', onAlphaPickerClick.bind(this));
}
};
render(element, options);
this.enabled(true);
this.visible(true);
}
AlphaPicker.prototype.value = function (value, applyValue) {
var element = this.options.element;
var btn;
var selected;
if (value !== undefined) {
if (value != null) {
value = value.toUpperCase();
this._currentValue = value;
if (this.options.mode !== 'keyboard') {
selected = element.querySelector('.' + selectedButtonClass);
try {
btn = element.querySelector('.alphaPickerButton[data-value=\'' + value + '\']');
} catch (err) {
console.error('error in querySelector: ' + err);
if (itemsContainer) {
itemsContainer.removeEventListener('focus', onItemsFocusIn, true);
}
if (btn && btn !== selected) {
btn.classList.add(selectedButtonClass);
element.removeEventListener('click', onAlphaPickerInKeyboardModeClick);
element.removeEventListener('focus', onAlphaPickerFocusIn, true);
element.removeEventListener('click', onAlphaPickerClick.bind(this));
}
};
render(element, options);
this.enabled(true);
this.visible(true);
}
value(value, applyValue) {
const element = this.options.element;
let btn;
let selected;
if (value !== undefined) {
if (value != null) {
value = value.toUpperCase();
this._currentValue = value;
if (this.options.mode !== 'keyboard') {
selected = element.querySelector(`.${selectedButtonClass}`);
try {
btn = element.querySelector(`.alphaPickerButton[data-value='${value}']`);
} catch (err) {
console.error('error in querySelector:', err);
}
if (btn && btn !== selected) {
btn.classList.add(selectedButtonClass);
}
if (selected && selected !== btn) {
selected.classList.remove(selectedButtonClass);
}
}
if (selected && selected !== btn) {
} else {
this._currentValue = value;
selected = element.querySelector(`.${selectedButtonClass}`);
if (selected) {
selected.classList.remove(selectedButtonClass);
}
}
} else {
this._currentValue = value;
selected = element.querySelector('.' + selectedButtonClass);
if (selected) {
selected.classList.remove(selectedButtonClass);
}
}
if (applyValue) {
element.dispatchEvent(new CustomEvent('alphavaluechanged', {
cancelable: false,
detail: {
value
}
}));
}
return this._currentValue;
}
if (applyValue) {
element.dispatchEvent(new CustomEvent('alphavaluechanged', {
cancelable: false,
detail: {
value: value
}
}));
on(name, fn) {
const element = this.options.element;
element.addEventListener(name, fn);
}
return this._currentValue;
};
AlphaPicker.prototype.on = function (name, fn) {
var element = this.options.element;
element.addEventListener(name, fn);
};
AlphaPicker.prototype.off = function (name, fn) {
var element = this.options.element;
element.removeEventListener(name, fn);
};
AlphaPicker.prototype.visible = function (visible) {
var element = this.options.element;
element.style.visibility = visible ? 'visible' : 'hidden';
};
AlphaPicker.prototype.values = function () {
var element = this.options.element;
var elems = element.querySelectorAll('.alphaPickerButton');
var values = [];
for (var i = 0, length = elems.length; i < length; i++) {
values.push(elems[i].getAttribute('data-value'));
off(name, fn) {
const element = this.options.element;
element.removeEventListener(name, fn);
}
return values;
};
visible(visible) {
AlphaPicker.prototype.focus = function () {
const element = this.options.element;
element.style.visibility = visible ? 'visible' : 'hidden';
}
var element = this.options.element;
focusManager.autoFocus(element, true);
};
values() {
AlphaPicker.prototype.destroy = function () {
const element = this.options.element;
const elems = element.querySelectorAll('.alphaPickerButton');
const values = [];
for (let i = 0, length = elems.length; i < length; i++) {
var element = this.options.element;
this.enabled(false);
element.classList.remove('focuscontainer-x');
this.options = null;
};
values.push(elems[i].getAttribute('data-value'));
return AlphaPicker;
});
}
return values;
}
focus() {
const element = this.options.element;
focusManager.autoFocus(element, true);
}
destroy() {
const element = this.options.element;
this.enabled(false);
element.classList.remove('focuscontainer-x');
this.options = null;
}
}
/* eslint-enable indent */
export default AlphaPicker;

View file

@ -21,7 +21,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
};
function beginConnectionWizard() {
backdrop.clear();
backdrop.clearBackdrop();
loading.show();
connectionManager.connect({
enableAutoLogin: appSettings.enableAutoLogin()
@ -657,7 +657,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
}
if (level === 'full' || level === 2) {
backdrop.clear(true);
backdrop.clearBackdrop(true);
document.documentElement.classList.add('transparentDocument');
backgroundContainer.classList.add('backgroundContainer-transparent');
backdropContainer.classList.add('hide');

View file

@ -1,5 +1,11 @@
define(['browser', 'connectionManager', 'playbackManager', 'dom', 'userSettings', 'css!./backdrop'], function (browser, connectionManager, playbackManager, dom, userSettings) {
'use strict';
import browser from 'browser';
import connectionManager from 'connectionManager';
import playbackManager from 'playbackManager';
import dom from 'dom';
import * as userSettings from 'userSettings';
import 'css!./backdrop';
/* eslint-disable indent */
function enableAnimation(elem) {
if (browser.slow) {
@ -22,71 +28,70 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', 'userSettings'
return true;
}
function Backdrop() {
}
class Backdrop {
load(url, parent, existingBackdropImage) {
const img = new Image();
const self = this;
Backdrop.prototype.load = function (url, parent, existingBackdropImage) {
var img = new Image();
var self = this;
img.onload = function () {
if (self.isDestroyed) {
return;
}
var backdropImage = document.createElement('div');
backdropImage.classList.add('backdropImage');
backdropImage.classList.add('displayingBackdropImage');
backdropImage.style.backgroundImage = "url('" + url + "')";
backdropImage.setAttribute('data-url', url);
backdropImage.classList.add('backdropImageFadeIn');
parent.appendChild(backdropImage);
if (!enableAnimation(backdropImage)) {
if (existingBackdropImage && existingBackdropImage.parentNode) {
existingBackdropImage.parentNode.removeChild(existingBackdropImage);
img.onload = () => {
if (self.isDestroyed) {
return;
}
internalBackdrop(true);
return;
}
var onAnimationComplete = function () {
dom.removeEventListener(backdropImage, dom.whichAnimationEvent(), onAnimationComplete, {
const backdropImage = document.createElement('div');
backdropImage.classList.add('backdropImage');
backdropImage.classList.add('displayingBackdropImage');
backdropImage.style.backgroundImage = `url('${url}')`;
backdropImage.setAttribute('data-url', url);
backdropImage.classList.add('backdropImageFadeIn');
parent.appendChild(backdropImage);
if (!enableAnimation(backdropImage)) {
if (existingBackdropImage && existingBackdropImage.parentNode) {
existingBackdropImage.parentNode.removeChild(existingBackdropImage);
}
internalBackdrop(true);
return;
}
const onAnimationComplete = () => {
dom.removeEventListener(backdropImage, dom.whichAnimationEvent(), onAnimationComplete, {
once: true
});
if (backdropImage === self.currentAnimatingElement) {
self.currentAnimatingElement = null;
}
if (existingBackdropImage && existingBackdropImage.parentNode) {
existingBackdropImage.parentNode.removeChild(existingBackdropImage);
}
};
dom.addEventListener(backdropImage, dom.whichAnimationEvent(), onAnimationComplete, {
once: true
});
if (backdropImage === self.currentAnimatingElement) {
self.currentAnimatingElement = null;
}
if (existingBackdropImage && existingBackdropImage.parentNode) {
existingBackdropImage.parentNode.removeChild(existingBackdropImage);
}
internalBackdrop(true);
};
dom.addEventListener(backdropImage, dom.whichAnimationEvent(), onAnimationComplete, {
once: true
});
internalBackdrop(true);
};
img.src = url;
};
Backdrop.prototype.cancelAnimation = function () {
var elem = this.currentAnimatingElement;
if (elem) {
elem.classList.remove('backdropImageFadeIn');
this.currentAnimatingElement = null;
img.src = url;
}
};
Backdrop.prototype.destroy = function () {
this.isDestroyed = true;
this.cancelAnimation();
};
cancelAnimation() {
const elem = this.currentAnimatingElement;
if (elem) {
elem.classList.remove('backdropImageFadeIn');
this.currentAnimatingElement = null;
}
}
var backdropContainer;
destroy() {
this.isDestroyed = true;
this.cancelAnimation();
}
}
let backdropContainer;
function getBackdropContainer() {
if (!backdropContainer) {
backdropContainer = document.querySelector('.backdropContainer');
@ -101,7 +106,7 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', 'userSettings'
return backdropContainer;
}
function clearBackdrop(clearAll) {
export function clearBackdrop(clearAll) {
clearRotation();
if (currentLoadingBackdrop) {
@ -109,7 +114,7 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', 'userSettings'
currentLoadingBackdrop = null;
}
var elem = getBackdropContainer();
const elem = getBackdropContainer();
elem.innerHTML = '';
if (clearAll) {
@ -119,7 +124,7 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', 'userSettings'
internalBackdrop(false);
}
var backgroundContainer;
let backgroundContainer;
function getBackgroundContainer() {
if (!backgroundContainer) {
backgroundContainer = document.querySelector('.backgroundContainer');
@ -135,31 +140,27 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', 'userSettings'
}
}
var hasInternalBackdrop;
let hasInternalBackdrop;
function internalBackdrop(enabled) {
hasInternalBackdrop = enabled;
setBackgroundContainerBackgroundEnabled();
}
var hasExternalBackdrop;
function externalBackdrop(enabled) {
let hasExternalBackdrop;
export function externalBackdrop(enabled) {
hasExternalBackdrop = enabled;
setBackgroundContainerBackgroundEnabled();
}
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
var currentLoadingBackdrop;
let currentLoadingBackdrop;
function setBackdropImage(url) {
if (currentLoadingBackdrop) {
currentLoadingBackdrop.destroy();
currentLoadingBackdrop = null;
}
var elem = getBackdropContainer();
var existingBackdropImage = elem.querySelector('.displayingBackdropImage');
const elem = getBackdropContainer();
const existingBackdropImage = elem.querySelector('.displayingBackdropImage');
if (existingBackdropImage && existingBackdropImage.getAttribute('data-url') === url) {
if (existingBackdropImage.getAttribute('data-url') === url) {
@ -168,7 +169,7 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', 'userSettings'
existingBackdropImage.classList.remove('displayingBackdropImage');
}
var instance = new Backdrop();
const instance = new Backdrop();
instance.load(url, elem, existingBackdropImage);
currentLoadingBackdrop = instance;
}
@ -176,9 +177,9 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', 'userSettings'
function getItemImageUrls(item, imageOptions) {
imageOptions = imageOptions || {};
var apiClient = connectionManager.getApiClient(item.ServerId);
const apiClient = connectionManager.getApiClient(item.ServerId);
if (item.BackdropImageTags && item.BackdropImageTags.length > 0) {
return item.BackdropImageTags.map(function (imgTag, index) {
return item.BackdropImageTags.map((imgTag, index) => {
return apiClient.getScaledImageUrl(item.BackdropItemId || item.Id, Object.assign(imageOptions, {
type: 'Backdrop',
tag: imgTag,
@ -189,7 +190,7 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', 'userSettings'
}
if (item.ParentBackdropItemId && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length) {
return item.ParentBackdropImageTags.map(function (imgTag, index) {
return item.ParentBackdropImageTags.map((imgTag, index) => {
return apiClient.getScaledImageUrl(item.ParentBackdropItemId, Object.assign(imageOptions, {
type: 'Backdrop',
tag: imgTag,
@ -203,13 +204,13 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', 'userSettings'
}
function getImageUrls(items, imageOptions) {
var list = [];
var onImg = function (img) {
const list = [];
const onImg = img => {
list.push(img);
};
for (var i = 0, length = items.length; i < length; i++) {
var itemImages = getItemImageUrls(items[i], imageOptions);
for (let i = 0, length = items.length; i < length; i++) {
const itemImages = getItemImageUrls(items[i], imageOptions);
itemImages.forEach(onImg);
}
@ -229,7 +230,7 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', 'userSettings'
// If you don't care about the order of the elements inside
// the array, you should sort both arrays here.
for (var i = 0; i < a.length; ++i) {
for (let i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) {
return false;
}
@ -242,12 +243,12 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', 'userSettings'
return userSettings.enableBackdrops();
}
var rotationInterval;
var currentRotatingImages = [];
var currentRotationIndex = -1;
function setBackdrops(items, imageOptions, enableImageRotation) {
let rotationInterval;
let currentRotatingImages = [];
let currentRotationIndex = -1;
export function setBackdrops(items, imageOptions, enableImageRotation) {
if (enabled()) {
var images = getImageUrls(items, imageOptions);
const images = getImageUrls(items, imageOptions);
if (images.length) {
startRotation(images, enableImageRotation);
@ -279,7 +280,7 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', 'userSettings'
return;
}
var newIndex = currentRotationIndex + 1;
let newIndex = currentRotationIndex + 1;
if (newIndex >= currentRotatingImages.length) {
newIndex = 0;
}
@ -289,7 +290,7 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', 'userSettings'
}
function clearRotation() {
var interval = rotationInterval;
const interval = rotationInterval;
if (interval) {
clearInterval(interval);
}
@ -299,7 +300,7 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', 'userSettings'
currentRotationIndex = -1;
}
function setBackdrop(url, imageOptions) {
export function setBackdrop(url, imageOptions) {
if (url && typeof url !== 'string') {
url = getImageUrls([url], imageOptions)[0];
}
@ -312,10 +313,11 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', 'userSettings'
}
}
return {
setBackdrops: setBackdrops,
setBackdrop: setBackdrop,
clear: clearBackdrop,
externalBackdrop: externalBackdrop
};
});
/* eslint-enable indent */
export default {
setBackdrops: setBackdrops,
setBackdrop: setBackdrop,
clearBackdrop: clearBackdrop,
externalBackdrop: externalBackdrop
};

View file

@ -192,9 +192,14 @@ button::-moz-focus-inner {
/* Needed in case this is a button */
display: block;
/* Needed in case this is a button */
margin: 0 !important;
border: 0 !important;
padding: 0 !important;
cursor: pointer;
color: inherit;
width: 100%;
font-family: inherit;
font-size: inherit;
/* Needed in safari */
height: 100%;
@ -203,19 +208,12 @@ button::-moz-focus-inner {
contain: strict;
}
.cardContent-button {
border: 0 !important;
padding: 0 !important;
cursor: pointer;
color: inherit;
width: 100%;
vertical-align: middle;
font-family: inherit;
font-size: inherit;
.cardContent:not(.defaultCardBackground) {
background-color: transparent;
}
.cardContent-button:not(.defaultCardBackground) {
background-color: transparent;
.cardBox:not(.visualCardBox) .cardPadder {
background-color: #242424;
}
.visualCardBox .cardContent {
@ -223,7 +221,8 @@ button::-moz-focus-inner {
border-bottom-right-radius: 0;
}
.cardContent-shadow {
.cardContent-shadow,
.cardBox:not(.visualCardBox) .cardPadder {
box-shadow: 0 0.0725em 0.29em 0 rgba(0, 0, 0, 0.37);
}

View file

@ -368,9 +368,7 @@ import 'programStyles';
let apiClient;
let lastServerId;
for (let i = 0; i < items.length; i++) {
let item = items[i];
for (const [i, item] of items.entries()) {
let serverId = item.ServerId || options.serverId;
if (serverId !== lastServerId) {
@ -541,7 +539,7 @@ import 'programStyles';
imgType = 'Backdrop';
imgTag = item.ParentBackdropImageTags[0];
itemId = item.ParentBackdropItemId;
} else if (item.ImageTags && item.ImageTags.Primary) {
} else if (item.ImageTags && item.ImageTags.Primary && (item.Type !== 'Episode' || item.ChildCount !== 0)) {
imgType = 'Primary';
imgTag = item.ImageTags.Primary;
height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null;
@ -556,7 +554,10 @@ import 'programStyles';
coverImage = (Math.abs(primaryImageAspectRatio - uiAspect) / uiAspect) <= 0.2;
}
}
} else if (item.SeriesPrimaryImageTag) {
imgType = 'Primary';
imgTag = item.SeriesPrimaryImageTag;
itemId = item.SeriesId;
} else if (item.PrimaryImageTag) {
imgType = 'Primary';
imgTag = item.PrimaryImageTag;
@ -577,10 +578,6 @@ import 'programStyles';
imgType = 'Primary';
imgTag = item.ParentPrimaryImageTag;
itemId = item.ParentPrimaryImageItemId;
} else if (item.SeriesPrimaryImageTag) {
imgType = 'Primary';
imgTag = item.SeriesPrimaryImageTag;
itemId = item.SeriesId;
} else if (item.AlbumId && item.AlbumPrimaryImageTag) {
imgType = 'Primary';
imgTag = item.AlbumPrimaryImageTag;
@ -1370,9 +1367,6 @@ import 'programStyles';
let cardScalableClose = '';
let cardContentClass = 'cardContent';
if (!options.cardLayout) {
cardContentClass += ' cardContent-shadow';
}
let blurhashAttrib = '';
if (blurhash && blurhash.length > 0) {
@ -1380,21 +1374,20 @@ import 'programStyles';
}
if (layoutManager.tv) {
// Don't use the IMG tag with safari because it puts a white border around it
cardImageContainerOpen = imgUrl ? ('<div class="' + cardImageContainerClass + ' ' + cardContentClass + ' lazy" data-src="' + imgUrl + '" ' + blurhashAttrib + '>') : ('<div class="' + cardImageContainerClass + ' ' + cardContentClass + '">');
cardImageContainerClose = '</div>';
} else {
// Don't use the IMG tag with safari because it puts a white border around it
cardImageContainerOpen = imgUrl ? ('<button data-action="' + action + '" class="cardContent-button ' + cardImageContainerClass + ' ' + cardContentClass + ' itemAction lazy" data-src="' + imgUrl + '" ' + blurhashAttrib + '>') : ('<button data-action="' + action + '" class="cardContent-button ' + cardImageContainerClass + ' ' + cardContentClass + ' itemAction">');
cardImageContainerOpen = imgUrl ? ('<button data-action="' + action + '" class="' + cardImageContainerClass + ' ' + cardContentClass + ' itemAction lazy" data-src="' + imgUrl + '" ' + blurhashAttrib + '>') : ('<button data-action="' + action + '" class="' + cardImageContainerClass + ' ' + cardContentClass + ' itemAction">');
cardImageContainerClose = '</button>';
}
let cardScalableClass = 'cardScalable';
cardImageContainerOpen = '<div class="' + cardBoxClass + '"><div class="' + cardScalableClass + '"><div class="cardPadder-' + shape + '"></div>' + cardImageContainerOpen;
cardImageContainerOpen = '<div class="' + cardBoxClass + '"><div class="' + cardScalableClass + '"><div class="cardPadder cardPadder-' + shape + '"></div>' + cardImageContainerOpen;
cardBoxClose = '</div>';
cardScalableClose = '</div>';

View file

@ -1,10 +1,21 @@
define(['dom', 'dialogHelper', 'loading', 'connectionManager', 'globalize', 'actionsheet', 'emby-input', 'paper-icon-button-light', 'emby-button', 'listViewStyle', 'material-icons', 'formDialogStyle'], function (dom, dialogHelper, loading, connectionManager, globalize, actionsheet) {
'use strict';
import dom from 'dom';
import dialogHelper from 'dialogHelper';
import loading from 'loading';
import connectionManager from 'connectionManager';
import globalize from 'globalize';
import actionsheet from 'actionsheet';
import 'emby-input';
import 'paper-icon-button-light';
import 'emby-button';
import 'listViewStyle';
import 'material-icons';
import 'formDialogStyle';
return function (options) {
export default class channelMapper {
constructor(options) {
function mapChannel(button, channelId, providerChannelId) {
loading.show();
var providerId = options.providerId;
const providerId = options.providerId;
connectionManager.getApiClient(options.serverId).ajax({
type: 'POST',
url: ApiClient.getUrl('LiveTv/ChannelMappings'),
@ -14,8 +25,8 @@ define(['dom', 'dialogHelper', 'loading', 'connectionManager', 'globalize', 'act
providerChannelId: providerChannelId
},
dataType: 'json'
}).then(function (mapping) {
var listItem = dom.parentWithClass(button, 'listItem');
}).then(mapping => {
const listItem = dom.parentWithClass(button, 'listItem');
button.setAttribute('data-providerid', mapping.ProviderChannelId);
listItem.querySelector('.secondary').innerHTML = getMappingSecondaryName(mapping, currentMappingOptions.ProviderName);
loading.hide();
@ -23,42 +34,42 @@ define(['dom', 'dialogHelper', 'loading', 'connectionManager', 'globalize', 'act
}
function onChannelsElementClick(e) {
var btnMap = dom.parentWithClass(e.target, 'btnMap');
const btnMap = dom.parentWithClass(e.target, 'btnMap');
if (btnMap) {
var channelId = btnMap.getAttribute('data-id');
var providerChannelId = btnMap.getAttribute('data-providerid');
var menuItems = currentMappingOptions.ProviderChannels.map(function (m) {
const channelId = btnMap.getAttribute('data-id');
const providerChannelId = btnMap.getAttribute('data-providerid');
const menuItems = currentMappingOptions.ProviderChannels.map(m => {
return {
name: m.Name,
id: m.Id,
selected: m.Id.toLowerCase() === providerChannelId.toLowerCase()
};
}).sort(function (a, b) {
}).sort((a, b) => {
return a.name.localeCompare(b.name);
});
actionsheet.show({
positionTo: btnMap,
items: menuItems
}).then(function (newChannelId) {
}).then(newChannelId => {
mapChannel(btnMap, channelId, newChannelId);
});
}
}
function getChannelMappingOptions(serverId, providerId) {
var apiClient = connectionManager.getApiClient(serverId);
const apiClient = connectionManager.getApiClient(serverId);
return apiClient.getJSON(apiClient.getUrl('LiveTv/ChannelMappingOptions', {
providerId: providerId
}));
}
function getMappingSecondaryName(mapping, providerName) {
return (mapping.ProviderChannelName || '') + ' - ' + providerName;
return `${mapping.ProviderChannelName || ''} - ${providerName}`;
}
function getTunerChannelHtml(channel, providerName) {
var html = '';
let html = '';
html += '<div class="listItem">';
html += '<span class="material-icons listItemIcon dvr"></span>';
html += '<div class="listItemBody two-line">';
@ -73,16 +84,16 @@ define(['dom', 'dialogHelper', 'loading', 'connectionManager', 'globalize', 'act
html += '</div>';
html += '</div>';
html += '<button class="btnMap autoSize" is="paper-icon-button-light" type="button" data-id="' + channel.Id + '" data-providerid="' + channel.ProviderChannelId + '"><span class="material-icons mode_edit"></span></button>';
html += `<button class="btnMap autoSize" is="paper-icon-button-light" type="button" data-id="${channel.Id}" data-providerid="${channel.ProviderChannelId}"><span class="material-icons mode_edit"></span></button>`;
return html += '</div>';
}
function getEditorHtml() {
var html = '';
let html = '';
html += '<div class="formDialogContent smoothScrollY">';
html += '<div class="dialogContentInner dialog-content-centered">';
html += '<form style="margin:auto;">';
html += '<h1>' + globalize.translate('HeaderChannels') + '</h1>';
html += `<h1>${globalize.translate('HeaderChannels')}</h1>`;
html += '<div class="channels paperList">';
html += '</div>';
html += '</form>';
@ -91,30 +102,29 @@ define(['dom', 'dialogHelper', 'loading', 'connectionManager', 'globalize', 'act
}
function initEditor(dlg, options) {
getChannelMappingOptions(options.serverId, options.providerId).then(function (result) {
getChannelMappingOptions(options.serverId, options.providerId).then(result => {
currentMappingOptions = result;
var channelsElement = dlg.querySelector('.channels');
channelsElement.innerHTML = result.TunerChannels.map(function (channel) {
const channelsElement = dlg.querySelector('.channels');
channelsElement.innerHTML = result.TunerChannels.map(channel => {
return getTunerChannelHtml(channel, result.ProviderName);
}).join('');
channelsElement.addEventListener('click', onChannelsElementClick);
});
}
var currentMappingOptions;
var self = this;
let currentMappingOptions;
self.show = function () {
var dialogOptions = {
this.show = () => {
const dialogOptions = {
removeOnClose: true
};
dialogOptions.size = 'small';
var dlg = dialogHelper.createDialog(dialogOptions);
const dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog');
dlg.classList.add('ui-body-a');
dlg.classList.add('background-theme-a');
var html = '';
var title = globalize.translate('MapChannels');
let html = '';
const title = globalize.translate('MapChannels');
html += '<div class="formDialogHeader">';
html += '<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><span class="material-icons arrow_back"></span></button>';
html += '<h3 class="formDialogHeaderTitle">';
@ -124,13 +134,13 @@ define(['dom', 'dialogHelper', 'loading', 'connectionManager', 'globalize', 'act
html += getEditorHtml();
dlg.innerHTML = html;
initEditor(dlg, options);
dlg.querySelector('.btnCancel').addEventListener('click', function () {
dlg.querySelector('.btnCancel').addEventListener('click', () => {
dialogHelper.close(dlg);
});
return new Promise(function (resolve, reject) {
return new Promise(resolve => {
dlg.addEventListener('close', resolve);
dialogHelper.open(dlg);
});
};
};
});
}
}

View file

@ -1,16 +1,32 @@
define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectionManager', 'appRouter', 'globalize', 'emby-checkbox', 'emby-input', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button', 'flexStyles'], function (dom, dialogHelper, loading, appHost, layoutManager, connectionManager, appRouter, globalize) {
'use strict';
import dom from 'dom';
import dialogHelper from 'dialogHelper';
import loading from 'loading';
import appHost from 'apphost';
import layoutManager from 'layoutManager';
import connectionManager from 'connectionManager';
import appRouter from 'appRouter';
import globalize from 'globalize';
import 'emby-checkbox';
import 'emby-input';
import 'paper-icon-button-light';
import 'emby-select';
import 'material-icons';
import 'css!./../formdialog';
import 'emby-button';
import 'flexStyles';
var currentServerId;
/* eslint-disable indent */
let currentServerId;
function onSubmit(e) {
loading.show();
var panel = dom.parentWithClass(this, 'dialog');
const panel = dom.parentWithClass(this, 'dialog');
var collectionId = panel.querySelector('#selectCollectionToAddTo').value;
const collectionId = panel.querySelector('#selectCollectionToAddTo').value;
var apiClient = connectionManager.getApiClient(currentServerId);
const apiClient = connectionManager.getApiClient(currentServerId);
if (collectionId) {
addToCollection(apiClient, panel, collectionId);
@ -24,7 +40,7 @@ define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectio
function createCollection(apiClient, dlg) {
var url = apiClient.getUrl('Collections', {
const url = apiClient.getUrl('Collections', {
Name: dlg.querySelector('#txtNewCollectionName').value,
IsLocked: !dlg.querySelector('#chkEnableInternetMetadata').checked,
@ -36,11 +52,11 @@ define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectio
url: url,
dataType: 'json'
}).then(function (result) {
}).then(result => {
loading.hide();
var id = result.Id;
const id = result.Id;
dlg.submitted = true;
dialogHelper.close(dlg);
@ -56,7 +72,7 @@ define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectio
function addToCollection(apiClient, dlg, id) {
var url = apiClient.getUrl('Collections/' + id + '/Items', {
const url = apiClient.getUrl(`Collections/${id}/Items`, {
Ids: dlg.querySelector('.fldSelectedItemIds').value || ''
});
@ -65,14 +81,14 @@ define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectio
type: 'POST',
url: url
}).then(function () {
}).then(() => {
loading.hide();
dlg.submitted = true;
dialogHelper.close(dlg);
require(['toast'], function (toast) {
import('toast').then(({default: toast}) => {
toast(globalize.translate('MessageItemsAdded'));
});
});
@ -86,11 +102,11 @@ define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectio
loading.show();
var select = panel.querySelector('#selectCollectionToAddTo');
const select = panel.querySelector('#selectCollectionToAddTo');
panel.querySelector('.newCollectionInfo').classList.add('hide');
var options = {
const options = {
Recursive: true,
IncludeItemTypes: 'BoxSet',
@ -98,16 +114,16 @@ define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectio
EnableTotalRecordCount: false
};
var apiClient = connectionManager.getApiClient(currentServerId);
apiClient.getItems(apiClient.getCurrentUserId(), options).then(function (result) {
const apiClient = connectionManager.getApiClient(currentServerId);
apiClient.getItems(apiClient.getCurrentUserId(), options).then(result => {
var html = '';
let html = '';
html += '<option value="">' + globalize.translate('OptionNew') + '</option>';
html += `<option value="">${globalize.translate('OptionNew')}</option>`;
html += result.Items.map(function (i) {
html += result.Items.map(i => {
return '<option value="' + i.Id + '">' + i.Name + '</option>';
return `<option value="${i.Id}">${i.Name}</option>`;
});
select.innerHTML = html;
@ -120,7 +136,7 @@ define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectio
function getEditorHtml() {
var html = '';
let html = '';
html += '<div class="formDialogContent smoothScrollY" style="padding-top:2em;">';
html += '<div class="dialogContentInner dialog-content-centered">';
@ -134,27 +150,27 @@ define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectio
html += '<br/>';
html += '<br/>';
html += '<div class="selectContainer">';
html += '<select is="emby-select" label="' + globalize.translate('LabelCollection') + '" id="selectCollectionToAddTo" autofocus></select>';
html += `<select is="emby-select" label="${globalize.translate('LabelCollection')}" id="selectCollectionToAddTo" autofocus></select>`;
html += '</div>';
html += '</div>';
html += '<div class="newCollectionInfo">';
html += '<div class="inputContainer">';
html += '<input is="emby-input" type="text" id="txtNewCollectionName" required="required" label="' + globalize.translate('LabelName') + '" />';
html += '<div class="fieldDescription">' + globalize.translate('NewCollectionNameExample') + '</div>';
html += `<input is="emby-input" type="text" id="txtNewCollectionName" required="required" label="${globalize.translate('LabelName')}" />`;
html += `<div class="fieldDescription">${globalize.translate('NewCollectionNameExample')}</div>`;
html += '</div>';
html += '<label class="checkboxContainer">';
html += '<input is="emby-checkbox" type="checkbox" id="chkEnableInternetMetadata" />';
html += '<span>' + globalize.translate('SearchForCollectionInternetMetadata') + '</span>';
html += `<span>${globalize.translate('SearchForCollectionInternetMetadata')}</span>`;
html += '</label>';
// newCollectionInfo
html += '</div>';
html += '<div class="formDialogFooter">';
html += '<button is="emby-button" type="submit" class="raised btnSubmit block formDialogFooterItem button-submit">' + globalize.translate('ButtonOk') + '</button>';
html += `<button is="emby-button" type="submit" class="raised btnSubmit block formDialogFooterItem button-submit">${globalize.translate('ButtonOk')}</button>`;
html += '</div>';
html += '<input type="hidden" class="fldSelectedItemIds" />';
@ -188,7 +204,7 @@ define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectio
} else {
content.querySelector('.fldSelectCollection').classList.add('hide');
var selectCollectionToAddTo = content.querySelector('#selectCollectionToAddTo');
const selectCollectionToAddTo = content.querySelector('#selectCollectionToAddTo');
selectCollectionToAddTo.innerHTML = '';
selectCollectionToAddTo.value = '';
triggerChange(selectCollectionToAddTo);
@ -196,79 +212,77 @@ define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectio
}
function centerFocus(elem, horiz, on) {
require(['scrollHelper'], function (scrollHelper) {
var fn = on ? 'on' : 'off';
import('scrollHelper').then(scrollHelper => {
const fn = on ? 'on' : 'off';
scrollHelper.centerFocus[fn](elem, horiz);
});
}
function CollectionEditor() {
export class showEditor {
constructor(options) {
}
const items = options.items || {};
currentServerId = options.serverId;
CollectionEditor.prototype.show = function (options) {
var items = options.items || {};
currentServerId = options.serverId;
var dialogOptions = {
removeOnClose: true,
scrollY: false
};
if (layoutManager.tv) {
dialogOptions.size = 'fullscreen';
} else {
dialogOptions.size = 'small';
}
var dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog');
var html = '';
var title = items.length ? globalize.translate('HeaderAddToCollection') : globalize.translate('NewCollection');
html += '<div class="formDialogHeader">';
html += '<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><span class="material-icons arrow_back"></span></button>';
html += '<h3 class="formDialogHeaderTitle">';
html += title;
html += '</h3>';
if (appHost.supports('externallinks')) {
html += '<a is="emby-linkbutton" class="button-link btnHelp flex align-items-center" href="https://web.archive.org/web/20181216120305/https://github.com/MediaBrowser/Wiki/wiki/Collections" target="_blank" style="margin-left:auto;margin-right:.5em;padding:.25em;" title="' + globalize.translate('Help') + '"><span class="material-icons info"></span><span style="margin-left:.25em;">' + globalize.translate('Help') + '</span></a>';
}
html += '</div>';
html += getEditorHtml();
dlg.innerHTML = html;
initEditor(dlg, items);
dlg.querySelector('.btnCancel').addEventListener('click', function () {
dialogHelper.close(dlg);
});
if (layoutManager.tv) {
centerFocus(dlg.querySelector('.formDialogContent'), false, true);
}
return dialogHelper.open(dlg).then(function () {
const dialogOptions = {
removeOnClose: true,
scrollY: false
};
if (layoutManager.tv) {
centerFocus(dlg.querySelector('.formDialogContent'), false, false);
dialogOptions.size = 'fullscreen';
} else {
dialogOptions.size = 'small';
}
if (dlg.submitted) {
return Promise.resolve();
const dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog');
let html = '';
const title = items.length ? globalize.translate('HeaderAddToCollection') : globalize.translate('NewCollection');
html += '<div class="formDialogHeader">';
html += '<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><span class="material-icons arrow_back"></span></button>';
html += '<h3 class="formDialogHeaderTitle">';
html += title;
html += '</h3>';
if (appHost.supports('externallinks')) {
html += `<a is="emby-linkbutton" class="button-link btnHelp flex align-items-center" href="https://web.archive.org/web/20181216120305/https://github.com/MediaBrowser/Wiki/wiki/Collections" target="_blank" style="margin-left:auto;margin-right:.5em;padding:.25em;" title="${globalize.translate('Help')}"><span class="material-icons info"></span><span style="margin-left:.25em;">${globalize.translate('Help')}</span></a>`;
}
return Promise.reject();
});
};
html += '</div>';
return CollectionEditor;
});
html += getEditorHtml();
dlg.innerHTML = html;
initEditor(dlg, items);
dlg.querySelector('.btnCancel').addEventListener('click', () => {
dialogHelper.close(dlg);
});
if (layoutManager.tv) {
centerFocus(dlg.querySelector('.formDialogContent'), false, true);
}
return dialogHelper.open(dlg).then(() => {
if (layoutManager.tv) {
centerFocus(dlg.querySelector('.formDialogContent'), false, false);
}
if (dlg.submitted) {
return Promise.resolve();
}
return Promise.reject();
});
}
}
/* eslint-enable indent */
export default showEditor;

View file

@ -1,20 +1,31 @@
define(['dialogHelper', 'dom', 'layoutManager', 'scrollHelper', 'globalize', 'require', 'material-icons', 'emby-button', 'paper-icon-button-light', 'emby-input', 'formDialogStyle', 'flexStyles'], function (dialogHelper, dom, layoutManager, scrollHelper, globalize, require) {
'use strict';
import dialogHelper from 'dialogHelper';
import dom from 'dom';
import layoutManager from 'layoutManager';
import scrollHelper from 'scrollHelper';
import globalize from 'globalize';
import 'material-icons';
import 'emby-button';
import 'paper-icon-button-light';
import 'emby-input';
import 'formDialogStyle';
import 'flexStyles';
/* eslint-disable indent */
function showDialog(options, template) {
var dialogOptions = {
const dialogOptions = {
removeOnClose: true,
scrollY: false
};
var enableTvLayout = layoutManager.tv;
const enableTvLayout = layoutManager.tv;
if (enableTvLayout) {
dialogOptions.size = 'fullscreen';
}
var dlg = dialogHelper.createDialog(dialogOptions);
const dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog');
@ -22,7 +33,7 @@ define(['dialogHelper', 'dom', 'layoutManager', 'scrollHelper', 'globalize', 're
dlg.classList.add('align-items-center');
dlg.classList.add('justify-content-center');
var formDialogContent = dlg.querySelector('.formDialogContent');
const formDialogContent = dlg.querySelector('.formDialogContent');
formDialogContent.classList.add('no-grow');
if (enableTvLayout) {
@ -30,7 +41,7 @@ define(['dialogHelper', 'dom', 'layoutManager', 'scrollHelper', 'globalize', 're
formDialogContent.style['max-height'] = '60%';
scrollHelper.centerFocus.on(formDialogContent, false);
} else {
formDialogContent.style.maxWidth = (Math.min((options.buttons.length * 150) + 200, dom.getWindowSize().innerWidth - 50)) + 'px';
formDialogContent.style.maxWidth = `${Math.min((options.buttons.length * 150) + 200, dom.getWindowSize().innerWidth - 50)}px`;
dlg.classList.add('dialog-fullscreen-lowres');
}
@ -44,27 +55,27 @@ define(['dialogHelper', 'dom', 'layoutManager', 'scrollHelper', 'globalize', 're
dlg.querySelector('.formDialogHeaderTitle').classList.add('hide');
}
var displayText = options.html || options.text || '';
const displayText = options.html || options.text || '';
dlg.querySelector('.text').innerHTML = displayText;
if (!displayText) {
dlg.querySelector('.dialogContentInner').classList.add('hide');
}
var i;
var length;
var html = '';
var hasDescriptions = false;
let i;
let length;
let html = '';
let hasDescriptions = false;
for (i = 0, length = options.buttons.length; i < length; i++) {
var item = options.buttons[i];
var autoFocus = i === 0 ? ' autofocus' : '';
const item = options.buttons[i];
const autoFocus = i === 0 ? ' autofocus' : '';
var buttonClass = 'btnOption raised formDialogFooterItem formDialogFooterItem-autosize';
let buttonClass = 'btnOption raised formDialogFooterItem formDialogFooterItem-autosize';
if (item.type) {
buttonClass += ' button-' + item.type;
buttonClass += ` button-${item.type}`;
}
if (item.description) {
@ -75,10 +86,10 @@ define(['dialogHelper', 'dom', 'layoutManager', 'scrollHelper', 'globalize', 're
buttonClass += ' formDialogFooterItem-vertical formDialogFooterItem-nomarginbottom';
}
html += '<button is="emby-button" type="button" class="' + buttonClass + '" data-id="' + item.id + '"' + autoFocus + '>' + item.name + '</button>';
html += `<button is="emby-button" type="button" class="${buttonClass}" data-id="${item.id}"${autoFocus}>${item.name}</button>`;
if (item.description) {
html += '<div class="formDialogFooterItem formDialogFooterItem-autosize fieldDescription" style="margin-top:.25em!important;margin-bottom:1.25em!important;">' + item.description + '</div>';
html += `<div class="formDialogFooterItem formDialogFooterItem-autosize fieldDescription" style="margin-top:.25em!important;margin-bottom:1.25em!important;">${item.description}</div>`;
}
}
@ -88,18 +99,18 @@ define(['dialogHelper', 'dom', 'layoutManager', 'scrollHelper', 'globalize', 're
dlg.querySelector('.formDialogFooter').classList.add('formDialogFooter-vertical');
}
var dialogResult;
let dialogResult;
function onButtonClick() {
dialogResult = this.getAttribute('data-id');
dialogHelper.close(dlg);
}
var buttons = dlg.querySelectorAll('.btnOption');
const buttons = dlg.querySelectorAll('.btnOption');
for (i = 0, length = buttons.length; i < length; i++) {
buttons[i].addEventListener('click', onButtonClick);
}
return dialogHelper.open(dlg).then(function () {
return dialogHelper.open(dlg).then(() => {
if (enableTvLayout) {
scrollHelper.centerFocus.off(dlg.querySelector('.formDialogContent'), false);
@ -113,9 +124,9 @@ define(['dialogHelper', 'dom', 'layoutManager', 'scrollHelper', 'globalize', 're
});
}
return function (text, title) {
export async function show(text, title) {
var options;
let options;
if (typeof text === 'string') {
options = {
title: title,
@ -125,10 +136,13 @@ define(['dialogHelper', 'dom', 'layoutManager', 'scrollHelper', 'globalize', 're
options = text;
}
return new Promise(function (resolve, reject) {
require(['text!./dialog.template.html'], function (template) {
showDialog(options, template).then(resolve, reject);
});
const { default: template } = await import('text!./dialog.template.html');
return new Promise((resolve, reject) => {
showDialog(options, template).then(resolve, reject);
});
};
});
}
/* eslint-enable indent */
export default {
show: show
};

View file

@ -4,12 +4,8 @@
<div class="formDialogContent smoothScrollY">
<div class="dialogContentInner dialog-content-centered" style="padding-top:1em;padding-bottom: 1em; text-align: center;">
<div class="text">
</div>
<div class="text"></div>
</div>
</div>
<div class="formDialogFooter formDialogFooter-clear formDialogFooter-flex" style="padding-bottom: 1.5em;">
</div>
<div class="formDialogFooter formDialogFooter-clear formDialogFooter-flex" style="margin:1em"></div>

View file

@ -1,7 +1,15 @@
define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager', 'dom', 'css!./dialoghelper.css', 'scrollStyles'], function (appRouter, focusManager, browser, layoutManager, inputManager, dom) {
'use strict';
import appRouter from 'appRouter';
import focusManager from 'focusManager';
import browser from 'browser';
import layoutManager from 'layoutManager';
import inputManager from 'inputManager';
import dom from 'dom';
import 'css!./dialoghelper.css';
import 'scrollStyles';
var globalOnOpenCallback;
/* eslint-disable indent */
let globalOnOpenCallback;
function enableAnimation() {
@ -25,7 +33,7 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
}
function tryRemoveElement(elem) {
var parentNode = elem.parentNode;
const parentNode = elem.parentNode;
if (parentNode) {
// Seeing crashes in edge webview
@ -39,14 +47,14 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
function DialogHashHandler(dlg, hash, resolve) {
var self = this;
const self = this;
self.originalUrl = window.location.href;
var activeElement = document.activeElement;
var removeScrollLockOnClose = false;
const activeElement = document.activeElement;
let removeScrollLockOnClose = false;
function onHashChange(e) {
var isBack = self.originalUrl === window.location.href;
const isBack = self.originalUrl === window.location.href;
if (isBack || !isOpened(dlg)) {
window.removeEventListener('popstate', onHashChange);
@ -84,7 +92,7 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
}
if (!self.closedByBack && isHistoryEnabled(dlg)) {
var state = history.state || {};
const state = history.state || {};
if (state.dialogId === hash) {
history.back();
}
@ -97,7 +105,7 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
if (dlg.getAttribute('data-removeonclose') !== 'false') {
removeCenterFocus(dlg);
var dialogContainer = dlg.dialogContainer;
const dialogContainer = dlg.dialogContainer;
if (dialogContainer) {
tryRemoveElement(dialogContainer);
dlg.dialogContainer = null;
@ -108,7 +116,7 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
//resolve();
// if we just called history.back(), then use a timeout to allow the history events to fire first
setTimeout(function () {
setTimeout(() => {
resolve({
element: dlg,
closedByBack: self.closedByBack
@ -118,7 +126,7 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
dlg.addEventListener('close', onDialogClosed);
var center = !dlg.classList.contains('dialog-fixedSize');
const center = !dlg.classList.contains('dialog-fixedSize');
if (center) {
dlg.classList.add('centeredDialog');
}
@ -141,7 +149,7 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
animateDialogOpen(dlg);
if (isHistoryEnabled(dlg)) {
appRouter.pushState({ dialogId: hash }, 'Dialog', '#' + hash);
appRouter.pushState({ dialogId: hash }, 'Dialog', `#${hash}`);
window.addEventListener('popstate', onHashChange);
} else {
@ -151,10 +159,10 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
function addBackdropOverlay(dlg) {
var backdrop = document.createElement('div');
const backdrop = document.createElement('div');
backdrop.classList.add('dialogBackdrop');
var backdropParent = dlg.dialogContainer || dlg;
const backdropParent = dlg.dialogContainer || dlg;
backdropParent.parentNode.insertBefore(backdrop, backdropParent);
dlg.backdrop = backdrop;
@ -162,7 +170,7 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
void backdrop.offsetWidth;
backdrop.classList.add('dialogBackdropOpened');
dom.addEventListener((dlg.dialogContainer || backdrop), 'click', function (e) {
dom.addEventListener((dlg.dialogContainer || backdrop), 'click', e => {
if (e.target === dlg.dialogContainer) {
close(dlg);
}
@ -170,7 +178,7 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
passive: true
});
dom.addEventListener((dlg.dialogContainer || backdrop), 'contextmenu', function (e) {
dom.addEventListener((dlg.dialogContainer || backdrop), 'contextmenu', e => {
if (e.target === dlg.dialogContainer) {
// Close the application dialog menu
close(dlg);
@ -184,26 +192,26 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
return dlg.getAttribute('data-history') === 'true';
}
function open(dlg) {
export function open(dlg) {
if (globalOnOpenCallback) {
globalOnOpenCallback(dlg);
}
var parent = dlg.parentNode;
const parent = dlg.parentNode;
if (parent) {
parent.removeChild(dlg);
}
var dialogContainer = document.createElement('div');
const dialogContainer = document.createElement('div');
dialogContainer.classList.add('dialogContainer');
dialogContainer.appendChild(dlg);
dlg.dialogContainer = dialogContainer;
document.body.appendChild(dialogContainer);
return new Promise(function (resolve, reject) {
return new Promise((resolve, reject) => {
new DialogHashHandler(dlg, 'dlg' + new Date().getTime(), resolve);
new DialogHashHandler(dlg, `dlg${new Date().getTime()}`, resolve);
});
}
@ -213,7 +221,7 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
return !dlg.classList.contains('hide');
}
function close(dlg) {
export function close(dlg) {
if (isOpened(dlg)) {
if (isHistoryEnabled(dlg)) {
@ -233,7 +241,7 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
cancelable: false
}));
var onAnimationFinish = function () {
const onAnimationFinish = () => {
focusManager.popScope(dlg);
dlg.classList.add('hide');
@ -249,7 +257,7 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
function animateDialogOpen(dlg) {
var onAnimationFinish = function () {
const onAnimationFinish = () => {
focusManager.pushScope(dlg);
if (dlg.getAttribute('data-autofocus') === 'true') {
@ -264,7 +272,7 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
if (enableAnimation()) {
var onFinish = function () {
const onFinish = () => {
dom.removeEventListener(dlg, dom.whichAnimationEvent(), onFinish, {
once: true
});
@ -283,24 +291,24 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
if (enableAnimation()) {
var animated = true;
let animated = true;
switch (dlg.animationConfig.exit.name) {
case 'fadeout':
dlg.style.animation = 'fadeout ' + dlg.animationConfig.exit.timing.duration + 'ms ease-out normal both';
dlg.style.animation = `fadeout ${dlg.animationConfig.exit.timing.duration}ms ease-out normal both`;
break;
case 'scaledown':
dlg.style.animation = 'scaledown ' + dlg.animationConfig.exit.timing.duration + 'ms ease-out normal both';
dlg.style.animation = `scaledown ${dlg.animationConfig.exit.timing.duration}ms ease-out normal both`;
break;
case 'slidedown':
dlg.style.animation = 'slidedown ' + dlg.animationConfig.exit.timing.duration + 'ms ease-out normal both';
dlg.style.animation = `slidedown ${dlg.animationConfig.exit.timing.duration}ms ease-out normal both`;
break;
default:
animated = false;
break;
}
var onFinish = function () {
const onFinish = () => {
dom.removeEventListener(dlg, dom.whichAnimationEvent(), onFinish, {
once: true
});
@ -318,7 +326,7 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
onAnimationFinish();
}
var supportsOverscrollBehavior = 'overscroll-behavior-y' in document.body.style;
const supportsOverscrollBehavior = 'overscroll-behavior-y' in document.body.style;
function shouldLockDocumentScroll(options) {
@ -343,7 +351,7 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
function removeBackdrop(dlg) {
var backdrop = dlg.backdrop;
const backdrop = dlg.backdrop;
if (!backdrop) {
return;
@ -351,7 +359,7 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
dlg.backdrop = null;
var onAnimationFinish = function () {
const onAnimationFinish = () => {
tryRemoveElement(backdrop);
};
@ -368,20 +376,20 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
}
function centerFocus(elem, horiz, on) {
require(['scrollHelper'], function (scrollHelper) {
var fn = on ? 'on' : 'off';
import('scrollHelper').then(scrollHelper => {
const fn = on ? 'on' : 'off';
scrollHelper.centerFocus[fn](elem, horiz);
});
}
function createDialog(options) {
export function createDialog(options) {
options = options || {};
// If there's no native dialog support, use a plain div
// Also not working well in samsung tizen browser, content inside not clickable
// Just go ahead and always use a plain div because we're seeing issues overlaying absoltutely positioned content over a modal dialog
var dlg = document.createElement('div');
const dlg = document.createElement('div');
dlg.classList.add('focuscontainer');
dlg.classList.add('hide');
@ -406,17 +414,17 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
dlg.setAttribute('data-autofocus', 'true');
}
var defaultEntryAnimation;
var defaultExitAnimation;
let defaultEntryAnimation;
let defaultExitAnimation;
defaultEntryAnimation = 'scaleup';
defaultExitAnimation = 'scaledown';
var entryAnimation = options.entryAnimation || defaultEntryAnimation;
var exitAnimation = options.exitAnimation || defaultExitAnimation;
const entryAnimation = options.entryAnimation || defaultEntryAnimation;
const exitAnimation = options.exitAnimation || defaultExitAnimation;
// If it's not fullscreen then lower the default animation speed to make it open really fast
var entryAnimationDuration = options.entryAnimationDuration || (options.size !== 'fullscreen' ? 180 : 280);
var exitAnimationDuration = options.exitAnimationDuration || (options.size !== 'fullscreen' ? 120 : 220);
const entryAnimationDuration = options.entryAnimationDuration || (options.size !== 'fullscreen' ? 180 : 280);
const exitAnimationDuration = options.exitAnimationDuration || (options.size !== 'fullscreen' ? 120 : 220);
dlg.animationConfig = {
// scale up
@ -461,7 +469,7 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
if (options.size) {
dlg.classList.add('dialog-fixedSize');
dlg.classList.add('dialog-' + options.size);
dlg.classList.add(`dialog-${options.size}`);
}
if (enableAnimation()) {
@ -469,16 +477,16 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
switch (dlg.animationConfig.entry.name) {
case 'fadein':
dlg.style.animation = 'fadein ' + entryAnimationDuration + 'ms ease-out normal';
dlg.style.animation = `fadein ${entryAnimationDuration}ms ease-out normal`;
break;
case 'scaleup':
dlg.style.animation = 'scaleup ' + entryAnimationDuration + 'ms ease-out normal both';
dlg.style.animation = `scaleup ${entryAnimationDuration}ms ease-out normal both`;
break;
case 'slideup':
dlg.style.animation = 'slideup ' + entryAnimationDuration + 'ms ease-out normal';
dlg.style.animation = `slideup ${entryAnimationDuration}ms ease-out normal`;
break;
case 'slidedown':
dlg.style.animation = 'slidedown ' + entryAnimationDuration + 'ms ease-out normal';
dlg.style.animation = `slidedown ${entryAnimationDuration}ms ease-out normal`;
break;
default:
break;
@ -488,12 +496,15 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
return dlg;
}
return {
open: open,
close: close,
createDialog: createDialog,
setOnOpen: function (val) {
globalOnOpenCallback = val;
}
};
});
export function setOnOpen(val) {
globalOnOpenCallback = val;
}
/* eslint-enable indent */
export default {
open: open,
close: close,
createDialog: createDialog,
setOnOpen: setOnOpen
};

View file

@ -1,9 +1,19 @@
define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-input', 'paper-icon-button-light', 'css!./directorybrowser', 'formDialogStyle', 'emby-button'], function(loading, dialogHelper, dom, globalize) {
'use strict';
import loading from 'loading';
import dialogHelper from 'dialogHelper';
import dom from 'dom';
import globalize from 'globalize';
import 'listViewStyle';
import 'emby-input';
import 'paper-icon-button-light';
import 'css!./directorybrowser';
import 'formDialogStyle';
import 'emby-button';
/* eslint-disable indent */
function getSystemInfo() {
return systemInfo ? Promise.resolve(systemInfo) : ApiClient.getPublicSystemInfo().then(
function(info) {
info => {
systemInfo = info;
return info;
}
@ -21,9 +31,9 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
loading.show();
var promises = [];
const promises = [];
if ('Network' === path) {
if (path === 'Network') {
promises.push(ApiClient.getNetworkDevices());
} else {
if (path) {
@ -35,10 +45,10 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
}
Promise.all(promises).then(
function(responses) {
var folders = responses[0];
var parentPath = responses[1] || '';
var html = '';
responses => {
const folders = responses[0];
const parentPath = responses[1] || '';
let html = '';
page.querySelector('.results').scrollTop = 0;
page.querySelector('#txtDirectoryPickerPath').value = path || '';
@ -46,9 +56,9 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
if (path) {
html += getItem('lnkPath lnkDirectory', '', parentPath, '...');
}
for (var i = 0, length = folders.length; i < length; i++) {
var folder = folders[i];
var cssClass = 'File' === folder.Type ? 'lnkPath lnkFile' : 'lnkPath lnkDirectory';
for (let i = 0, length = folders.length; i < length; i++) {
const folder = folders[i];
const cssClass = folder.Type === 'File' ? 'lnkPath lnkFile' : 'lnkPath lnkDirectory';
html += getItem(cssClass, folder.Type, folder.Path, folder.Name);
}
@ -58,7 +68,7 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
page.querySelector('.results').innerHTML = html;
loading.hide();
}, function() {
}, () => {
if (updatePathOnError) {
page.querySelector('#txtDirectoryPickerPath').value = '';
page.querySelector('.results').innerHTML = '';
@ -69,8 +79,8 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
}
function getItem(cssClass, type, path, name) {
var html = '';
html += '<div class="listItem listItem-border ' + cssClass + '" data-type="' + type + '" data-path="' + path + '">';
let html = '';
html += `<div class="listItem listItem-border ${cssClass}" data-type="${type}" data-path="${path}">`;
html += '<div class="listItemBody" style="padding-left:0;padding-top:.5em;padding-bottom:.5em;">';
html += '<div class="listItemBodyText">';
html += name;
@ -82,19 +92,19 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
}
function getEditorHtml(options, systemInfo) {
var html = '';
let html = '';
html += '<div class="formDialogContent scrollY">';
html += '<div class="dialogContentInner dialog-content-centered" style="padding-top:2em;">';
if (!options.pathReadOnly) {
var instruction = options.instruction ? options.instruction + '<br/><br/>' : '';
const instruction = options.instruction ? `${options.instruction}<br/><br/>` : '';
html += '<div class="infoBanner" style="margin-bottom:1.5em;">';
html += instruction;
if ('bsd' === systemInfo.OperatingSystem.toLowerCase()) {
if (systemInfo.OperatingSystem.toLowerCase() === 'bsd') {
html += '<br/>';
html += '<br/>';
html += globalize.translate('MessageDirectoryPickerBSDInstruction');
html += '<br/>';
} else if ('linux' === systemInfo.OperatingSystem.toLowerCase()) {
} else if (systemInfo.OperatingSystem.toLowerCase() === 'linux') {
html += '<br/>';
html += '<br/>';
html += globalize.translate('MessageDirectoryPickerLinuxInstruction');
@ -105,17 +115,17 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
html += '<form style="margin:auto;">';
html += '<div class="inputContainer" style="display: flex; align-items: center;">';
html += '<div style="flex-grow:1;">';
var labelKey;
let labelKey;
if (options.includeFiles !== true) {
labelKey = 'LabelFolder';
} else {
labelKey = 'LabelPath';
}
var readOnlyAttribute = options.pathReadOnly ? ' readonly' : '';
html += '<input is="emby-input" id="txtDirectoryPickerPath" type="text" required="required" ' + readOnlyAttribute + ' label="' + globalize.translate(labelKey) + '"/>';
const readOnlyAttribute = options.pathReadOnly ? ' readonly' : '';
html += `<input is="emby-input" id="txtDirectoryPickerPath" type="text" required="required" ${readOnlyAttribute} label="${globalize.translate(labelKey)}"/>`;
html += '</div>';
if (!readOnlyAttribute) {
html += '<button type="button" is="paper-icon-button-light" class="btnRefreshDirectories emby-input-iconbutton" title="' + globalize.translate('ButtonRefresh') + '"><span class="material-icons search"></span></button>';
html += `<button type="button" is="paper-icon-button-light" class="btnRefreshDirectories emby-input-iconbutton" title="${globalize.translate('ButtonRefresh')}"><span class="material-icons search"></span></button>`;
}
html += '</div>';
if (!readOnlyAttribute) {
@ -123,14 +133,14 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
}
if (options.enableNetworkSharePath) {
html += '<div class="inputContainer" style="margin-top:2em;">';
html += '<input is="emby-input" id="txtNetworkPath" type="text" label="' + globalize.translate('LabelOptionalNetworkPath') + '"/>';
html += `<input is="emby-input" id="txtNetworkPath" type="text" label="${globalize.translate('LabelOptionalNetworkPath')}"/>`;
html += '<div class="fieldDescription">';
html += globalize.translate('LabelOptionalNetworkPathHelp', '<b>\\\\server</b>', '<b>\\\\192.168.1.101</b>');
html += '</div>';
html += '</div>';
}
html += '<div class="formDialogFooter">';
html += '<button is="emby-button" type="submit" class="raised button-submit block formDialogFooterItem">' + globalize.translate('ButtonOk') + '</button>';
html += `<button is="emby-button" type="submit" class="raised button-submit block formDialogFooterItem">${globalize.translate('ButtonOk')}</button>`;
html += '</div>';
html += '</form>';
html += '</div>';
@ -147,7 +157,7 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
}
function alertTextWithOptions(options) {
require(['alert'], function(alert) {
require(['alert'], alert => {
alert(options);
});
}
@ -160,7 +170,7 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
ValidateWriteable: validateWriteable,
Path: path
}
}).catch(function(response) {
}).catch(response => {
if (response) {
if (response.status === 404) {
alertText(globalize.translate('PathNotFound'));
@ -180,10 +190,10 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
}
function initEditor(content, options, fileOptions) {
content.addEventListener('click', function(e) {
var lnkPath = dom.parentWithClass(e.target, 'lnkPath');
content.addEventListener('click', e => {
const lnkPath = dom.parentWithClass(e.target, 'lnkPath');
if (lnkPath) {
var path = lnkPath.getAttribute('data-path');
const path = lnkPath.getAttribute('data-path');
if (lnkPath.classList.contains('lnkFile')) {
content.querySelector('#txtDirectoryPickerPath').value = path;
} else {
@ -192,25 +202,25 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
}
});
content.addEventListener('click', function(e) {
content.addEventListener('click', e => {
if (dom.parentWithClass(e.target, 'btnRefreshDirectories')) {
var path = content.querySelector('#txtDirectoryPickerPath').value;
const path = content.querySelector('#txtDirectoryPickerPath').value;
refreshDirectoryBrowser(content, path, fileOptions);
}
});
content.addEventListener('change', function(e) {
var txtDirectoryPickerPath = dom.parentWithTag(e.target, 'INPUT');
if (txtDirectoryPickerPath && 'txtDirectoryPickerPath' === txtDirectoryPickerPath.id) {
content.addEventListener('change', e => {
const txtDirectoryPickerPath = dom.parentWithTag(e.target, 'INPUT');
if (txtDirectoryPickerPath && txtDirectoryPickerPath.id === 'txtDirectoryPickerPath') {
refreshDirectoryBrowser(content, txtDirectoryPickerPath.value, fileOptions);
}
});
content.querySelector('form').addEventListener('submit', function(e) {
if (options.callback) {
var networkSharePath = this.querySelector('#txtNetworkPath');
let networkSharePath = this.querySelector('#txtNetworkPath');
networkSharePath = networkSharePath ? networkSharePath.value : null;
var path = this.querySelector('#txtDirectoryPickerPath').value;
const path = this.querySelector('#txtDirectoryPickerPath').value;
validatePath(path, options.validateWriteable, ApiClient).then(options.callback(path, networkSharePath));
}
e.preventDefault();
@ -224,77 +234,79 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
return Promise.resolve(options.path);
} else {
return ApiClient.getJSON(ApiClient.getUrl('Environment/DefaultDirectoryBrowser')).then(
function(result) {
result => {
return result.Path || '';
}, function() {
}, () => {
return '';
}
);
}
}
function directoryBrowser() {
var currentDialog;
var self = this;
self.show = function(options) {
options = options || {};
var fileOptions = {
includeDirectories: true
};
if (options.includeDirectories != null) {
fileOptions.includeDirectories = options.includeDirectories;
}
if (options.includeFiles != null) {
fileOptions.includeFiles = options.includeFiles;
}
Promise.all([getSystemInfo(), getDefaultPath(options)]).then(
function(responses) {
var systemInfo = responses[0];
var initialPath = responses[1];
var dlg = dialogHelper.createDialog({
size: 'small',
removeOnClose: true,
scrollY: false
});
dlg.classList.add('ui-body-a');
dlg.classList.add('background-theme-a');
dlg.classList.add('directoryPicker');
dlg.classList.add('formDialog');
var html = '';
html += '<div class="formDialogHeader">';
html += '<button is="paper-icon-button-light" class="btnCloseDialog autoSize" tabindex="-1"><span class="material-icons arrow_back"></span></button>';
html += '<h3 class="formDialogHeaderTitle">';
html += options.header || globalize.translate('HeaderSelectPath');
html += '</h3>';
html += '</div>';
html += getEditorHtml(options, systemInfo);
dlg.innerHTML = html;
initEditor(dlg, options, fileOptions);
dlg.addEventListener('close', onDialogClosed);
dialogHelper.open(dlg);
dlg.querySelector('.btnCloseDialog').addEventListener('click', function() {
dialogHelper.close(dlg);
});
currentDialog = dlg;
dlg.querySelector('#txtDirectoryPickerPath').value = initialPath;
var txtNetworkPath = dlg.querySelector('#txtNetworkPath');
if (txtNetworkPath) {
txtNetworkPath.value = options.networkSharePath || '';
}
if (!options.pathReadOnly) {
refreshDirectoryBrowser(dlg, initialPath, fileOptions, true);
}
class directoryBrowser {
constructor() {
let currentDialog;
this.show = options => {
options = options || {};
const fileOptions = {
includeDirectories: true
};
if (options.includeDirectories != null) {
fileOptions.includeDirectories = options.includeDirectories;
}
);
};
self.close = function() {
if (currentDialog) {
dialogHelper.close(currentDialog);
}
};
if (options.includeFiles != null) {
fileOptions.includeFiles = options.includeFiles;
}
Promise.all([getSystemInfo(), getDefaultPath(options)]).then(
responses => {
const systemInfo = responses[0];
const initialPath = responses[1];
const dlg = dialogHelper.createDialog({
size: 'small',
removeOnClose: true,
scrollY: false
});
dlg.classList.add('ui-body-a');
dlg.classList.add('background-theme-a');
dlg.classList.add('directoryPicker');
dlg.classList.add('formDialog');
let html = '';
html += '<div class="formDialogHeader">';
html += '<button is="paper-icon-button-light" class="btnCloseDialog autoSize" tabindex="-1"><span class="material-icons arrow_back"></span></button>';
html += '<h3 class="formDialogHeaderTitle">';
html += options.header || globalize.translate('HeaderSelectPath');
html += '</h3>';
html += '</div>';
html += getEditorHtml(options, systemInfo);
dlg.innerHTML = html;
initEditor(dlg, options, fileOptions);
dlg.addEventListener('close', onDialogClosed);
dialogHelper.open(dlg);
dlg.querySelector('.btnCloseDialog').addEventListener('click', () => {
dialogHelper.close(dlg);
});
currentDialog = dlg;
dlg.querySelector('#txtDirectoryPickerPath').value = initialPath;
const txtNetworkPath = dlg.querySelector('#txtNetworkPath');
if (txtNetworkPath) {
txtNetworkPath.value = options.networkSharePath || '';
}
if (!options.pathReadOnly) {
refreshDirectoryBrowser(dlg, initialPath, fileOptions, true);
}
}
);
};
this.close = () => {
if (currentDialog) {
dialogHelper.close(currentDialog);
}
};
}
}
var systemInfo;
return directoryBrowser;
});
let systemInfo;
/* eslint-enable indent */
export default directoryBrowser;

View file

@ -1,22 +1,37 @@
define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', 'apphost', 'focusManager', 'datetime', 'globalize', 'loading', 'connectionManager', 'skinManager', 'dom', 'events', 'emby-select', 'emby-checkbox', 'emby-button'], function (require, browser, layoutManager, appSettings, pluginManager, appHost, focusManager, datetime, globalize, loading, connectionManager, skinManager, dom, events) {
'use strict';
import browser from 'browser';
import layoutManager from 'layoutManager';
import appSettings from 'appSettings';
import pluginManager from 'pluginManager';
import appHost from 'apphost';
import focusManager from 'focusManager';
import datetime from 'datetime';
import globalize from 'globalize';
import loading from 'loading';
import connectionManager from 'connectionManager';
import skinManager from 'skinManager';
import events from 'events';
import 'emby-select';
import 'emby-checkbox';
import 'emby-button';
/* eslint-disable indent */
function fillThemes(select, isDashboard) {
select.innerHTML = skinManager.getThemes().map(function (t) {
var value = t.id;
select.innerHTML = skinManager.getThemes().map(t => {
let value = t.id;
if (t.isDefault && !isDashboard) {
value = '';
} else if (t.isDefaultServerDashboard && isDashboard) {
value = '';
}
return '<option value="' + value + '">' + t.name + '</option>';
return `<option value="${value}">${t.name}</option>`;
}).join('');
}
function loadScreensavers(context, userSettings) {
var selectScreensaver = context.querySelector('.selectScreensaver');
var options = pluginManager.ofType('screensaver').map(function (plugin) {
const selectScreensaver = context.querySelector('.selectScreensaver');
const options = pluginManager.ofType('screensaver').map(plugin => {
return {
name: plugin.name,
value: plugin.id
@ -28,8 +43,8 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
value: 'none'
});
selectScreensaver.innerHTML = options.map(function (o) {
return '<option value="' + o.value + '">' + o.name + '</option>';
selectScreensaver.innerHTML = options.map(o => {
return `<option value="${o.value}">${o.name}</option>`;
}).join('');
selectScreensaver.value = userSettings.screensaver();
@ -41,8 +56,8 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
function loadSoundEffects(context, userSettings) {
var selectSoundEffects = context.querySelector('.selectSoundEffects');
var options = pluginManager.ofType('soundeffects').map(function (plugin) {
const selectSoundEffects = context.querySelector('.selectSoundEffects');
const options = pluginManager.ofType('soundeffects').map(plugin => {
return {
name: plugin.name,
value: plugin.id
@ -54,8 +69,8 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
value: 'none'
});
selectSoundEffects.innerHTML = options.map(function (o) {
return '<option value="' + o.value + '">' + o.name + '</option>';
selectSoundEffects.innerHTML = options.map(o => {
return `<option value="${o.value}">${o.name}</option>`;
}).join('');
selectSoundEffects.value = userSettings.soundEffects();
@ -67,17 +82,17 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
function loadSkins(context, userSettings) {
var selectSkin = context.querySelector('.selectSkin');
const selectSkin = context.querySelector('.selectSkin');
var options = pluginManager.ofType('skin').map(function (plugin) {
const options = pluginManager.ofType('skin').map(plugin => {
return {
name: plugin.name,
value: plugin.id
};
});
selectSkin.innerHTML = options.map(function (o) {
return '<option value="' + o.value + '">' + o.name + '</option>';
selectSkin.innerHTML = options.map(o => {
return `<option value="${o.value}">${o.name}</option>`;
}).join('');
selectSkin.value = userSettings.skin();
@ -92,7 +107,7 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
}
}
function showOrHideMissingEpisodesField(context, user, apiClient) {
function showOrHideMissingEpisodesField(context) {
if (browser.tizen || browser.web0s) {
context.querySelector('.fldDisplayMissingEpisodes').classList.add('hide');
@ -102,10 +117,7 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
context.querySelector('.fldDisplayMissingEpisodes').classList.remove('hide');
}
function loadForm(context, user, userSettings, apiClient) {
var loggedInUserId = apiClient.getCurrentUserId();
var userId = user.Id;
function loadForm(context, user, userSettings) {
if (user.Policy.IsAdministrator) {
context.querySelector('.selectDashboardThemeContainer').classList.remove('hide');
@ -167,8 +179,8 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
context.querySelector('.chkRunAtStartup').checked = appSettings.runAtStartup();
var selectTheme = context.querySelector('#selectTheme');
var selectDashboardTheme = context.querySelector('#selectDashboardTheme');
const selectTheme = context.querySelector('#selectTheme');
const selectDashboardTheme = context.querySelector('#selectDashboardTheme');
fillThemes(selectTheme);
fillThemes(selectDashboardTheme, true);
@ -195,7 +207,7 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
context.querySelector('.selectLayout').value = layoutManager.getSavedLayout() || '';
showOrHideMissingEpisodesField(context, user, apiClient);
showOrHideMissingEpisodesField(context);
loading.hide();
}
@ -239,29 +251,29 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
function save(instance, context, userId, userSettings, apiClient, enableSaveConfirmation) {
loading.show();
apiClient.getUser(userId).then(function (user) {
saveUser(context, user, userSettings, apiClient).then(function () {
apiClient.getUser(userId).then(user => {
saveUser(context, user, userSettings, apiClient).then(() => {
loading.hide();
if (enableSaveConfirmation) {
require(['toast'], function (toast) {
import('toast').then(({default: toast}) => {
toast(globalize.translate('SettingsSaved'));
});
}
events.trigger(instance, 'saved');
}, function () {
}, () => {
loading.hide();
});
});
}
function onSubmit(e) {
var self = this;
var apiClient = connectionManager.getApiClient(self.options.serverId);
var userId = self.options.userId;
var userSettings = self.options.userSettings;
const self = this;
const apiClient = connectionManager.getApiClient(self.options.serverId);
const userId = self.options.userId;
const userSettings = self.options.userSettings;
userSettings.setUserInfo(userId, apiClient).then(function () {
var enableSaveConfirmation = self.options.enableSaveConfirmation;
userSettings.setUserInfo(userId, apiClient).then(() => {
const enableSaveConfirmation = self.options.enableSaveConfirmation;
save(self, self.options.element, userId, userSettings, apiClient, enableSaveConfirmation);
});
@ -272,50 +284,51 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
return false;
}
function embed(options, self) {
require(['text!./displaySettings.template.html'], function (template) {
options.element.innerHTML = globalize.translateDocument(template, 'core');
options.element.querySelector('form').addEventListener('submit', onSubmit.bind(self));
if (options.enableSaveButton) {
options.element.querySelector('.btnSave').classList.remove('hide');
}
self.loadData(options.autoFocus);
});
async function embed(options, self) {
const { default: template } = await import('text!./displaySettings.template.html');
options.element.innerHTML = globalize.translateDocument(template, 'core');
options.element.querySelector('form').addEventListener('submit', onSubmit.bind(self));
if (options.enableSaveButton) {
options.element.querySelector('.btnSave').classList.remove('hide');
}
self.loadData(options.autoFocus);
}
function DisplaySettings(options) {
this.options = options;
embed(options, this);
}
class DisplaySettings {
constructor(options) {
this.options = options;
embed(options, this);
}
DisplaySettings.prototype.loadData = function (autoFocus) {
var self = this;
var context = self.options.element;
loadData(autoFocus) {
const self = this;
const context = self.options.element;
loading.show();
loading.show();
var userId = self.options.userId;
var apiClient = connectionManager.getApiClient(self.options.serverId);
var userSettings = self.options.userSettings;
const userId = self.options.userId;
const apiClient = connectionManager.getApiClient(self.options.serverId);
const userSettings = self.options.userSettings;
return apiClient.getUser(userId).then(function (user) {
return userSettings.setUserInfo(userId, apiClient).then(function () {
self.dataLoaded = true;
loadForm(context, user, userSettings, apiClient);
if (autoFocus) {
focusManager.autoFocus(context);
}
return apiClient.getUser(userId).then(user => {
return userSettings.setUserInfo(userId, apiClient).then(() => {
self.dataLoaded = true;
loadForm(context, user, userSettings);
if (autoFocus) {
focusManager.autoFocus(context);
}
});
});
});
};
}
DisplaySettings.prototype.submit = function () {
onSubmit.call(this);
};
submit() {
onSubmit.call(this);
}
DisplaySettings.prototype.destroy = function () {
this.options = null;
};
destroy() {
this.options = null;
}
}
return DisplaySettings;
});
/* eslint-enable indent */
export default DisplaySettings;

View file

@ -55,7 +55,7 @@
/* Without this emby-checkbox is able to appear on top */
z-index: 1;
align-items: flex-end;
justify-content: flex-end;
justify-content: center;
flex-wrap: wrap;
}

View file

@ -1,5 +1,8 @@
define(['dom', 'appRouter', 'connectionManager'], function (dom, appRouter, connectionManager) {
'use strict';
/* eslint-disable indent */
import dom from 'dom';
import appRouter from 'appRouter';
import connectionManager from 'connectionManager';
function onGroupedCardClick(e, card) {
var itemId = card.getAttribute('data-id');
@ -31,7 +34,7 @@ define(['dom', 'appRouter', 'connectionManager'], function (dom, appRouter, conn
}
}
function onItemsContainerClick(e) {
export function onItemsContainerClick(e) {
var groupedCard = dom.parentWithClass(e.target, 'groupedCard');
if (groupedCard) {
@ -39,7 +42,4 @@ define(['dom', 'appRouter', 'connectionManager'], function (dom, appRouter, conn
}
}
return {
onItemsContainerClick: onItemsContainerClick
};
});
/* eslint-enable indent */

View file

@ -1,26 +1,37 @@
define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loading', 'connectionManager', 'homeSections', 'dom', 'events', 'listViewStyle', 'emby-select', 'emby-checkbox'], function (require, appHost, layoutManager, focusManager, globalize, loading, connectionManager, homeSections, dom, events) {
'use strict';
import layoutManager from 'layoutManager';
import focusManager from 'focusManager';
import globalize from 'globalize';
import loading from 'loading';
import connectionManager from 'connectionManager';
import homeSections from 'homeSections';
import dom from 'dom';
import events from 'events';
import 'listViewStyle';
import 'emby-select';
import 'emby-checkbox';
var numConfigurableSections = 7;
/* eslint-disable indent */
const numConfigurableSections = 7;
function renderViews(page, user, result) {
var folderHtml = '';
let folderHtml = '';
folderHtml += '<div class="checkboxList">';
folderHtml += result.map(function (i) {
folderHtml += result.map(i => {
var currentHtml = '';
let currentHtml = '';
var id = 'chkGroupFolder' + i.Id;
const id = `chkGroupFolder${i.Id}`;
var isChecked = user.Configuration.GroupedFolders.indexOf(i.Id) !== -1;
const isChecked = user.Configuration.GroupedFolders.includes(i.Id);
var checkedHtml = isChecked ? ' checked="checked"' : '';
const checkedHtml = isChecked ? ' checked="checked"' : '';
currentHtml += '<label>';
currentHtml += '<input type="checkbox" is="emby-checkbox" class="chkGroupFolder" data-folderid="' + i.Id + '" id="' + id + '"' + checkedHtml + '/>';
currentHtml += '<span>' + i.Name + '</span>';
currentHtml += `<input type="checkbox" is="emby-checkbox" class="chkGroupFolder" data-folderid="${i.Id}" id="${id}"${checkedHtml}/>`;
currentHtml += `<span>${i.Name}</span>`;
currentHtml += '</label>';
return currentHtml;
@ -34,7 +45,7 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
function getLandingScreenOptions(type) {
var list = [];
const list = [];
if (type === 'movies') {
list.push({
@ -123,27 +134,27 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
function getLandingScreenOptionsHtml(type, userValue) {
return getLandingScreenOptions(type).map(function (o) {
return getLandingScreenOptions(type).map(o => {
var selected = userValue === o.value || (o.isDefault && !userValue);
var selectedHtml = selected ? ' selected' : '';
var optionValue = o.isDefault ? '' : o.value;
const selected = userValue === o.value || (o.isDefault && !userValue);
const selectedHtml = selected ? ' selected' : '';
const optionValue = o.isDefault ? '' : o.value;
return '<option value="' + optionValue + '"' + selectedHtml + '>' + o.name + '</option>';
return `<option value="${optionValue}"${selectedHtml}>${o.name}</option>`;
}).join('');
}
function renderViewOrder(context, user, result) {
var html = '';
let html = '';
var index = 0;
let index = 0;
html += result.Items.map(function (view) {
html += result.Items.map(view => {
var currentHtml = '';
let currentHtml = '';
currentHtml += '<div class="listItem viewItem" data-viewid="' + view.Id + '">';
currentHtml += `<div class="listItem viewItem" data-viewid="${view.Id}">`;
currentHtml += '<span class="material-icons listItemIcon folder_open"></span>';
@ -155,8 +166,8 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
currentHtml += '</div>';
currentHtml += '<button type="button" is="paper-icon-button-light" class="btnViewItemUp btnViewItemMove autoSize" title="' + globalize.translate('Up') + '"><span class="material-icons keyboard_arrow_up"></span></button>';
currentHtml += '<button type="button" is="paper-icon-button-light" class="btnViewItemDown btnViewItemMove autoSize" title="' + globalize.translate('Down') + '"><span class="material-icons keyboard_arrow_down"></span></button>';
currentHtml += `<button type="button" is="paper-icon-button-light" class="btnViewItemUp btnViewItemMove autoSize" title="${globalize.translate('Up')}"><span class="material-icons keyboard_arrow_up"></span></button>`;
currentHtml += `<button type="button" is="paper-icon-button-light" class="btnViewItemDown btnViewItemMove autoSize" title="${globalize.translate('Down')}"><span class="material-icons keyboard_arrow_down"></span></button>`;
currentHtml += '</div>';
@ -170,14 +181,14 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
function updateHomeSectionValues(context, userSettings) {
for (var i = 1; i <= 7; i++) {
for (let i = 1; i <= 7; i++) {
var select = context.querySelector('#selectHomeSection' + i);
var defaultValue = homeSections.getDefaultSection(i - 1);
const select = context.querySelector(`#selectHomeSection${i}`);
const defaultValue = homeSections.getDefaultSection(i - 1);
var option = select.querySelector('option[value=' + defaultValue + ']') || select.querySelector('option[value=""]');
const option = select.querySelector(`option[value=${defaultValue}]`) || select.querySelector('option[value=""]');
var userValue = userSettings.get('homesection' + (i - 1));
const userValue = userSettings.get(`homesection${i - 1}`);
option.value = '';
@ -193,42 +204,42 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
function getPerLibrarySettingsHtml(item, user, userSettings, apiClient) {
var html = '';
let html = '';
var isChecked;
let isChecked;
if (item.Type === 'Channel' || item.CollectionType === 'boxsets' || item.CollectionType === 'playlists') {
isChecked = (user.Configuration.MyMediaExcludes || []).indexOf(item.Id) === -1;
isChecked = !(user.Configuration.MyMediaExcludes || []).includes(item.Id);
html += '<div>';
html += '<label>';
html += '<input type="checkbox" is="emby-checkbox" class="chkIncludeInMyMedia" data-folderid="' + item.Id + '"' + (isChecked ? ' checked="checked"' : '') + '/>';
html += '<span>' + globalize.translate('DisplayInMyMedia') + '</span>';
html += `<input type="checkbox" is="emby-checkbox" class="chkIncludeInMyMedia" data-folderid="${item.Id}"${isChecked ? ' checked="checked"' : ''}/>`;
html += `<span>${globalize.translate('DisplayInMyMedia')}</span>`;
html += '</label>';
html += '</div>';
}
var excludeFromLatest = ['playlists', 'livetv', 'boxsets', 'channels'];
if (excludeFromLatest.indexOf(item.CollectionType || '') === -1) {
const excludeFromLatest = ['playlists', 'livetv', 'boxsets', 'channels'];
if (!excludeFromLatest.includes(item.CollectionType || '')) {
isChecked = user.Configuration.LatestItemsExcludes.indexOf(item.Id) === -1;
isChecked = !user.Configuration.LatestItemsExcludes.includes(item.Id);
html += '<label class="fldIncludeInLatest">';
html += '<input type="checkbox" is="emby-checkbox" class="chkIncludeInLatest" data-folderid="' + item.Id + '"' + (isChecked ? ' checked="checked"' : '') + '/>';
html += '<span>' + globalize.translate('DisplayInOtherHomeScreenSections') + '</span>';
html += `<input type="checkbox" is="emby-checkbox" class="chkIncludeInLatest" data-folderid="${item.Id}"${isChecked ? ' checked="checked"' : ''}/>`;
html += `<span>${globalize.translate('DisplayInOtherHomeScreenSections')}</span>`;
html += '</label>';
}
if (html) {
html = '<div class="checkboxListContainer">' + html + '</div>';
html = `<div class="checkboxListContainer">${html}</div>`;
}
if (item.CollectionType === 'movies' || item.CollectionType === 'tvshows' || item.CollectionType === 'music' || item.CollectionType === 'livetv') {
var idForLanding = item.CollectionType === 'livetv' ? item.CollectionType : item.Id;
const idForLanding = item.CollectionType === 'livetv' ? item.CollectionType : item.Id;
html += '<div class="selectContainer">';
html += '<select is="emby-select" class="selectLanding" data-folderid="' + idForLanding + '" label="' + globalize.translate('LabelDefaultScreen') + '">';
html += `<select is="emby-select" class="selectLanding" data-folderid="${idForLanding}" label="${globalize.translate('LabelDefaultScreen')}">`;
var userValue = userSettings.get('landing-' + idForLanding);
const userValue = userSettings.get(`landing-${idForLanding}`);
html += getLandingScreenOptionsHtml(item.CollectionType, userValue);
@ -238,7 +249,7 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
if (html) {
var prefix = '';
let prefix = '';
prefix += '<div class="verticalSection">';
prefix += '<h2 class="sectionTitle">';
@ -254,10 +265,10 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
function renderPerLibrarySettings(context, user, userViews, userSettings, apiClient) {
var elem = context.querySelector('.perLibrarySettings');
var html = '';
const elem = context.querySelector('.perLibrarySettings');
let html = '';
for (var i = 0, length = userViews.length; i < length; i++) {
for (let i = 0, length = userViews.length; i < length; i++) {
html += getPerLibrarySettingsHtml(userViews[i], user, userSettings, apiClient);
}
@ -271,10 +282,10 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
updateHomeSectionValues(context, userSettings);
var promise1 = apiClient.getUserViews({ IncludeHidden: true }, user.Id);
var promise2 = apiClient.getJSON(apiClient.getUrl('Users/' + user.Id + '/GroupingOptions'));
const promise1 = apiClient.getUserViews({ IncludeHidden: true }, user.Id);
const promise2 = apiClient.getJSON(apiClient.getUrl(`Users/${user.Id}/GroupingOptions`));
Promise.all([promise1, promise2]).then(function (responses) {
Promise.all([promise1, promise2]).then(responses => {
renderViewOrder(context, user, responses[0]);
@ -286,38 +297,19 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
});
}
function getSibling(elem, type, className) {
var sibling = elem[type];
while (sibling != null) {
if (sibling.classList.contains(className)) {
break;
}
}
if (sibling != null) {
if (!sibling.classList.contains(className)) {
sibling = null;
}
}
return sibling;
}
function onSectionOrderListClick(e) {
var target = dom.parentWithClass(e.target, 'btnViewItemMove');
const target = dom.parentWithClass(e.target, 'btnViewItemMove');
if (target) {
var viewItem = dom.parentWithClass(target, 'viewItem');
const viewItem = dom.parentWithClass(target, 'viewItem');
if (viewItem) {
var ul = dom.parentWithClass(viewItem, 'paperList');
const ul = dom.parentWithClass(viewItem, 'paperList');
if (target.classList.contains('btnViewItemDown')) {
var next = viewItem.nextSibling;
const next = viewItem.nextSibling;
if (next) {
viewItem.parentNode.removeChild(viewItem);
@ -327,7 +319,7 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
} else {
var prev = viewItem.previousSibling;
const prev = viewItem.previousSibling;
if (prev) {
viewItem.parentNode.removeChild(viewItem);
@ -341,10 +333,10 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
function getCheckboxItems(selector, context, isChecked) {
var inputs = context.querySelectorAll(selector);
var list = [];
const inputs = context.querySelectorAll(selector);
const list = [];
for (var i = 0, length = inputs.length; i < length; i++) {
for (let i = 0, length = inputs.length; i < length; i++) {
if (inputs[i].checked === isChecked) {
list.push(inputs[i]);
@ -359,25 +351,25 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
user.Configuration.HidePlayedInLatest = context.querySelector('.chkHidePlayedFromLatest').checked;
user.Configuration.LatestItemsExcludes = getCheckboxItems('.chkIncludeInLatest', context, false).map(function (i) {
user.Configuration.LatestItemsExcludes = getCheckboxItems('.chkIncludeInLatest', context, false).map(i => {
return i.getAttribute('data-folderid');
});
user.Configuration.MyMediaExcludes = getCheckboxItems('.chkIncludeInMyMedia', context, false).map(function (i) {
user.Configuration.MyMediaExcludes = getCheckboxItems('.chkIncludeInMyMedia', context, false).map(i => {
return i.getAttribute('data-folderid');
});
user.Configuration.GroupedFolders = getCheckboxItems('.chkGroupFolder', context, true).map(function (i) {
user.Configuration.GroupedFolders = getCheckboxItems('.chkGroupFolder', context, true).map(i => {
return i.getAttribute('data-folderid');
});
var viewItems = context.querySelectorAll('.viewItem');
var orderedViews = [];
var i;
var length;
const viewItems = context.querySelectorAll('.viewItem');
const orderedViews = [];
let i;
let length;
for (i = 0, length = viewItems.length; i < length; i++) {
orderedViews.push(viewItems[i].getAttribute('data-viewid'));
}
@ -394,10 +386,10 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
userSettingsInstance.set('homesection5', context.querySelector('#selectHomeSection6').value);
userSettingsInstance.set('homesection6', context.querySelector('#selectHomeSection7').value);
var selectLandings = context.querySelectorAll('.selectLanding');
const selectLandings = context.querySelectorAll('.selectLanding');
for (i = 0, length = selectLandings.length; i < length; i++) {
var selectLanding = selectLandings[i];
userSettingsInstance.set('landing-' + selectLanding.getAttribute('data-folderid'), selectLanding.value);
const selectLanding = selectLandings[i];
userSettingsInstance.set(`landing-${selectLanding.getAttribute('data-folderid')}`, selectLanding.value);
}
return apiClient.updateUserConfiguration(user.Id, user.Configuration);
@ -407,20 +399,20 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
loading.show();
apiClient.getUser(userId).then(function (user) {
apiClient.getUser(userId).then(user => {
saveUser(context, user, userSettings, apiClient).then(function () {
saveUser(context, user, userSettings, apiClient).then(() => {
loading.hide();
if (enableSaveConfirmation) {
require(['toast'], function (toast) {
import('toast').then(({default: toast}) => {
toast(globalize.translate('SettingsSaved'));
});
}
events.trigger(instance, 'saved');
}, function () {
}, () => {
loading.hide();
});
});
@ -428,14 +420,14 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
function onSubmit(e) {
var self = this;
var apiClient = connectionManager.getApiClient(self.options.serverId);
var userId = self.options.userId;
var userSettings = self.options.userSettings;
const self = this;
const apiClient = connectionManager.getApiClient(self.options.serverId);
const userId = self.options.userId;
const userSettings = self.options.userSettings;
userSettings.setUserInfo(userId, apiClient).then(function () {
userSettings.setUserInfo(userId, apiClient).then(() => {
var enableSaveConfirmation = self.options.enableSaveConfirmation;
const enableSaveConfirmation = self.options.enableSaveConfirmation;
save(self, self.options.element, userId, userSettings, apiClient, enableSaveConfirmation);
});
@ -448,13 +440,13 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
function onChange(e) {
var chkIncludeInMyMedia = dom.parentWithClass(e.target, 'chkIncludeInMyMedia');
const chkIncludeInMyMedia = dom.parentWithClass(e.target, 'chkIncludeInMyMedia');
if (!chkIncludeInMyMedia) {
return;
}
var section = dom.parentWithClass(chkIncludeInMyMedia, 'verticalSection');
var fldIncludeInLatest = section.querySelector('.fldIncludeInLatest');
const section = dom.parentWithClass(chkIncludeInMyMedia, 'verticalSection');
const fldIncludeInLatest = section.querySelector('.fldIncludeInLatest');
if (fldIncludeInLatest) {
if (chkIncludeInMyMedia.checked) {
fldIncludeInLatest.classList.remove('hide');
@ -466,10 +458,10 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
function embed(options, self) {
require(['text!./homeScreenSettings.template.html'], function (template) {
return import('text!./homeScreenSettings.template.html').then(({default: template}) => {
for (var i = 1; i <= numConfigurableSections; i++) {
template = template.replace('{section' + i + 'label}', globalize.translate('LabelHomeScreenSectionValue', i));
for (let i = 1; i <= numConfigurableSections; i++) {
template = template.replace(`{section${i}label}`, globalize.translate('LabelHomeScreenSectionValue', i));
}
options.element.innerHTML = globalize.translateDocument(template, 'core');
@ -492,47 +484,48 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
});
}
function HomeScreenSettings(options) {
class HomeScreenSettings {
constructor(options) {
this.options = options;
embed(options, this);
}
this.options = options;
loadData(autoFocus) {
embed(options, this);
const self = this;
const context = self.options.element;
loading.show();
const userId = self.options.userId;
const apiClient = connectionManager.getApiClient(self.options.serverId);
const userSettings = self.options.userSettings;
apiClient.getUser(userId).then(user => {
userSettings.setUserInfo(userId, apiClient).then(() => {
self.dataLoaded = true;
loadForm(context, user, userSettings, apiClient);
if (autoFocus) {
focusManager.autoFocus(context);
}
});
});
}
submit() {
onSubmit.call(this);
}
destroy() {
this.options = null;
}
}
HomeScreenSettings.prototype.loadData = function (autoFocus) {
/* eslint-enable indent */
var self = this;
var context = self.options.element;
loading.show();
var userId = self.options.userId;
var apiClient = connectionManager.getApiClient(self.options.serverId);
var userSettings = self.options.userSettings;
apiClient.getUser(userId).then(function (user) {
userSettings.setUserInfo(userId, apiClient).then(function () {
self.dataLoaded = true;
loadForm(context, user, userSettings, apiClient);
if (autoFocus) {
focusManager.autoFocus(context);
}
});
});
};
HomeScreenSettings.prototype.submit = function () {
onSubmit.call(this);
};
HomeScreenSettings.prototype.destroy = function () {
this.options = null;
};
return HomeScreenSettings;
});
export default HomeScreenSettings;

View file

@ -168,7 +168,7 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la
}
function getPortraitShape() {
return enableScrollX() ? 'autooverflow' : 'auto';
return enableScrollX() ? 'overflowPortrait' : 'portrait';
}
function getLibraryButtonsHtml(items) {
@ -254,7 +254,7 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la
return cardBuilder.getCardsHtml({
items: items,
shape: shape,
preferThumb: viewType !== 'movies' && itemType !== 'Channel' && viewType !== 'music' ? 'auto' : null,
preferThumb: viewType !== 'movies' && viewType !== 'tvshows' && itemType !== 'Channel' && viewType !== 'music' ? 'auto' : null,
showUnplayedIndicator: false,
showChildCountIndicator: true,
context: 'home',

View file

@ -1,17 +1,20 @@
define(['appSettings', 'browser', 'events'], function (appSettings, browser, events) {
'use strict';
/* eslint-disable indent */
function getSavedVolume() {
import appSettings from 'appSettings' ;
import browser from 'browser';
import events from 'events';
export function getSavedVolume() {
return appSettings.get('volume') || 1;
}
function saveVolume(value) {
export function saveVolume(value) {
if (value) {
appSettings.set('volume', value);
}
}
function getCrossOriginValue(mediaSource) {
export function getCrossOriginValue(mediaSource) {
if (mediaSource.IsRemote) {
return null;
}
@ -30,7 +33,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve
return false;
}
function enableHlsShakaPlayer(item, mediaSource, mediaType) {
export function enableHlsShakaPlayer(item, mediaSource, mediaType) {
/* eslint-disable-next-line compat/compat */
if (!!window.MediaSource && !!MediaSource.isTypeSupported) {
@ -56,7 +59,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve
return false;
}
function enableHlsJsPlayer(runTimeTicks, mediaType) {
export function enableHlsJsPlayer(runTimeTicks, mediaType) {
if (window.MediaSource == null) {
return false;
@ -98,7 +101,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve
var recoverDecodingErrorDate;
var recoverSwapAudioCodecDate;
function handleHlsJsMediaError(instance, reject) {
export function handleHlsJsMediaError(instance, reject) {
var hlsPlayer = instance._hlsPlayer;
@ -134,7 +137,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve
}
}
function onErrorInternal(instance, type) {
export function onErrorInternal(instance, type) {
// Needed for video
if (instance.destroyCustomTrack) {
@ -148,7 +151,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve
]);
}
function isValidDuration(duration) {
export function isValidDuration(duration) {
if (duration && !isNaN(duration) && duration !== Number.POSITIVE_INFINITY && duration !== Number.NEGATIVE_INFINITY) {
return true;
}
@ -162,7 +165,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve
}
}
function seekOnPlaybackStart(instance, element, ticks, onMediaReady) {
export function seekOnPlaybackStart(instance, element, ticks, onMediaReady) {
var seconds = (ticks || 0) / 10000000;
@ -200,7 +203,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve
}
}
function applySrc(elem, src, options) {
export function applySrc(elem, src, options) {
if (window.Windows && options.mediaSource && options.mediaSource.IsLocal) {
@ -228,7 +231,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve
elem.addEventListener('error', onErrorFn);
}
function playWithPromise(elem, onErrorFn) {
export function playWithPromise(elem, onErrorFn) {
try {
var promise = elem.play();
@ -256,7 +259,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve
}
}
function destroyCastPlayer(instance) {
export function destroyCastPlayer(instance) {
var player = instance._castPlayer;
if (player) {
@ -270,7 +273,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve
}
}
function destroyShakaPlayer(instance) {
export function destroyShakaPlayer(instance) {
var player = instance._shakaPlayer;
if (player) {
try {
@ -283,7 +286,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve
}
}
function destroyHlsPlayer(instance) {
export function destroyHlsPlayer(instance) {
var player = instance._hlsPlayer;
if (player) {
try {
@ -296,7 +299,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve
}
}
function destroyFlvPlayer(instance) {
export function destroyFlvPlayer(instance) {
var player = instance._flvPlayer;
if (player) {
try {
@ -311,7 +314,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve
}
}
function bindEventsToHlsPlayer(instance, hls, elem, onErrorFn, resolve, reject) {
export function bindEventsToHlsPlayer(instance, hls, elem, onErrorFn, resolve, reject) {
hls.on(Hls.Events.MANIFEST_PARSED, function () {
playWithPromise(elem, onErrorFn).then(resolve, function () {
@ -403,7 +406,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve
});
}
function onEndedInternal(instance, elem, onErrorFn) {
export function onEndedInternal(instance, elem, onErrorFn) {
elem.removeEventListener('error', onErrorFn);
@ -427,7 +430,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve
instance._currentPlayOptions = null;
}
function getBufferedRanges(instance, elem) {
export function getBufferedRanges(instance, elem) {
var ranges = [];
var seekable = elem.buffered || [];
@ -462,23 +465,4 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve
return ranges;
}
return {
getSavedVolume: getSavedVolume,
saveVolume: saveVolume,
enableHlsJsPlayer: enableHlsJsPlayer,
enableHlsShakaPlayer: enableHlsShakaPlayer,
handleHlsJsMediaError: handleHlsJsMediaError,
isValidDuration: isValidDuration,
onErrorInternal: onErrorInternal,
seekOnPlaybackStart: seekOnPlaybackStart,
applySrc: applySrc,
playWithPromise: playWithPromise,
destroyHlsPlayer: destroyHlsPlayer,
destroyFlvPlayer: destroyFlvPlayer,
destroyCastPlayer: destroyCastPlayer,
bindEventsToHlsPlayer: bindEventsToHlsPlayer,
onEndedInternal: onEndedInternal,
getCrossOriginValue: getCrossOriginValue,
getBufferedRanges: getBufferedRanges
};
});
/* eslint-enable indent */

View file

@ -1,10 +1,26 @@
define(['dialogHelper', 'connectionManager', 'dom', 'loading', 'scrollHelper', 'layoutManager', 'globalize', 'require', 'emby-button', 'emby-select', 'formDialogStyle', 'css!./style'], function (dialogHelper, connectionManager, dom, loading, scrollHelper, layoutManager, globalize, require) {
'use strict';
/* eslint-disable indent */
var currentItemId;
var currentServerId;
var currentFile;
var hasChanges = false;
/**
* Module for imageUploader.
* @module components/imageUploader/imageUploader
*/
import dialogHelper from 'dialogHelper';
import connectionManager from 'connectionManager';
import dom from 'dom';
import loading from 'loading';
import scrollHelper from 'scrollHelper';
import layoutManager from 'layoutManager';
import globalize from 'globalize';
import 'emby-button';
import 'emby-select';
import 'formDialogStyle';
import 'css!./style';
let currentItemId;
let currentServerId;
let currentFile;
let hasChanges = false;
function onFileReaderError(evt) {
@ -12,14 +28,14 @@ define(['dialogHelper', 'connectionManager', 'dom', 'loading', 'scrollHelper', '
switch (evt.target.error.code) {
case evt.target.error.NOT_FOUND_ERR:
require(['toast'], function (toast) {
import('toast').then(({default: toast}) => {
toast(globalize.translate('MessageFileReadError'));
});
break;
case evt.target.error.ABORT_ERR:
break; // noop
default:
require(['toast'], function (toast) {
import('toast').then(({default: toast}) => {
toast(globalize.translate('MessageFileReadError'));
});
break;
@ -28,7 +44,7 @@ define(['dialogHelper', 'connectionManager', 'dom', 'loading', 'scrollHelper', '
function setFiles(page, files) {
var file = files[0];
const file = files[0];
if (!file || !file.type.match('image.*')) {
page.querySelector('#imageOutput').innerHTML = '';
@ -39,23 +55,23 @@ define(['dialogHelper', 'connectionManager', 'dom', 'loading', 'scrollHelper', '
currentFile = file;
var reader = new FileReader();
const reader = new FileReader();
reader.onerror = onFileReaderError;
reader.onloadstart = function () {
reader.onloadstart = () => {
page.querySelector('#fldUpload').classList.add('hide');
};
reader.onabort = function () {
reader.onabort = () => {
loading.hide();
console.debug('File read cancelled');
};
// Closure to capture the file information.
reader.onload = (function (theFile) {
return function (e) {
reader.onload = (theFile => {
return e => {
// Render thumbnail.
var html = ['<img style="max-width:100%;max-height:100%;" src="', e.target.result, '" title="', escape(theFile.name), '"/>'].join('');
const html = ['<img style="max-width:100%;max-height:100%;" src="', e.target.result, '" title="', escape(theFile.name), '"/>'].join('');
page.querySelector('#imageOutput').innerHTML = html;
page.querySelector('#dropImageText').classList.add('hide');
@ -69,14 +85,14 @@ define(['dialogHelper', 'connectionManager', 'dom', 'loading', 'scrollHelper', '
function onSubmit(e) {
var file = currentFile;
const file = currentFile;
if (!file) {
return false;
}
if (!file.type.startsWith('image/')) {
require(['toast'], function (toast) {
import('toast').then(({default: toast}) => {
toast(globalize.translate('MessageImageFileTypeAllowed'));
});
e.preventDefault();
@ -85,18 +101,18 @@ define(['dialogHelper', 'connectionManager', 'dom', 'loading', 'scrollHelper', '
loading.show();
var dlg = dom.parentWithClass(this, 'dialog');
const dlg = dom.parentWithClass(this, 'dialog');
var imageType = dlg.querySelector('#selectImageType').value;
const imageType = dlg.querySelector('#selectImageType').value;
if (imageType === 'None') {
require(['toast'], function(toast) {
import('toast').then(({default: toast}) => {
toast(globalize.translate('MessageImageTypeNotSelected'));
});
e.preventDefault();
return false;
}
connectionManager.getApiClient(currentServerId).uploadItemImage(currentItemId, imageType, file).then(function () {
connectionManager.getApiClient(currentServerId).uploadItemImage(currentItemId, imageType, file).then(() => {
dlg.querySelector('#uploadImage').value = '';
@ -117,21 +133,21 @@ define(['dialogHelper', 'connectionManager', 'dom', 'loading', 'scrollHelper', '
setFiles(page, this.files);
});
page.querySelector('.btnBrowse').addEventListener('click', function () {
page.querySelector('.btnBrowse').addEventListener('click', () => {
page.querySelector('#uploadImage').click();
});
}
function showEditor(options, resolve, reject) {
function showEditor(options, resolve) {
options = options || {};
require(['text!./imageUploader.template.html'], function (template) {
return import('text!./imageUploader.template.html').then(({default: template}) => {
currentItemId = options.itemId;
currentServerId = options.serverId;
var dialogOptions = {
const dialogOptions = {
removeOnClose: true
};
@ -141,7 +157,7 @@ define(['dialogHelper', 'connectionManager', 'dom', 'loading', 'scrollHelper', '
dialogOptions.size = 'small';
}
var dlg = dialogHelper.createDialog(dialogOptions);
const dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog');
@ -152,7 +168,7 @@ define(['dialogHelper', 'connectionManager', 'dom', 'loading', 'scrollHelper', '
}
// Has to be assigned a z-index after the call to .open()
dlg.addEventListener('close', function () {
dlg.addEventListener('close', () => {
if (layoutManager.tv) {
scrollHelper.centerFocus.off(dlg, false);
@ -168,22 +184,24 @@ define(['dialogHelper', 'connectionManager', 'dom', 'loading', 'scrollHelper', '
dlg.querySelector('#selectImageType').value = options.imageType || 'Primary';
dlg.querySelector('.btnCancel').addEventListener('click', function () {
dlg.querySelector('.btnCancel').addEventListener('click', () => {
dialogHelper.close(dlg);
});
});
}
return {
show: function (options) {
export function show(options) {
return new Promise(function (resolve, reject) {
return new Promise(resolve => {
hasChanges = false;
hasChanges = false;
showEditor(options, resolve, reject);
});
}
};
});
showEditor(options, resolve);
});
}
/* eslint-enable indent */
export default {
show: show
};

View file

@ -20,7 +20,7 @@
</div>
<div>
<div class="imageEditor-dropZone fieldDescription">
<div class="dropImageText">${LabelDropImageHere}</div>
<div id="dropImageText">${LabelDropImageHere}</div>
<output id="imageOutput" class="flex align-items-center justify-content-center" style="position: absolute;top:0;left:0;right:0;bottom:0;width:100%;"></output>
<input type="file" accept="image/*" id="uploadImage" name="uploadImage" style="position: absolute;top:0;left:0;right:0;bottom:0;width:100%;opacity:0;" />
</div>

View file

@ -1,11 +1,11 @@
.lazy-image-fadein {
opacity: 1;
transition: opacity 0.7s;
transition: opacity 0.5s;
}
.lazy-image-fadein-fast {
opacity: 1;
transition: opacity 0.2s;
transition: opacity 0.1s;
}
.lazy-hidden {

View file

@ -1,14 +1,24 @@
define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter', 'playbackManager', 'loading', 'appSettings', 'browser', 'actionsheet'], function (appHost, globalize, connectionManager, itemHelper, appRouter, playbackManager, loading, appSettings, browser, actionsheet) {
'use strict';
import appHost from 'apphost';
import globalize from 'globalize';
import connectionManager from 'connectionManager';
import itemHelper from 'itemHelper';
import appRouter from 'appRouter';
import playbackManager from 'playbackManager';
import loading from 'loading';
import appSettings from 'appSettings';
import browser from 'browser';
import actionsheet from 'actionsheet';
function getCommands(options) {
var item = options.item;
var user = options.user;
/* eslint-disable indent */
var canPlay = playbackManager.canPlay(item);
var restrictOptions = (browser.operaTv || browser.web0s) && !user.Policy.IsAdministrator;
export function getCommands(options) {
const item = options.item;
const user = options.user;
var commands = [];
const canPlay = playbackManager.canPlay(item);
const restrictOptions = (browser.operaTv || browser.web0s) && !user.Policy.IsAdministrator;
let commands = [];
if (canPlay && item.MediaType !== 'Photo') {
if (options.play !== false) {
@ -161,10 +171,10 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
});
}
var canEdit = itemHelper.canEdit(user, item);
const canEdit = itemHelper.canEdit(user, item);
if (canEdit) {
if (options.edit !== false && item.Type !== 'SeriesTimer') {
var text = (item.Type === 'Timer' || item.Type === 'SeriesTimer') ? globalize.translate('Edit') : globalize.translate('EditMetadata');
const text = (item.Type === 'Timer' || item.Type === 'SeriesTimer') ? globalize.translate('Edit') : globalize.translate('EditMetadata');
commands.push({
name: text,
id: 'edit',
@ -311,31 +321,31 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
}
function executeCommand(item, id, options) {
var itemId = item.Id;
var serverId = item.ServerId;
var apiClient = connectionManager.getApiClient(serverId);
const itemId = item.Id;
const serverId = item.ServerId;
const apiClient = connectionManager.getApiClient(serverId);
return new Promise(function (resolve, reject) {
switch (id) {
case 'addtocollection':
require(['collectionEditor'], function (collectionEditor) {
new collectionEditor().show({
import('collectionEditor').then(({default: collectionEditor}) => {
new collectionEditor({
items: [itemId],
serverId: serverId
}).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
});
break;
case 'addtoplaylist':
require(['playlistEditor'], function (playlistEditor) {
new playlistEditor().show({
import('playlistEditor').then(({default: playlistEditor}) => {
new playlistEditor({
items: [itemId],
serverId: serverId
}).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
});
break;
case 'download':
require(['fileDownloader'], function (fileDownloader) {
var downloadHref = apiClient.getItemDownloadUrl(itemId);
import('fileDownloader').then(({default: fileDownloader}) => {
const downloadHref = apiClient.getItemDownloadUrl(itemId);
fileDownloader.download([{
url: downloadHref,
itemId: itemId,
@ -346,17 +356,17 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
getResolveFunction(getResolveFunction(resolve, id), id)();
});
break;
case 'copy-stream':
var downloadHref = apiClient.getItemDownloadUrl(itemId);
var textAreaCopy = function () {
var textArea = document.createElement('textarea');
case 'copy-stream': {
const downloadHref = apiClient.getItemDownloadUrl(itemId);
const textAreaCopy = function () {
let textArea = document.createElement('textarea');
textArea.value = downloadHref;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
if (document.execCommand('copy')) {
require(['toast'], function (toast) {
import('toast').then(({default: toast}) => {
toast(globalize.translate('CopyStreamURLSuccess'));
});
} else {
@ -371,7 +381,7 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
} else {
/* eslint-disable-next-line compat/compat */
navigator.clipboard.writeText(downloadHref).then(function () {
require(['toast'], function (toast) {
import('toast').then(({default: toast}) => {
toast(globalize.translate('CopyStreamURLSuccess'));
});
}).catch(function () {
@ -380,8 +390,9 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
}
getResolveFunction(resolve, id)();
break;
}
case 'editsubtitles':
require(['subtitleEditor'], function (subtitleEditor) {
import('subtitleEditor').then(({default: subtitleEditor}) => {
subtitleEditor.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
});
break;
@ -389,7 +400,7 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
editItem(apiClient, item).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
break;
case 'editimages':
require(['imageEditor'], function (imageEditor) {
import('imageEditor').then(({default: imageEditor}) => {
imageEditor.show({
itemId: itemId,
serverId: serverId
@ -397,13 +408,13 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
});
break;
case 'identify':
require(['itemIdentifier'], function (itemIdentifier) {
import('itemIdentifier').then(({default: itemIdentifier}) => {
itemIdentifier.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
});
break;
case 'moremediainfo':
require(['itemMediaInfo'], function (itemMediaInfo) {
itemMediaInfo.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
import('itemMediaInfo').then(({default: itemMediaInfo}) => {
itemMediaInfo.show(itemId, serverId).then(getResolveFunction(resolve, id), getResolveFunction(resolve, id));
});
break;
case 'refresh':
@ -431,7 +442,7 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
getResolveFunction(resolve, id)();
break;
case 'record':
require(['recordingCreator'], function (recordingCreator) {
import('recordingCreator').then(({default: recordingCreator}) => {
recordingCreator.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
});
break;
@ -502,8 +513,8 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
}
function deleteTimer(apiClient, item, resolve, command) {
require(['recordingHelper'], function (recordingHelper) {
var timerId = item.TimerId || item.Id;
import('recordingHelper').then(({default: recordingHelper}) => {
const timerId = item.TimerId || item.Id;
recordingHelper.cancelTimerWithConfirmation(timerId, item.ServerId).then(function () {
getResolveFunction(resolve, command, true)();
});
@ -511,7 +522,7 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
}
function deleteSeriesTimer(apiClient, item, resolve, command) {
require(['recordingHelper'], function (recordingHelper) {
import('recordingHelper').then(({default: recordingHelper}) => {
recordingHelper.cancelSeriesTimerWithConfirmation(item.Id, item.ServerId).then(function () {
getResolveFunction(resolve, command, true)();
});
@ -519,9 +530,9 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
}
function play(item, resume, queue, queueNext) {
var method = queue ? (queueNext ? 'queueNext' : 'queue') : 'play';
const method = queue ? (queueNext ? 'queueNext' : 'queue') : 'play';
var startPosition = 0;
let startPosition = 0;
if (resume && item.UserData && item.UserData.PlaybackPositionTicks) {
startPosition = item.UserData.PlaybackPositionTicks;
}
@ -542,18 +553,18 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
function editItem(apiClient, item) {
return new Promise(function (resolve, reject) {
var serverId = apiClient.serverInfo().Id;
const serverId = apiClient.serverInfo().Id;
if (item.Type === 'Timer') {
require(['recordingEditor'], function (recordingEditor) {
import('recordingEditor').then(({default: recordingEditor}) => {
recordingEditor.show(item.Id, serverId).then(resolve, reject);
});
} else if (item.Type === 'SeriesTimer') {
require(['seriesRecordingEditor'], function (recordingEditor) {
import('seriesRecordingEditor').then(({default: recordingEditor}) => {
recordingEditor.show(item.Id, serverId).then(resolve, reject);
});
} else {
require(['metadataEditor'], function (metadataEditor) {
import('metadataEditor').then(({default: metadataEditor}) => {
metadataEditor.show(item.Id, serverId).then(resolve, reject);
});
}
@ -562,7 +573,7 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
function deleteItem(apiClient, item) {
return new Promise(function (resolve, reject) {
require(['deleteHelper'], function (deleteHelper) {
import('deleteHelper').then(({default: deleteHelper}) => {
deleteHelper.deleteItem({
item: item,
navigate: false
@ -574,7 +585,7 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
}
function refresh(apiClient, item) {
require(['refreshDialog'], function (refreshDialog) {
import('refreshDialog').then(({default: refreshDialog}) => {
new refreshDialog({
itemIds: [item.Id],
serverId: apiClient.serverInfo().Id,
@ -583,8 +594,8 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
});
}
function show(options) {
var commands = getCommands(options);
export function show(options) {
const commands = getCommands(options);
if (!commands.length) {
return Promise.reject();
}
@ -598,8 +609,9 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
});
}
return {
getCommands: getCommands,
show: show
};
});
/* eslint-enable indent */
export default {
getCommands: getCommands,
show: show
};

View file

@ -1,44 +1,61 @@
define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings', 'connectionManager', 'loading', 'focusManager', 'dom', 'apphost', 'emby-select', 'listViewStyle', 'paper-icon-button-light', 'css!./../formdialog', 'material-icons', 'emby-button', 'flexStyles'], function (dialogHelper, require, layoutManager, globalize, userSettings, connectionManager, loading, focusManager, dom, appHost) {
'use strict';
/* eslint-disable indent */
/**
* Module for display media info.
* @module components/itemMediaInfo/itemMediaInfo
*/
import dialogHelper from 'dialogHelper';
import layoutManager from 'layoutManager';
import globalize from 'globalize';
import connectionManager from 'connectionManager';
import loading from 'loading';
import 'emby-select';
import 'listViewStyle';
import 'paper-icon-button-light';
import 'css!./../formdialog';
import 'material-icons';
import 'emby-button';
import 'flexStyles';
function setMediaInfo(user, page, item) {
var html = item.MediaSources.map(function (version) {
let html = item.MediaSources.map(version => {
return getMediaSourceHtml(user, item, version);
}).join('<div style="border-top:1px solid #444;margin: 1em 0;"></div>');
if (item.MediaSources.length > 1) {
html = '<br/>' + html;
html = `<br/>${html}`;
}
var mediaInfoContent = page.querySelector('#mediaInfoContent');
const mediaInfoContent = page.querySelector('#mediaInfoContent');
mediaInfoContent.innerHTML = html;
}
function getMediaSourceHtml(user, item, version) {
var html = '';
let html = '';
if (version.Name) {
html += '<div><h2 class="mediaInfoStreamType">' + version.Name + '</h2></div>';
html += `<div><h2 class="mediaInfoStreamType">${version.Name}</h2></div>`;
}
if (version.Container) {
html += createAttribute(globalize.translate('MediaInfoContainer'), version.Container) + '<br/>';
html += `${createAttribute(globalize.translate('MediaInfoContainer'), version.Container)}<br/>`;
}
if (version.Formats && version.Formats.length) {
html += createAttribute(globalize.translate('MediaInfoFormat'), version.Formats.join(',')) + '<br/>';
html += `${createAttribute(globalize.translate('MediaInfoFormat'), version.Formats.join(','))}<br/>`;
}
if (version.Path && user && user.Policy.IsAdministrator) {
html += createAttribute(globalize.translate('MediaInfoPath'), version.Path) + '<br/>';
html += `${createAttribute(globalize.translate('MediaInfoPath'), version.Path)}<br/>`;
}
if (version.Size) {
var size = (version.Size / (1024 * 1024)).toFixed(0) + ' MB';
html += createAttribute(globalize.translate('MediaInfoSize'), size) + '<br/>';
const size = `${(version.Size / (1024 * 1024)).toFixed(0)} MB`;
html += `${createAttribute(globalize.translate('MediaInfoSize'), size)}<br/>`;
}
for (var i = 0, length = version.MediaStreams.length; i < length; i++) {
var stream = version.MediaStreams[i];
for (let i = 0, length = version.MediaStreams.length; i < length; i++) {
const stream = version.MediaStreams[i];
if (stream.Type === 'Data') {
continue;
}
html += '<div class="mediaInfoStream">';
var displayType = globalize.translate('MediaInfoStreamType' + stream.Type);
html += '<h2 class="mediaInfoStreamType">' + displayType + '</h2>';
var attributes = [];
const displayType = globalize.translate(`MediaInfoStreamType${stream.Type}`);
html += `<h2 class="mediaInfoStreamType">${displayType}</h2>`;
const attributes = [];
if (stream.DisplayTitle) {
attributes.push(createAttribute('Title', stream.DisplayTitle));
}
@ -61,7 +78,7 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
attributes.push(createAttribute(globalize.translate('MediaInfoLevel'), stream.Level));
}
if (stream.Width || stream.Height) {
attributes.push(createAttribute(globalize.translate('MediaInfoResolution'), stream.Width + 'x' + stream.Height));
attributes.push(createAttribute(globalize.translate('MediaInfoResolution'), `${stream.Width}x${stream.Height}`));
}
if (stream.AspectRatio && stream.Codec !== 'mjpeg') {
attributes.push(createAttribute(globalize.translate('MediaInfoAspectRatio'), stream.AspectRatio));
@ -79,16 +96,16 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
attributes.push(createAttribute(globalize.translate('MediaInfoLayout'), stream.ChannelLayout));
}
if (stream.Channels) {
attributes.push(createAttribute(globalize.translate('MediaInfoChannels'), stream.Channels + ' ch'));
attributes.push(createAttribute(globalize.translate('MediaInfoChannels'), `${stream.Channels} ch`));
}
if (stream.BitRate && stream.Codec !== 'mjpeg') {
attributes.push(createAttribute(globalize.translate('MediaInfoBitrate'), (parseInt(stream.BitRate / 1000)) + ' kbps'));
attributes.push(createAttribute(globalize.translate('MediaInfoBitrate'), `${parseInt(stream.BitRate / 1000)} kbps`));
}
if (stream.SampleRate) {
attributes.push(createAttribute(globalize.translate('MediaInfoSampleRate'), stream.SampleRate + ' Hz'));
attributes.push(createAttribute(globalize.translate('MediaInfoSampleRate'), `${stream.SampleRate} Hz`));
}
if (stream.BitDepth) {
attributes.push(createAttribute(globalize.translate('MediaInfoBitDepth'), stream.BitDepth + ' bit'));
attributes.push(createAttribute(globalize.translate('MediaInfoBitDepth'), `${stream.BitDepth} bit`));
}
if (stream.PixelFormat) {
attributes.push(createAttribute(globalize.translate('MediaInfoPixelFormat'), stream.PixelFormat));
@ -116,13 +133,13 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
}
function createAttribute(label, value) {
return '<span class="mediaInfoLabel">' + label + '</span><span class="mediaInfoAttribute">' + value + '</span>';
return `<span class="mediaInfoLabel">${label}</span><span class="mediaInfoAttribute">${value}</span>`;
}
function showMediaInfoMore(itemId, serverId, template) {
var apiClient = connectionManager.getApiClient(serverId);
return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) {
var dialogOptions = {
function loadMediaInfo(itemId, serverId, template) {
const apiClient = connectionManager.getApiClient(serverId);
return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(item => {
const dialogOptions = {
size: 'small',
removeOnClose: true,
scrollY: false
@ -130,35 +147,35 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
if (layoutManager.tv) {
dialogOptions.size = 'fullscreen';
}
var dlg = dialogHelper.createDialog(dialogOptions);
const dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog');
var html = '';
let html = '';
html += globalize.translateDocument(template, 'core');
dlg.innerHTML = html;
if (layoutManager.tv) {
dlg.querySelector('.formDialogContent');
}
dialogHelper.open(dlg);
dlg.querySelector('.btnCancel').addEventListener('click', function (e) {
dlg.querySelector('.btnCancel').addEventListener('click', () => {
dialogHelper.close(dlg);
});
apiClient.getCurrentUser().then(function (user) {
apiClient.getCurrentUser().then(user => {
setMediaInfo(user, dlg, item);
});
loading.hide();
});
}
function showMediaInfo(itemId, serverId) {
export function show(itemId, serverId) {
loading.show();
return new Promise(function (resolve, reject) {
require(['text!./itemMediaInfo.template.html'], function (template) {
showMediaInfoMore(itemId, serverId, template).then(resolve, reject);
return import('text!./itemMediaInfo.template.html').then(({default: template}) => {
return new Promise((resolve, reject) => {
loadMediaInfo(itemId, serverId, template).then(resolve, reject);
});
});
}
return {
show: showMediaInfo
};
});
/* eslint-enable indent */
export default {
show: show
};

View file

@ -1,15 +1,34 @@
define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize', 'scrollHelper', 'layoutManager', 'focusManager', 'browser', 'emby-input', 'emby-checkbox', 'paper-icon-button-light', 'css!./../formdialog', 'material-icons', 'cardStyle'], function (dialogHelper, loading, connectionManager, require, globalize, scrollHelper, layoutManager, focusManager, browser) {
'use strict';
/* eslint-disable indent */
var enableFocusTransform = !browser.slow && !browser.edge;
/**
* Module for itemidentifier media item.
* @module components/itemidentifier/itemidentifier
*/
var currentItem;
var currentItemType;
var currentServerId;
var currentResolve;
var currentReject;
var hasChanges = false;
var currentSearchResult;
import dialogHelper from 'dialogHelper';
import loading from 'loading';
import connectionManager from 'connectionManager';
import globalize from 'globalize';
import scrollHelper from 'scrollHelper';
import layoutManager from 'layoutManager';
import focusManager from 'focusManager';
import browser from 'browser';
import 'emby-input';
import 'emby-checkbox';
import 'paper-icon-button-light';
import 'css!./../formdialog';
import 'material-icons';
import 'cardStyle';
const enableFocusTransform = !browser.slow && !browser.edge;
let currentItem;
let currentItemType;
let currentServerId;
let currentResolve;
let currentReject;
let hasChanges = false;
let currentSearchResult;
function getApiClient() {
return connectionManager.getApiClient(currentServerId);
@ -17,14 +36,14 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
function searchForIdentificationResults(page) {
var lookupInfo = {
let lookupInfo = {
ProviderIds: {}
};
var i;
var length;
var identifyField = page.querySelectorAll('.identifyField');
var value;
let i;
let length;
const identifyField = page.querySelectorAll('.identifyField');
let value;
for (i = 0, length = identifyField.length; i < length; i++) {
value = identifyField[i].value;
@ -39,9 +58,9 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
}
}
var hasId = false;
let hasId = false;
var txtLookupId = page.querySelectorAll('.txtLookupId');
const txtLookupId = page.querySelectorAll('.txtLookupId');
for (i = 0, length = txtLookupId.length; i < length; i++) {
value = txtLookupId[i].value;
@ -53,7 +72,7 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
}
if (!hasId && !lookupInfo.Name) {
require(['toast'], function (toast) {
import('toast').then(({default: toast}) => {
toast(globalize.translate('PleaseEnterNameOrId'));
});
return;
@ -71,16 +90,16 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
loading.show();
var apiClient = getApiClient();
const apiClient = getApiClient();
apiClient.ajax({
type: 'POST',
url: apiClient.getUrl('Items/RemoteSearch/' + currentItemType),
url: apiClient.getUrl(`Items/RemoteSearch/${currentItemType}`),
data: JSON.stringify(lookupInfo),
contentType: 'application/json',
dataType: 'json'
}).then(function (results) {
}).then(results => {
loading.hide();
showIdentificationSearchResults(page, results);
@ -89,29 +108,29 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
function showIdentificationSearchResults(page, results) {
var identificationSearchResults = page.querySelector('.identificationSearchResults');
const identificationSearchResults = page.querySelector('.identificationSearchResults');
page.querySelector('.popupIdentifyForm').classList.add('hide');
identificationSearchResults.classList.remove('hide');
page.querySelector('.identifyOptionsForm').classList.add('hide');
page.querySelector('.dialogContentInner').classList.remove('dialog-content-centered');
var html = '';
var i;
var length;
let html = '';
let i;
let length;
for (i = 0, length = results.length; i < length; i++) {
var result = results[i];
const result = results[i];
html += getSearchResultHtml(result, i);
}
var elem = page.querySelector('.identificationSearchResultList');
const elem = page.querySelector('.identificationSearchResultList');
elem.innerHTML = html;
function onSearchImageClick() {
var index = parseInt(this.getAttribute('data-index'));
const index = parseInt(this.getAttribute('data-index'));
var currentResult = results[index];
const currentResult = results[index];
if (currentItem != null) {
@ -122,7 +141,7 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
}
}
var searchImages = elem.querySelectorAll('.card');
const searchImages = elem.querySelectorAll('.card');
for (i = 0, length = searchImages.length; i < length; i++) {
searchImages[i].addEventListener('click', onSearchImageClick);
@ -143,7 +162,7 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
function showIdentifyOptions(page, identifyResult) {
var identifyOptionsForm = page.querySelector('.identifyOptionsForm');
const identifyOptionsForm = page.querySelector('.identifyOptionsForm');
page.querySelector('.popupIdentifyForm').classList.add('hide');
page.querySelector('.identificationSearchResults').classList.add('hide');
@ -153,19 +172,19 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
currentSearchResult = identifyResult;
var lines = [];
const lines = [];
lines.push(identifyResult.Name);
if (identifyResult.ProductionYear) {
lines.push(identifyResult.ProductionYear);
}
var resultHtml = lines.join('<br/>');
let resultHtml = lines.join('<br/>');
if (identifyResult.ImageUrl) {
var displayUrl = getSearchImageDisplayUrl(identifyResult.ImageUrl, identifyResult.SearchProviderName);
const displayUrl = getSearchImageDisplayUrl(identifyResult.ImageUrl, identifyResult.SearchProviderName);
resultHtml = '<div style="display:flex;align-items:center;"><img src="' + displayUrl + '" style="max-height:240px;" /><div style="margin-left:1em;">' + resultHtml + '</div>';
resultHtml = `<div style="display:flex;align-items:center;"><img src="${displayUrl}" style="max-height:240px;" /><div style="margin-left:1em;">${resultHtml}</div>`;
}
page.querySelector('.selectedSearchResult').innerHTML = resultHtml;
@ -177,10 +196,10 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
// TODO move card creation code to Card component
var html = '';
var cssClass = 'card scalableCard';
var cardBoxCssClass = 'cardBox';
var padderClass;
let html = '';
let cssClass = 'card scalableCard';
let cardBoxCssClass = 'cardBox';
let padderClass;
if (currentItemType === 'Episode') {
cssClass += ' backdropCard backdropCard-scalable';
@ -203,30 +222,30 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
cardBoxCssClass += ' cardBox-bottompadded';
html += '<button type="button" class="' + cssClass + '" data-index="' + index + '">';
html += '<div class="' + cardBoxCssClass + '">';
html += `<button type="button" class="${cssClass}" data-index="${index}">`;
html += `<div class="${cardBoxCssClass}">`;
html += '<div class="cardScalable">';
html += '<div class="' + padderClass + '"></div>';
html += `<div class="${padderClass}"></div>`;
html += '<div class="cardContent searchImage">';
if (result.ImageUrl) {
var displayUrl = getSearchImageDisplayUrl(result.ImageUrl, result.SearchProviderName);
const displayUrl = getSearchImageDisplayUrl(result.ImageUrl, result.SearchProviderName);
html += '<div class="cardImageContainer coveredImage" style="background-image:url(\'' + displayUrl + '\');"></div>';
html += `<div class="cardImageContainer coveredImage" style="background-image:url('${displayUrl}');"></div>`;
} else {
html += '<div class="cardImageContainer coveredImage defaultCardBackground defaultCardBackground1"><div class="cardText cardCenteredText">' + result.Name + '</div></div>';
html += `<div class="cardImageContainer coveredImage defaultCardBackground defaultCardBackground1"><div class="cardText cardCenteredText">${result.Name}</div></div>`;
}
html += '</div>';
html += '</div>';
var numLines = 2;
let numLines = 2;
if (currentItemType === 'MusicAlbum') {
numLines++;
}
var lines = [result.Name];
const lines = [result.Name];
if (result.AlbumArtist) {
lines.push(result.AlbumArtist.Name);
@ -235,7 +254,7 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
lines.push(result.ProductionYear);
}
for (var i = 0; i < numLines; i++) {
for (let i = 0; i < numLines; i++) {
if (i === 0) {
html += '<div class="cardText cardText-first cardTextCentered">';
@ -252,7 +271,7 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
}
function getSearchImageDisplayUrl(url, provider) {
var apiClient = getApiClient();
const apiClient = getApiClient();
return apiClient.getUrl('Items/RemoteSearch/Image', { imageUrl: url, ProviderName: provider });
}
@ -261,26 +280,26 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
loading.show();
var options = {
const options = {
ReplaceAllImages: page.querySelector('#chkIdentifyReplaceImages').checked
};
var apiClient = getApiClient();
const apiClient = getApiClient();
apiClient.ajax({
type: 'POST',
url: apiClient.getUrl('Items/RemoteSearch/Apply/' + currentItem.Id, options),
url: apiClient.getUrl(`Items/RemoteSearch/Apply/${currentItem.Id}`, options),
data: JSON.stringify(currentSearchResult),
contentType: 'application/json'
}).then(function () {
}).then(() => {
hasChanges = true;
loading.hide();
dialogHelper.close(page);
}, function () {
}, () => {
loading.hide();
@ -290,28 +309,28 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
function showIdentificationForm(page, item) {
var apiClient = getApiClient();
const apiClient = getApiClient();
apiClient.getJSON(apiClient.getUrl('Items/' + item.Id + '/ExternalIdInfos')).then(function (idList) {
apiClient.getJSON(apiClient.getUrl(`Items/${item.Id}/ExternalIdInfos`)).then(idList => {
var html = '';
let html = '';
for (var i = 0, length = idList.length; i < length; i++) {
for (let i = 0, length = idList.length; i < length; i++) {
var idInfo = idList[i];
const idInfo = idList[i];
var id = 'txtLookup' + idInfo.Key;
const id = `txtLookup${idInfo.Key}`;
html += '<div class="inputContainer">';
var fullName = idInfo.Name;
let fullName = idInfo.Name;
if (idInfo.Type) {
fullName = idInfo.Name + ' ' + globalize.translate(idInfo.Type);
fullName = `${idInfo.Name} ${globalize.translate(idInfo.Type)}`;
}
var idLabel = globalize.translate('LabelDynamicExternalId', fullName);
const idLabel = globalize.translate('LabelDynamicExternalId', fullName);
html += '<input is="emby-input" class="txtLookupId" data-providerkey="' + idInfo.Key + '" id="' + id + '" label="' + idLabel + '"/>';
html += `<input is="emby-input" class="txtLookupId" data-providerkey="${idInfo.Key}" id="${id}" label="${idLabel}"/>`;
html += '</div>';
}
@ -338,16 +357,16 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
loading.show();
require(['text!./itemidentifier.template.html'], function (template) {
return import('text!./itemidentifier.template.html').then(({default: template}) => {
var apiClient = getApiClient();
const apiClient = getApiClient();
apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) {
apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(item => {
currentItem = item;
currentItemType = currentItem.Type;
var dialogOptions = {
const dialogOptions = {
size: 'small',
removeOnClose: true,
scrollY: false
@ -357,12 +376,12 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
dialogOptions.size = 'fullscreen';
}
var dlg = dialogHelper.createDialog(dialogOptions);
const dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog');
dlg.classList.add('recordingDialog');
var html = '';
let html = '';
html += globalize.translateDocument(template, 'core');
dlg.innerHTML = html;
@ -384,21 +403,21 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
dialogHelper.open(dlg);
dlg.querySelector('.popupIdentifyForm').addEventListener('submit', function (e) {
dlg.querySelector('.popupIdentifyForm').addEventListener('submit', e => {
e.preventDefault();
searchForIdentificationResults(dlg);
return false;
});
dlg.querySelector('.identifyOptionsForm').addEventListener('submit', function (e) {
dlg.querySelector('.identifyOptionsForm').addEventListener('submit', e => {
e.preventDefault();
submitIdentficationResult(dlg);
return false;
});
dlg.querySelector('.btnCancel').addEventListener('click', function (e) {
dlg.querySelector('.btnCancel').addEventListener('click', () => {
dialogHelper.close(dlg);
});
@ -421,14 +440,15 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
}
}
// TODO investigate where this was used
function showEditorFindNew(itemName, itemYear, itemType, resolveFunc) {
currentItem = null;
currentItemType = itemType;
require(['text!./itemidentifier.template.html'], function (template) {
return import('text!./itemidentifier.template.html').then(({default: template}) => {
var dialogOptions = {
const dialogOptions = {
size: 'small',
removeOnClose: true,
scrollY: false
@ -438,12 +458,12 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
dialogOptions.size = 'fullscreen';
}
var dlg = dialogHelper.createDialog(dialogOptions);
const dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog');
dlg.classList.add('recordingDialog');
var html = '';
let html = '';
html += globalize.translateDocument(template, 'core');
dlg.innerHTML = html;
@ -454,22 +474,22 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
dialogHelper.open(dlg);
dlg.querySelector('.btnCancel').addEventListener('click', function (e) {
dlg.querySelector('.btnCancel').addEventListener('click', () => {
dialogHelper.close(dlg);
});
dlg.querySelector('.popupIdentifyForm').addEventListener('submit', function (e) {
dlg.querySelector('.popupIdentifyForm').addEventListener('submit', e => {
e.preventDefault();
searchForIdentificationResults(dlg);
return false;
});
dlg.addEventListener('close', function () {
dlg.addEventListener('close', () => {
loading.hide();
var foundItem = hasChanges ? currentSearchResult : null;
const foundItem = hasChanges ? currentSearchResult : null;
resolveFunc(foundItem);
});
@ -498,29 +518,32 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
dlg.querySelector('.formDialogHeaderTitle').innerHTML = globalize.translate('Search');
}
return {
show: function (itemId, serverId) {
export function show(itemId, serverId) {
return new Promise(function (resolve, reject) {
return new Promise((resolve, reject) => {
currentResolve = resolve;
currentReject = reject;
currentServerId = serverId;
hasChanges = false;
currentResolve = resolve;
currentReject = reject;
currentServerId = serverId;
hasChanges = false;
showEditor(itemId);
});
},
showEditor(itemId);
});
}
showFindNew: function (itemName, itemYear, itemType, serverId) {
export function showFindNew(itemName, itemYear, itemType, serverId) {
return new Promise(function (resolve, reject) {
return new Promise((resolve) => {
currentServerId = serverId;
currentServerId = serverId;
hasChanges = false;
showEditorFindNew(itemName, itemYear, itemType, resolve);
});
}
};
});
hasChanges = false;
showEditorFindNew(itemName, itemYear, itemType, resolve);
});
}
/* eslint-enable indent */
export default {
show: show,
showFindNew: showFindNew
};

View file

@ -11,9 +11,9 @@
(entries) => {
entries.forEach(entry => {
callback(entry);
},
{rootMargin: '50%'});
});
});
},
{rootMargin: '25%'});
this.observer = observer;
}

View file

@ -1,5 +1,20 @@
define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutManager', 'globalize', 'datetime', 'apphost', 'css!./listview', 'emby-ratingbutton', 'emby-playstatebutton'], function (itemHelper, mediaInfo, indicators, connectionManager, layoutManager, globalize, datetime, appHost) {
'use strict';
/* eslint-disable indent */
/**
* Module for display list view.
* @module components/listview/listview
*/
import itemHelper from 'itemHelper';
import mediaInfo from 'mediaInfo';
import indicators from 'indicators';
import connectionManager from 'connectionManager';
import layoutManager from 'layoutManager';
import globalize from 'globalize';
import datetime from 'datetime';
import 'css!./listview';
import 'emby-ratingbutton';
import 'emby-playstatebutton';
function getIndex(item, options) {
@ -8,9 +23,9 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
return item.ParentIndexNumber == null ? '' : globalize.translate('ValueDiscNumber', item.ParentIndexNumber);
}
var sortBy = (options.sortBy || '').toLowerCase();
var code;
var name;
const sortBy = (options.sortBy || '').toLowerCase();
let code;
let name;
if (sortBy.indexOf('sortname') === 0) {
@ -69,10 +84,10 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
function getImageUrl(item, width) {
var apiClient = connectionManager.getApiClient(item.ServerId);
const apiClient = connectionManager.getApiClient(item.ServerId);
let itemId;
var options = {
const options = {
maxWidth: width * 2,
type: 'Primary'
};
@ -102,8 +117,8 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
function getChannelImageUrl(item, width) {
var apiClient = connectionManager.getApiClient(item.ServerId);
var options = {
const apiClient = connectionManager.getApiClient(item.ServerId);
const options = {
maxWidth: width * 2,
type: 'Primary'
};
@ -121,13 +136,13 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
function getTextLinesHtml(textlines, isLargeStyle) {
var html = '';
let html = '';
var largeTitleTagName = layoutManager.tv ? 'h2' : 'div';
const largeTitleTagName = layoutManager.tv ? 'h2' : 'div';
for (var i = 0, length = textlines.length; i < length; i++) {
for (let i = 0, length = textlines.length; i < length; i++) {
var text = textlines[i];
const text = textlines[i];
if (!text) {
continue;
@ -135,7 +150,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
if (i === 0) {
if (isLargeStyle) {
html += '<' + largeTitleTagName + ' class="listItemBodyText">';
html += `<${largeTitleTagName} class="listItemBodyText">`;
} else {
html += '<div class="listItemBodyText">';
}
@ -144,7 +159,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
}
html += (textlines[i] || '&nbsp;');
if (i === 0 && isLargeStyle) {
html += '</' + largeTitleTagName + '>';
html += `</${largeTitleTagName}>`;
} else {
html += '</div>';
}
@ -155,13 +170,13 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
function getRightButtonsHtml(options) {
var html = '';
let html = '';
for (var i = 0, length = options.rightButtons.length; i < length; i++) {
for (let i = 0, length = options.rightButtons.length; i < length; i++) {
var button = options.rightButtons[i];
const button = options.rightButtons[i];
html += '<button is="paper-icon-button-light" class="listItemButton itemAction" data-action="custom" data-customaction="' + button.id + '" title="' + button.title + '"><span class="material-icons ' + button.icon + '"></span></button>';
html += `<button is="paper-icon-button-light" class="listItemButton itemAction" data-action="custom" data-customaction="${button.id}" title="${button.title}"><span class="material-icons ${button.icon}"></span></button>`;
}
return html;
@ -171,34 +186,34 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
return item.Id;
}
function getListViewHtml(options) {
export function getListViewHtml(options) {
var items = options.items;
const items = options.items;
var groupTitle = '';
var action = options.action || 'link';
let groupTitle = '';
const action = options.action || 'link';
var isLargeStyle = options.imageSize === 'large';
var enableOverview = options.enableOverview;
const isLargeStyle = options.imageSize === 'large';
const enableOverview = options.enableOverview;
var clickEntireItem = layoutManager.tv ? true : false;
var outerTagName = clickEntireItem ? 'button' : 'div';
var enableSideMediaInfo = options.enableSideMediaInfo != null ? options.enableSideMediaInfo : true;
const clickEntireItem = layoutManager.tv ? true : false;
const outerTagName = clickEntireItem ? 'button' : 'div';
const enableSideMediaInfo = options.enableSideMediaInfo != null ? options.enableSideMediaInfo : true;
var outerHtml = '';
let outerHtml = '';
var enableContentWrapper = options.enableOverview && !layoutManager.tv;
var containerAlbumArtistIds = (options.containerAlbumArtists || []).map(getId);
const enableContentWrapper = options.enableOverview && !layoutManager.tv;
const containerAlbumArtistIds = (options.containerAlbumArtists || []).map(getId);
for (var i = 0, length = items.length; i < length; i++) {
for (let i = 0, length = items.length; i < length; i++) {
var item = items[i];
const item = items[i];
var html = '';
let html = '';
if (options.showIndex) {
var itemGroupTitle = getIndex(item, options);
const itemGroupTitle = getIndex(item, options);
if (itemGroupTitle !== groupTitle) {
@ -220,7 +235,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
}
}
var cssClass = 'listItem';
let cssClass = 'listItem';
if (options.border || (options.highlight !== false && !layoutManager.tv)) {
cssClass += ' listItem-border';
@ -234,28 +249,28 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
cssClass += ' listItem-focusscale';
}
var downloadWidth = 80;
let downloadWidth = 80;
if (isLargeStyle) {
cssClass += ' listItem-largeImage';
downloadWidth = 500;
}
var playlistItemId = item.PlaylistItemId ? (' data-playlistitemid="' + item.PlaylistItemId + '"') : '';
const playlistItemId = item.PlaylistItemId ? (` data-playlistitemid="${item.PlaylistItemId}"`) : '';
var positionTicksData = item.UserData && item.UserData.PlaybackPositionTicks ? (' data-positionticks="' + item.UserData.PlaybackPositionTicks + '"') : '';
var collectionIdData = options.collectionId ? (' data-collectionid="' + options.collectionId + '"') : '';
var playlistIdData = options.playlistId ? (' data-playlistid="' + options.playlistId + '"') : '';
var mediaTypeData = item.MediaType ? (' data-mediatype="' + item.MediaType + '"') : '';
var collectionTypeData = item.CollectionType ? (' data-collectiontype="' + item.CollectionType + '"') : '';
var channelIdData = item.ChannelId ? (' data-channelid="' + item.ChannelId + '"') : '';
const positionTicksData = item.UserData && item.UserData.PlaybackPositionTicks ? (` data-positionticks="${item.UserData.PlaybackPositionTicks}"`) : '';
const collectionIdData = options.collectionId ? (` data-collectionid="${options.collectionId}"`) : '';
const playlistIdData = options.playlistId ? (` data-playlistid="${options.playlistId}"`) : '';
const mediaTypeData = item.MediaType ? (` data-mediatype="${item.MediaType}"`) : '';
const collectionTypeData = item.CollectionType ? (` data-collectiontype="${item.CollectionType}"`) : '';
const channelIdData = item.ChannelId ? (` data-channelid="${item.ChannelId}"`) : '';
if (enableContentWrapper) {
cssClass += ' listItem-withContentWrapper';
}
html += '<' + outerTagName + ' class="' + cssClass + '"' + playlistItemId + ' data-action="' + action + '" data-isfolder="' + item.IsFolder + '" data-id="' + item.Id + '" data-serverid="' + item.ServerId + '" data-type="' + item.Type + '"' + mediaTypeData + collectionTypeData + channelIdData + positionTicksData + collectionIdData + playlistIdData + '>';
html += `<${outerTagName} class="${cssClass}"${playlistItemId} data-action="${action}" data-isfolder="${item.IsFolder}" data-id="${item.Id}" data-serverid="${item.ServerId}" data-type="${item.Type}"${mediaTypeData}${collectionTypeData}${channelIdData}${positionTicksData}${collectionIdData}${playlistIdData}>`;
if (enableContentWrapper) {
@ -278,37 +293,37 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
imageClass += ' listItemImage-large-tv';
}
var playOnImageClick = options.imagePlayButton && !layoutManager.tv;
const playOnImageClick = options.imagePlayButton && !layoutManager.tv;
if (!clickEntireItem) {
imageClass += ' itemAction';
}
var imageAction = playOnImageClick ? 'resume' : action;
const imageAction = playOnImageClick ? 'resume' : action;
let blurhashAttrib = '';
if (blurhash && blurhash.length > 0) {
blurhashAttrib = 'data-blurhash="' + blurhash + '"';
blurhashAttrib = `data-blurhash="${blurhash}"`;
}
if (imgUrl) {
html += '<div data-action="' + imageAction + '" class="' + imageClass + ' lazy" data-src="' + imgUrl + '" ' + blurhashAttrib + ' item-icon>';
html += `<div data-action="${imageAction}" class="${imageClass} lazy" data-src="${imgUrl}" ${blurhashAttrib} item-icon>`;
} else {
html += '<div class="' + imageClass + '">';
html += `<div class="${imageClass}">`;
}
var indicatorsHtml = '';
let indicatorsHtml = '';
indicatorsHtml += indicators.getPlayedIndicatorHtml(item);
if (indicatorsHtml) {
html += '<div class="indicators listItemIndicators">' + indicatorsHtml + '</div>';
html += `<div class="indicators listItemIndicators">${indicatorsHtml}</div>`;
}
if (playOnImageClick) {
html += '<button is="paper-icon-button-light" class="listItemImageButton itemAction" data-action="resume"><span class="material-icons listItemImageButton-icon play_arrow"></span></button>';
}
var progressHtml = indicators.getProgressBarHtml(item, {
const progressHtml = indicators.getProgressBarHtml(item, {
containerClass: 'listItemProgressBar'
});
@ -325,7 +340,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
html += '</div>';
}
var textlines = [];
const textlines = [];
if (options.showProgramDateTime) {
textlines.push(datetime.toLocaleString(datetime.parseISO8601Date(item.StartDate), {
@ -348,7 +363,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
}
}
var parentTitle = null;
let parentTitle = null;
if (options.showParentTitle) {
if (item.Type === 'Episode') {
@ -358,12 +373,12 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
}
}
var displayName = itemHelper.getDisplayName(item, {
let displayName = itemHelper.getDisplayName(item, {
includeParentInfo: options.includeParentInfoInTitle
});
if (options.showIndexNumber && item.IndexNumber != null) {
displayName = item.IndexNumber + '. ' + displayName;
displayName = `${item.IndexNumber}. ${displayName}`;
}
if (options.showParentTitle && options.parentTitleWithTitle) {
@ -394,14 +409,14 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
}
} else {
var showArtist = options.artist === true;
var artistItems = item.ArtistItems;
let showArtist = options.artist === true;
const artistItems = item.ArtistItems;
if (!showArtist && options.artist !== false) {
if (!artistItems || !artistItems.length) {
showArtist = true;
} else if (artistItems.length > 1 || containerAlbumArtistIds.indexOf(artistItems[0].Id) === -1) {
} else if (artistItems.length > 1 || !containerAlbumArtistIds.includes(artistItems[0].Id)) {
showArtist = true;
}
}
@ -409,7 +424,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
if (showArtist) {
if (artistItems && item.Type !== 'MusicAlbum') {
textlines.push(artistItems.map(function (a) {
textlines.push(artistItems.map(a => {
return a.Name;
}).join(', '));
}
@ -432,7 +447,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
cssClass += ' listItemBody-noleftpadding';
}
html += '<div class="' + cssClass + '">';
html += `<div class="${cssClass}">`;
const moreIcon = 'more_vert';
@ -441,14 +456,16 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
if (options.mediaInfo !== false) {
if (!enableSideMediaInfo) {
var mediaInfoClass = 'secondary listItemMediaInfo listItemBodyText';
const mediaInfoClass = 'secondary listItemMediaInfo listItemBodyText';
html += '<div class="' + mediaInfoClass + '">' + mediaInfo.getPrimaryMediaInfoHtml(item, {
html += `<div class="${mediaInfoClass}">`;
html += mediaInfo.getPrimaryMediaInfoHtml(item, {
episodeTitle: false,
originalAirDate: false,
subtitles: false
}) + '</div>';
});
html += '</div>';
}
}
@ -462,7 +479,8 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
if (options.mediaInfo !== false) {
if (enableSideMediaInfo) {
html += '<div class="secondary listItemMediaInfo">' + mediaInfo.getPrimaryMediaInfoHtml(item, {
html += '<div class="secondary listItemMediaInfo">';
html += mediaInfo.getPrimaryMediaInfoHtml(item, {
year: false,
container: false,
@ -470,7 +488,8 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
criticRating: false,
endsAt: false
}) + '</div>';
});
html += '</div>';
}
}
@ -487,7 +506,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
}
if (options.moreButton !== false) {
html += '<button is="paper-icon-button-light" class="listItemButton itemAction" data-action="menu"><span class="material-icons ' + moreIcon + '"></span></button>';
html += `<button is="paper-icon-button-light" class="listItemButton itemAction" data-action="menu"><span class="material-icons ${moreIcon}"></span></button>`;
}
if (options.infoButton) {
@ -500,15 +519,15 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
if (options.enableUserDataButtons !== false) {
var userData = item.UserData || {};
var likes = userData.Likes == null ? '' : userData.Likes;
const userData = item.UserData || {};
const likes = userData.Likes == null ? '' : userData.Likes;
if (itemHelper.canMarkPlayed(item)) {
html += '<button is="emby-playstatebutton" type="button" class="listItemButton paper-icon-button-light" data-id="' + item.Id + '" data-serverid="' + item.ServerId + '" data-itemtype="' + item.Type + '" data-played="' + (userData.Played) + '"><span class="material-icons check"></span></button>';
html += `<button is="emby-playstatebutton" type="button" class="listItemButton paper-icon-button-light" data-id="${item.Id}" data-serverid="${item.ServerId}" data-itemtype="${item.Type}" data-played="${userData.Played}"><span class="material-icons check"></span></button>`;
}
if (itemHelper.canRate(item)) {
html += '<button is="emby-ratingbutton" type="button" class="listItemButton paper-icon-button-light" data-id="' + item.Id + '" data-serverid="' + item.ServerId + '" data-itemtype="' + item.Type + '" data-likes="' + likes + '" data-isfavorite="' + (userData.IsFavorite) + '"><span class="material-icons favorite"></span></button>';
html += `<button is="emby-ratingbutton" type="button" class="listItemButton paper-icon-button-light" data-id="${item.Id}" data-serverid="${item.ServerId}" data-itemtype="${item.Type}" data-likes="${likes}" data-isfavorite="${userData.IsFavorite}"><span class="material-icons favorite"></span></button>`;
}
}
}
@ -524,7 +543,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
}
}
html += '</' + outerTagName + '>';
html += `</${outerTagName}>`;
outerHtml += html;
}
@ -532,7 +551,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
return outerHtml;
}
return {
getListViewHtml: getListViewHtml
};
});
/* eslint-enable indent */
export default {
getListViewHtml: getListViewHtml
};

View file

@ -1,92 +0,0 @@
define(['loading', 'events', 'dialogHelper', 'dom', 'layoutManager', 'scrollHelper', 'globalize', 'require', 'material-icons', 'emby-button', 'paper-icon-button-light', 'emby-input', 'formDialogStyle', 'flexStyles'], function (loading, events, dialogHelper, dom, layoutManager, scrollHelper, globalize, require) {
'use strict';
function showDialog(instance, options, template) {
var dialogOptions = {
removeOnClose: true,
scrollY: false
};
var enableTvLayout = layoutManager.tv;
if (enableTvLayout) {
dialogOptions.size = 'fullscreen';
}
var dlg = dialogHelper.createDialog(dialogOptions);
var configuredButtons = [];
dlg.classList.add('formDialog');
dlg.innerHTML = globalize.translateHtml(template, 'core');
dlg.classList.add('align-items-center');
dlg.classList.add('justify-items-center');
var formDialogContent = dlg.querySelector('.formDialogContent');
formDialogContent.style['flex-grow'] = 'initial';
formDialogContent.style['max-width'] = '50%';
formDialogContent.style['max-height'] = '60%';
if (enableTvLayout) {
scrollHelper.centerFocus.on(formDialogContent, false);
dlg.querySelector('.formDialogHeader').style.marginTop = '15%';
} else {
dlg.classList.add('dialog-fullscreen-lowres');
}
//dlg.querySelector('.btnCancel').addEventListener('click', function (e) {
// dialogHelper.close(dlg);
//});
dlg.querySelector('.formDialogHeaderTitle').innerHTML = options.title;
dlg.querySelector('.text').innerHTML = options.text;
instance.dlg = dlg;
return dialogHelper.open(dlg).then(function () {
if (enableTvLayout) {
scrollHelper.centerFocus.off(dlg.querySelector('.formDialogContent'), false);
}
loading.hide();
});
}
function LoadingDialog(options) {
this.options = options;
}
LoadingDialog.prototype.show = function () {
var instance = this;
loading.show();
return new Promise(function (resolve, reject) {
require(['text!./../dialog/dialog.template.html'], function (template) {
showDialog(instance, instance.options, template);
resolve();
});
});
};
LoadingDialog.prototype.setTitle = function (title) {
};
LoadingDialog.prototype.setText = function (text) {
};
LoadingDialog.prototype.hide = function () {
if (this.dlg) {
dialogHelper.close(this.dlg);
this.dlg = null;
}
};
LoadingDialog.prototype.destroy = function () {
this.dlg = null;
this.options = null;
};
return LoadingDialog;
});

View file

@ -1,11 +1,16 @@
define(['dom', 'browser', 'events', 'emby-tabs', 'emby-button'], function (dom, browser, events) {
'use strict';
import dom from 'dom';
import browser from 'browser';
import events from 'events';
import 'emby-tabs';
import 'emby-button';
var tabOwnerView;
var queryScope = document.querySelector('.skinHeader');
var footerTabsContainer;
var headerTabsContainer;
var tabsElem;
/* eslint-disable indent */
let tabOwnerView;
const queryScope = document.querySelector('.skinHeader');
let footerTabsContainer;
let headerTabsContainer;
let tabsElem;
function enableTabsInFooter() {
return false;
@ -44,7 +49,7 @@ define(['dom', 'browser', 'events', 'emby-tabs', 'emby-button'], function (dom,
return false;
}
var classList = elem.classList;
const classList = elem.classList;
if (classList) {
return !classList.contains('scrollX') && !classList.contains('animatedScrollX');
}
@ -52,7 +57,7 @@ define(['dom', 'browser', 'events', 'emby-tabs', 'emby-button'], function (dom,
return true;
}
var parent = target;
let parent = target;
while (parent != null) {
if (!allowSwipeOn(parent)) {
return false;
@ -70,22 +75,22 @@ define(['dom', 'browser', 'events', 'emby-tabs', 'emby-button'], function (dom,
}
// implement without hammer
var pageCount = getTabContainersFn().length;
var onSwipeLeft = function (e, target) {
const pageCount = getTabContainersFn().length;
const onSwipeLeft = function (e, target) {
if (allowSwipe(target) && view.contains(target)) {
tabsElem.selectNext();
}
};
var onSwipeRight = function (e, target) {
const onSwipeRight = function (e, target) {
if (allowSwipe(target) && view.contains(target)) {
tabsElem.selectPrevious();
}
};
require(['touchHelper'], function (TouchHelper) {
import('touchHelper').then(({default: TouchHelper}) => {
var touchHelper = new TouchHelper(view.parentNode.parentNode);
const touchHelper = new TouchHelper(view.parentNode.parentNode);
events.on(touchHelper, 'swipeleft', onSwipeLeft);
events.on(touchHelper, 'swiperight', onSwipeRight);
@ -96,9 +101,9 @@ define(['dom', 'browser', 'events', 'emby-tabs', 'emby-button'], function (dom,
});
}
function setTabs(view, selectedIndex, getTabsFn, getTabContainersFn, onBeforeTabChange, onTabChange, setSelectedIndex) {
export function setTabs(view, selectedIndex, getTabsFn, getTabContainersFn, onBeforeTabChange, onTabChange, setSelectedIndex) {
var enableInFooter = enableTabsInFooter();
const enableInFooter = enableTabsInFooter();
if (!view) {
if (tabOwnerView) {
@ -129,7 +134,7 @@ define(['dom', 'browser', 'events', 'emby-tabs', 'emby-button'], function (dom,
ensureElements(enableInFooter);
var tabsContainerElem = enableInFooter ? footerTabsContainer : headerTabsContainer;
const tabsContainerElem = enableInFooter ? footerTabsContainer : headerTabsContainer;
if (!tabOwnerView) {
tabsContainerElem.classList.remove('hide');
@ -137,18 +142,18 @@ define(['dom', 'browser', 'events', 'emby-tabs', 'emby-button'], function (dom,
if (tabOwnerView !== view) {
var index = 0;
let index = 0;
var indexAttribute = selectedIndex == null ? '' : (' data-index="' + selectedIndex + '"');
var tabsHtml = '<div is="emby-tabs"' + indexAttribute + ' class="tabs-viewmenubar"><div class="emby-tabs-slider" style="white-space:nowrap;">' + getTabsFn().map(function (t) {
const indexAttribute = selectedIndex == null ? '' : (' data-index="' + selectedIndex + '"');
const tabsHtml = '<div is="emby-tabs"' + indexAttribute + ' class="tabs-viewmenubar"><div class="emby-tabs-slider" style="white-space:nowrap;">' + getTabsFn().map(function (t) {
var tabClass = 'emby-tab-button';
let tabClass = 'emby-tab-button';
if (t.enabled === false) {
tabClass += ' hide';
}
var tabHtml;
let tabHtml;
if (t.cssClass) {
tabClass += ' ' + t.cssClass;
@ -177,16 +182,16 @@ define(['dom', 'browser', 'events', 'emby-tabs', 'emby-button'], function (dom,
tabsElem.addEventListener('beforetabchange', function (e) {
var tabContainers = getTabContainersFn();
const tabContainers = getTabContainersFn();
if (e.detail.previousIndex != null) {
var previousPanel = tabContainers[e.detail.previousIndex];
const previousPanel = tabContainers[e.detail.previousIndex];
if (previousPanel) {
previousPanel.classList.remove('is-active');
}
}
var newPanel = tabContainers[e.detail.selectedTabIndex];
const newPanel = tabContainers[e.detail.selectedTabIndex];
//if (e.detail.previousIndex != null && e.detail.previousIndex != e.detail.selectedTabIndex) {
// if (newPanel.animate && (animateTabs || []).indexOf(e.detail.selectedTabIndex) != -1) {
@ -241,9 +246,9 @@ define(['dom', 'browser', 'events', 'emby-tabs', 'emby-button'], function (dom,
};
}
function selectedTabIndex(index) {
export function selectedTabIndex(index) {
var tabsContainerElem = headerTabsContainer;
const tabsContainerElem = headerTabsContainer;
if (!tabsElem) {
tabsElem = tabsContainerElem.querySelector('[is="emby-tabs"]');
@ -256,13 +261,8 @@ define(['dom', 'browser', 'events', 'emby-tabs', 'emby-button'], function (dom,
}
}
function getTabsElement() {
export function getTabsElement() {
return document.querySelector('.tabs-viewmenubar');
}
return {
setTabs: setTabs,
getTabsElement: getTabsElement,
selectedTabIndex: selectedTabIndex
};
});
/* eslint-enable indent */

View file

@ -1,5 +1,24 @@
define(['loading', 'dialogHelper', 'dom', 'jQuery', 'components/libraryoptionseditor/libraryoptionseditor', 'globalize', 'emby-toggle', 'emby-input', 'emby-select', 'paper-icon-button-light', 'listViewStyle', 'formDialogStyle', 'emby-button', 'flexStyles'], function (loading, dialogHelper, dom, $, libraryoptionseditor, globalize) {
'use strict';
/* eslint-disable indent */
/**
* Module for media library creator.
* @module components/mediaLibraryCreator/mediaLibraryCreator
*/
import loading from 'loading';
import dialogHelper from 'dialogHelper';
import dom from 'dom';
import $ from 'jQuery';
import libraryoptionseditor from 'components/libraryoptionseditor/libraryoptionseditor';
import globalize from 'globalize';
import 'emby-toggle';
import 'emby-input';
import 'emby-select';
import 'paper-icon-button-light';
import 'listViewStyle';
import 'formDialogStyle';
import 'emby-button';
import 'flexStyles';
function onAddLibrary() {
if (isCreating) {
@ -7,7 +26,7 @@ define(['loading', 'dialogHelper', 'dom', 'jQuery', 'components/libraryoptionsed
}
if (pathInfos.length == 0) {
require(['alert'], function (alert) {
import('alert').then(({default: alert}) => {
alert({
text: globalize.translate('PleaseAddAtLeastOneFolder'),
type: 'error'
@ -19,23 +38,23 @@ define(['loading', 'dialogHelper', 'dom', 'jQuery', 'components/libraryoptionsed
isCreating = true;
loading.show();
var dlg = dom.parentWithClass(this, 'dlg-librarycreator');
var name = $('#txtValue', dlg).val();
var type = $('#selectCollectionType', dlg).val();
const dlg = dom.parentWithClass(this, 'dlg-librarycreator');
const name = $('#txtValue', dlg).val();
let type = $('#selectCollectionType', dlg).val();
if (type == 'mixed') {
type = null;
}
var libraryOptions = libraryoptionseditor.getLibraryOptions(dlg.querySelector('.libraryOptions'));
const libraryOptions = libraryoptionseditor.getLibraryOptions(dlg.querySelector('.libraryOptions'));
libraryOptions.PathInfos = pathInfos;
ApiClient.addVirtualFolder(name, type, currentOptions.refresh, libraryOptions).then(function () {
ApiClient.addVirtualFolder(name, type, currentOptions.refresh, libraryOptions).then(() => {
hasChanges = true;
isCreating = false;
loading.hide();
dialogHelper.close(dlg);
}, function () {
require(['toast'], function (toast) {
}, () => {
import('toast').then(({default: toast}) => {
toast(globalize.translate('ErrorAddingMediaPathToVirtualFolder'));
});
@ -46,15 +65,15 @@ define(['loading', 'dialogHelper', 'dom', 'jQuery', 'components/libraryoptionsed
}
function getCollectionTypeOptionsHtml(collectionTypeOptions) {
return collectionTypeOptions.map(function (i) {
return '<option value="' + i.value + '">' + i.name + '</option>';
return collectionTypeOptions.map(i => {
return `<option value="${i.value}">${i.name}</option>`;
}).join('');
}
function initEditor(page, collectionTypeOptions) {
$('#selectCollectionType', page).html(getCollectionTypeOptionsHtml(collectionTypeOptions)).val('').on('change', function () {
var value = this.value;
var dlg = $(this).parents('.dialog')[0];
const value = this.value;
const dlg = $(this).parents('.dialog')[0];
libraryoptionseditor.setContentType(dlg.querySelector('.libraryOptions'), value == 'mixed' ? '' : value);
if (value) {
@ -64,12 +83,12 @@ define(['loading', 'dialogHelper', 'dom', 'jQuery', 'components/libraryoptionsed
}
if (value != 'mixed') {
var index = this.selectedIndex;
const index = this.selectedIndex;
if (index != -1) {
var name = this.options[index].innerHTML.replace('*', '').replace('&amp;', '&');
const name = this.options[index].innerHTML.replace('*', '').replace('&amp;', '&');
$('#txtValue', dlg).val(name);
var folderOption = collectionTypeOptions.filter(function (i) {
const folderOption = collectionTypeOptions.filter(i => {
return i.value == value;
})[0];
$('.collectionTypeFieldDescription', dlg).html(folderOption.message || '');
@ -83,15 +102,15 @@ define(['loading', 'dialogHelper', 'dom', 'jQuery', 'components/libraryoptionsed
}
function onToggleAdvancedChange() {
var dlg = dom.parentWithClass(this, 'dlg-librarycreator');
const dlg = dom.parentWithClass(this, 'dlg-librarycreator');
libraryoptionseditor.setAdvancedVisible(dlg.querySelector('.libraryOptions'), this.checked);
}
function onAddButtonClick() {
var page = dom.parentWithClass(this, 'dlg-librarycreator');
const page = dom.parentWithClass(this, 'dlg-librarycreator');
require(['directorybrowser'], function (directoryBrowser) {
var picker = new directoryBrowser();
import('directorybrowser').then(({default: directoryBrowser}) => {
const picker = new directoryBrowser();
picker.show({
enableNetworkSharePath: true,
callback: function (path, networkSharePath) {
@ -106,24 +125,24 @@ define(['loading', 'dialogHelper', 'dom', 'jQuery', 'components/libraryoptionsed
}
function getFolderHtml(pathInfo, index) {
var html = '';
let html = '';
html += '<div class="listItem listItem-border lnkPath" style="padding-left:.5em;">';
html += '<div class="' + (pathInfo.NetworkPath ? 'listItemBody two-line' : 'listItemBody') + '">';
html += '<div class="listItemBodyText">' + pathInfo.Path + '</div>';
html += `<div class="${pathInfo.NetworkPath ? 'listItemBody two-line' : 'listItemBody'}">`;
html += `<div class="listItemBodyText">${pathInfo.Path}</div>`;
if (pathInfo.NetworkPath) {
html += '<div class="listItemBodyText secondary">' + pathInfo.NetworkPath + '</div>';
html += `<div class="listItemBodyText secondary">${pathInfo.NetworkPath}</div>`;
}
html += '</div>';
html += '<button type="button" is="paper-icon-button-light"" class="listItemButton btnRemovePath" data-index="' + index + '"><span class="material-icons remove_circle"></span></button>';
html += `<button type="button" is="paper-icon-button-light"" class="listItemButton btnRemovePath" data-index="${index}"><span class="material-icons remove_circle"></span></button>`;
html += '</div>';
return html;
}
function renderPaths(page) {
var foldersHtml = pathInfos.map(getFolderHtml).join('');
var folderList = page.querySelector('.folderList');
const foldersHtml = pathInfos.map(getFolderHtml).join('');
const folderList = page.querySelector('.folderList');
folderList.innerHTML = foldersHtml;
if (foldersHtml) {
@ -134,13 +153,13 @@ define(['loading', 'dialogHelper', 'dom', 'jQuery', 'components/libraryoptionsed
}
function addMediaLocation(page, path, networkSharePath) {
var pathLower = path.toLowerCase();
var pathFilter = pathInfos.filter(function (p) {
const pathLower = path.toLowerCase();
const pathFilter = pathInfos.filter(p => {
return p.Path.toLowerCase() == pathLower;
});
if (!pathFilter.length) {
var pathInfo = {
const pathInfo = {
Path: path
};
@ -154,11 +173,11 @@ define(['loading', 'dialogHelper', 'dom', 'jQuery', 'components/libraryoptionsed
}
function onRemoveClick(e) {
var button = dom.parentWithClass(e.target, 'btnRemovePath');
var index = parseInt(button.getAttribute('data-index'));
var location = pathInfos[index].Path;
var locationLower = location.toLowerCase();
pathInfos = pathInfos.filter(function (p) {
const button = dom.parentWithClass(e.target, 'btnRemovePath');
const index = parseInt(button.getAttribute('data-index'));
const location = pathInfos[index].Path;
const locationLower = location.toLowerCase();
pathInfos = pathInfos.filter(p => {
return p.Path.toLowerCase() != locationLower;
});
renderPaths(dom.parentWithClass(button, 'dlg-librarycreator'));
@ -169,54 +188,49 @@ define(['loading', 'dialogHelper', 'dom', 'jQuery', 'components/libraryoptionsed
}
function initLibraryOptions(dlg) {
libraryoptionseditor.embed(dlg.querySelector('.libraryOptions')).then(function () {
libraryoptionseditor.embed(dlg.querySelector('.libraryOptions')).then(() => {
$('#selectCollectionType', dlg).trigger('change');
onToggleAdvancedChange.call(dlg.querySelector('.chkAdvanced'));
});
}
function editor() {
this.show = function (options) {
return new Promise(function (resolve, reject) {
currentOptions = options;
currentResolve = resolve;
hasChanges = false;
var xhr = new XMLHttpRequest();
xhr.open('GET', 'components/mediaLibraryCreator/mediaLibraryCreator.template.html', true);
xhr.onload = function (e) {
var template = this.response;
var dlg = dialogHelper.createDialog({
size: 'small',
modal: false,
removeOnClose: true,
scrollY: false
});
dlg.classList.add('ui-body-a');
dlg.classList.add('background-theme-a');
dlg.classList.add('dlg-librarycreator');
dlg.classList.add('formDialog');
dlg.innerHTML = globalize.translateDocument(template);
initEditor(dlg, options.collectionTypeOptions);
dlg.addEventListener('close', onDialogClosed);
dialogHelper.open(dlg);
dlg.querySelector('.btnCancel').addEventListener('click', function () {
dialogHelper.close(dlg);
});
pathInfos = [];
renderPaths(dlg);
initLibraryOptions(dlg);
};
xhr.send();
export class showEditor {
constructor(options) {
return new Promise((resolve) => {
currentOptions = options;
currentResolve = resolve;
hasChanges = false;
import('text!./components/mediaLibraryCreator/mediaLibraryCreator.template.html').then(({default: template}) => {
const dlg = dialogHelper.createDialog({
size: 'small',
modal: false,
removeOnClose: true,
scrollY: false
});
dlg.classList.add('ui-body-a');
dlg.classList.add('background-theme-a');
dlg.classList.add('dlg-librarycreator');
dlg.classList.add('formDialog');
dlg.innerHTML = globalize.translateDocument(template);
initEditor(dlg, options.collectionTypeOptions);
dlg.addEventListener('close', onDialogClosed);
dialogHelper.open(dlg);
dlg.querySelector('.btnCancel').addEventListener('click', () => {
dialogHelper.close(dlg);
});
pathInfos = [];
renderPaths(dlg);
initLibraryOptions(dlg);
});
};
});
}
}
var pathInfos = [];
var currentResolve;
var currentOptions;
var hasChanges = false;
var isCreating = false;
return editor;
});
let pathInfos = [];
let currentResolve;
let currentOptions;
let hasChanges = false;
let isCreating = false;
/* eslint-enable indent */
export default showEditor;

View file

@ -1,5 +1,22 @@
define(['jQuery', 'loading', 'dialogHelper', 'dom', 'components/libraryoptionseditor/libraryoptionseditor', 'globalize', 'emby-button', 'listViewStyle', 'paper-icon-button-light', 'formDialogStyle', 'emby-toggle', 'flexStyles'], function (jQuery, loading, dialogHelper, dom, libraryoptionseditor, globalize) {
'use strict';
/* eslint-disable indent */
/**
* Module for media library editor.
* @module components/mediaLibraryEditor/mediaLibraryEditor
*/
import jQuery from 'jQuery';
import loading from 'loading';
import dialogHelper from 'dialogHelper';
import dom from 'dom';
import libraryoptionseditor from 'components/libraryoptionseditor/libraryoptionseditor';
import globalize from 'globalize';
import 'emby-button';
import 'listViewStyle';
import 'paper-icon-button-light';
import 'formDialogStyle';
import 'emby-toggle';
import 'flexStyles';
function onEditLibrary() {
if (isCreating) {
@ -8,15 +25,15 @@ define(['jQuery', 'loading', 'dialogHelper', 'dom', 'components/libraryoptionsed
isCreating = true;
loading.show();
var dlg = dom.parentWithClass(this, 'dlg-libraryeditor');
var libraryOptions = libraryoptionseditor.getLibraryOptions(dlg.querySelector('.libraryOptions'));
const dlg = dom.parentWithClass(this, 'dlg-libraryeditor');
let libraryOptions = libraryoptionseditor.getLibraryOptions(dlg.querySelector('.libraryOptions'));
libraryOptions = Object.assign(currentOptions.library.LibraryOptions || {}, libraryOptions);
ApiClient.updateVirtualFolderOptions(currentOptions.library.ItemId, libraryOptions).then(function () {
ApiClient.updateVirtualFolderOptions(currentOptions.library.ItemId, libraryOptions).then(() => {
hasChanges = true;
isCreating = false;
loading.hide();
dialogHelper.close(dlg);
}, function () {
}, () => {
isCreating = false;
loading.hide();
});
@ -24,50 +41,50 @@ define(['jQuery', 'loading', 'dialogHelper', 'dom', 'components/libraryoptionsed
}
function addMediaLocation(page, path, networkSharePath) {
var virtualFolder = currentOptions.library;
var refreshAfterChange = currentOptions.refresh;
ApiClient.addMediaPath(virtualFolder.Name, path, networkSharePath, refreshAfterChange).then(function () {
const virtualFolder = currentOptions.library;
const refreshAfterChange = currentOptions.refresh;
ApiClient.addMediaPath(virtualFolder.Name, path, networkSharePath, refreshAfterChange).then(() => {
hasChanges = true;
refreshLibraryFromServer(page);
}, function () {
require(['toast'], function (toast) {
}, () => {
import('toast').then(({default: toast}) => {
toast(globalize.translate('ErrorAddingMediaPathToVirtualFolder'));
});
});
}
function updateMediaLocation(page, path, networkSharePath) {
var virtualFolder = currentOptions.library;
const virtualFolder = currentOptions.library;
ApiClient.updateMediaPath(virtualFolder.Name, {
Path: path,
NetworkPath: networkSharePath
}).then(function () {
}).then(() => {
hasChanges = true;
refreshLibraryFromServer(page);
}, function () {
require(['toast'], function (toast) {
}, () => {
import('toast').then(({default: toast}) => {
toast(globalize.translate('ErrorAddingMediaPathToVirtualFolder'));
});
});
}
function onRemoveClick(btnRemovePath, location) {
var button = btnRemovePath;
var virtualFolder = currentOptions.library;
const button = btnRemovePath;
const virtualFolder = currentOptions.library;
require(['confirm'], function (confirm) {
import('confirm').then(({default: confirm}) => {
confirm({
title: globalize.translate('HeaderRemoveMediaLocation'),
text: globalize.translate('MessageConfirmRemoveMediaLocation'),
confirmText: globalize.translate('ButtonDelete'),
primary: 'delete'
}).then(function () {
var refreshAfterChange = currentOptions.refresh;
ApiClient.removeMediaPath(virtualFolder.Name, location, refreshAfterChange).then(function () {
}).then(() => {
const refreshAfterChange = currentOptions.refresh;
ApiClient.removeMediaPath(virtualFolder.Name, location, refreshAfterChange).then(() => {
hasChanges = true;
refreshLibraryFromServer(dom.parentWithClass(button, 'dlg-libraryeditor'));
}, function () {
require(['toast'], function (toast) {
}, () => {
import('toast').then(({default: toast}) => {
toast(globalize.translate('DefaultErrorMessage'));
});
});
@ -76,14 +93,14 @@ define(['jQuery', 'loading', 'dialogHelper', 'dom', 'components/libraryoptionsed
}
function onListItemClick(e) {
var listItem = dom.parentWithClass(e.target, 'listItem');
const listItem = dom.parentWithClass(e.target, 'listItem');
if (listItem) {
var index = parseInt(listItem.getAttribute('data-index'));
var pathInfos = (currentOptions.library.LibraryOptions || {}).PathInfos || [];
var pathInfo = null == index ? {} : pathInfos[index] || {};
var originalPath = pathInfo.Path || (null == index ? null : currentOptions.library.Locations[index]);
var btnRemovePath = dom.parentWithClass(e.target, 'btnRemovePath');
const index = parseInt(listItem.getAttribute('data-index'));
const pathInfos = (currentOptions.library.LibraryOptions || {}).PathInfos || [];
const pathInfo = null == index ? {} : pathInfos[index] || {};
const originalPath = pathInfo.Path || (null == index ? null : currentOptions.library.Locations[index]);
const btnRemovePath = dom.parentWithClass(e.target, 'btnRemovePath');
if (btnRemovePath) {
onRemoveClick(btnRemovePath, originalPath);
@ -95,26 +112,26 @@ define(['jQuery', 'loading', 'dialogHelper', 'dom', 'components/libraryoptionsed
}
function getFolderHtml(pathInfo, index) {
var html = '';
html += '<div class="listItem listItem-border lnkPath" data-index="' + index + '" style="padding-left:.5em;">';
html += '<div class="' + (pathInfo.NetworkPath ? 'listItemBody two-line' : 'listItemBody') + '">';
let html = '';
html += `<div class="listItem listItem-border lnkPath" data-index="${index}" style="padding-left:.5em;">`;
html += `<div class="${pathInfo.NetworkPath ? 'listItemBody two-line' : 'listItemBody'}">`;
html += '<h3 class="listItemBodyText">';
html += pathInfo.Path;
html += '</h3>';
if (pathInfo.NetworkPath) {
html += '<div class="listItemBodyText secondary">' + pathInfo.NetworkPath + '</div>';
html += `<div class="listItemBodyText secondary">${pathInfo.NetworkPath}</div>`;
}
html += '</div>';
html += '<button type="button" is="paper-icon-button-light" class="listItemButton btnRemovePath" data-index="' + index + '"><span class="material-icons remove_circle"></span></button>';
html += `<button type="button" is="paper-icon-button-light" class="listItemButton btnRemovePath" data-index="${index}"><span class="material-icons remove_circle"></span></button>`;
html += '</div>';
return html;
}
function refreshLibraryFromServer(page) {
ApiClient.getVirtualFolders().then(function (result) {
var library = result.filter(function (f) {
ApiClient.getVirtualFolders().then(result => {
const library = result.filter(f => {
return f.Name === currentOptions.library.Name;
})[0];
@ -126,10 +143,10 @@ define(['jQuery', 'loading', 'dialogHelper', 'dom', 'components/libraryoptionsed
}
function renderLibrary(page, options) {
var pathInfos = (options.library.LibraryOptions || {}).PathInfos || [];
let pathInfos = (options.library.LibraryOptions || {}).PathInfos || [];
if (!pathInfos.length) {
pathInfos = options.library.Locations.map(function (p) {
pathInfos = options.library.Locations.map(p => {
return {
Path: p
};
@ -150,8 +167,8 @@ define(['jQuery', 'loading', 'dialogHelper', 'dom', 'components/libraryoptionsed
}
function showDirectoryBrowser(context, originalPath, networkPath) {
require(['directorybrowser'], function (directoryBrowser) {
var picker = new directoryBrowser();
import('directorybrowser').then(({default: directoryBrowser}) => {
const picker = new directoryBrowser();
picker.show({
enableNetworkSharePath: true,
pathReadOnly: null != originalPath,
@ -173,7 +190,7 @@ define(['jQuery', 'loading', 'dialogHelper', 'dom', 'components/libraryoptionsed
}
function onToggleAdvancedChange() {
var dlg = dom.parentWithClass(this, 'dlg-libraryeditor');
const dlg = dom.parentWithClass(this, 'dlg-libraryeditor');
libraryoptionseditor.setAdvancedVisible(dlg.querySelector('.libraryOptions'), this.checked);
}
@ -183,7 +200,7 @@ define(['jQuery', 'loading', 'dialogHelper', 'dom', 'components/libraryoptionsed
dlg.querySelector('.folderList').addEventListener('click', onListItemClick);
dlg.querySelector('.chkAdvanced').addEventListener('change', onToggleAdvancedChange);
dlg.querySelector('.btnSubmit').addEventListener('click', onEditLibrary);
libraryoptionseditor.embed(dlg.querySelector('.libraryOptions'), options.library.CollectionType, options.library.LibraryOptions).then(function () {
libraryoptionseditor.embed(dlg.querySelector('.libraryOptions'), options.library.CollectionType, options.library.LibraryOptions).then(() => {
onToggleAdvancedChange.call(dlg.querySelector('.chkAdvanced'));
});
}
@ -192,46 +209,41 @@ define(['jQuery', 'loading', 'dialogHelper', 'dom', 'components/libraryoptionsed
currentDeferred.resolveWith(null, [hasChanges]);
}
function editor() {
this.show = function (options) {
var deferred = jQuery.Deferred();
currentOptions = options;
currentDeferred = deferred;
hasChanges = false;
var xhr = new XMLHttpRequest();
xhr.open('GET', 'components/mediaLibraryEditor/mediaLibraryEditor.template.html', true);
xhr.onload = function (e) {
var template = this.response;
var dlg = dialogHelper.createDialog({
size: 'small',
modal: false,
removeOnClose: true,
scrollY: false
});
dlg.classList.add('dlg-libraryeditor');
dlg.classList.add('ui-body-a');
dlg.classList.add('background-theme-a');
dlg.classList.add('formDialog');
dlg.innerHTML = globalize.translateDocument(template);
dlg.querySelector('.formDialogHeaderTitle').innerHTML = options.library.Name;
initEditor(dlg, options);
dlg.addEventListener('close', onDialogClosed);
dialogHelper.open(dlg);
dlg.querySelector('.btnCancel').addEventListener('click', function () {
dialogHelper.close(dlg);
});
refreshLibraryFromServer(dlg);
};
xhr.send();
return deferred.promise();
};
export class showEditor {
constructor(options) {
const deferred = jQuery.Deferred();
currentOptions = options;
currentDeferred = deferred;
hasChanges = false;
import('text!./components/mediaLibraryEditor/mediaLibraryEditor.template.html').then(({default: template}) => {
const dlg = dialogHelper.createDialog({
size: 'small',
modal: false,
removeOnClose: true,
scrollY: false
});
dlg.classList.add('dlg-libraryeditor');
dlg.classList.add('ui-body-a');
dlg.classList.add('background-theme-a');
dlg.classList.add('formDialog');
dlg.innerHTML = globalize.translateDocument(template);
dlg.querySelector('.formDialogHeaderTitle').innerHTML = options.library.Name;
initEditor(dlg, options);
dlg.addEventListener('close', onDialogClosed);
dialogHelper.open(dlg);
dlg.querySelector('.btnCancel').addEventListener('click', () => {
dialogHelper.close(dlg);
});
refreshLibraryFromServer(dlg);
});
return deferred.promise();
}
}
var currentDeferred;
var currentOptions;
var hasChanges = false;
var isCreating = false;
return editor;
});
let currentDeferred;
let currentOptions;
let hasChanges = false;
let isCreating = false;
/* eslint-enable indent */
export default showEditor;

View file

@ -7,7 +7,6 @@
<div class="formDialogContent smoothScrollY" style="padding-top:2em;">
<form class="popupEditPersonForm dialogContentInner dialog-content-centered">
<div class="inputContainer">
<input type="text" is="emby-input" class="txtPersonName" required="required" label="${LabelName}" />
</div>
@ -23,6 +22,7 @@
<option value="Writer">${Writer}</option>
</select>
</div>
<div class="inputContainer fldRole hide">
<input is="emby-input" type="text" class="txtPersonRole" label="${LabelPersonRole}" />
<div class="fieldDescription">${LabelPersonRoleHelp}</div>
@ -33,6 +33,5 @@
<span>${Save}</span>
</button>
</div>
</form>
</div>

View file

@ -262,7 +262,7 @@ import 'css!./multiSelect';
switch (id) {
case 'addtocollection':
import('collectionEditor').then(({default: collectionEditor}) => {
new collectionEditor().show({
new collectionEditor.showEditor({
items: items,
serverId: serverId
});
@ -272,7 +272,7 @@ import 'css!./multiSelect';
break;
case 'playlist':
import('playlistEditor').then(({default: playlistEditor}) => {
new playlistEditor().show({
new playlistEditor.showEditor({
items: items,
serverId: serverId
});

View file

@ -241,6 +241,15 @@ import connectionManager from 'connectionManager';
navigator.mediaSession.setActionHandler('seekforward', function () {
execute('fastForward');
});
/* eslint-disable-next-line compat/compat */
navigator.mediaSession.setActionHandler('seekto', function (object) {
let item = playbackManager.getPlayerState(currentPlayer).NowPlayingItem;
// Convert to ms
let duration = parseInt(item.RunTimeTicks ? (item.RunTimeTicks / 10000) : 0);
let wantedTime = object.seekTime * 1000;
playbackManager.seekPercent(wantedTime / duration * 100, currentPlayer);
});
}
events.on(playbackManager, 'playerchange', function () {

View file

@ -1,31 +1,42 @@
define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'qualityoptions', 'globalize', 'loading', 'connectionManager', 'dom', 'events', 'emby-select', 'emby-checkbox'], function (require, browser, appSettings, appHost, focusManager, qualityoptions, globalize, loading, connectionManager, dom, events) {
'use strict';
import browser from 'browser';
import appSettings from 'appSettings';
import appHost from 'apphost';
import focusManager from 'focusManager';
import qualityoptions from 'qualityoptions';
import globalize from 'globalize';
import loading from 'loading';
import connectionManager from 'connectionManager';
import events from 'events';
import 'emby-select';
import 'emby-checkbox';
/* eslint-disable indent */
function fillSkipLengths(select) {
var options = [5, 10, 15, 20, 25, 30];
const options = [5, 10, 15, 20, 25, 30];
select.innerHTML = options.map(function (option) {
select.innerHTML = options.map(option => {
return {
name: globalize.translate('ValueSeconds', option),
value: option * 1000
};
}).map(function (o) {
return '<option value="' + o.value + '">' + o.name + '</option>';
}).map(o => {
return `<option value="${o.value}">${o.name}</option>`;
}).join('');
}
function populateLanguages(select, languages) {
var html = '';
let html = '';
html += "<option value=''>" + globalize.translate('AnyLanguage') + '</option>';
html += `<option value=''>${globalize.translate('AnyLanguage')}</option>`;
for (var i = 0, length = languages.length; i < length; i++) {
for (let i = 0, length = languages.length; i < length; i++) {
var culture = languages[i];
const culture = languages[i];
html += "<option value='" + culture.ThreeLetterISOLanguageName + "'>" + culture.DisplayName + '</option>';
html += `<option value='${culture.ThreeLetterISOLanguageName}'>${culture.DisplayName}</option>`;
}
select.innerHTML = html;
@ -33,7 +44,7 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality
function setMaxBitrateIntoField(select, isInNetwork, mediatype) {
var options = mediatype === 'Audio' ? qualityoptions.getAudioQualityOptions({
const options = mediatype === 'Audio' ? qualityoptions.getAudioQualityOptions({
currentMaxBitrate: appSettings.maxStreamingBitrate(isInNetwork, mediatype),
isAutomaticBitrateEnabled: appSettings.enableAutomaticBitrateDetection(isInNetwork, mediatype),
@ -47,10 +58,10 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality
});
select.innerHTML = options.map(function (i) {
select.innerHTML = options.map(i => {
// render empty string instead of 0 for the auto option
return '<option value="' + (i.bitrate || '') + '">' + i.name + '</option>';
return `<option value="${i.bitrate || ''}">${i.name}</option>`;
}).join('');
if (appSettings.enableAutomaticBitrateDetection(isInNetwork, mediatype)) {
@ -62,23 +73,23 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality
function fillChromecastQuality(select) {
var options = qualityoptions.getVideoQualityOptions({
const options = qualityoptions.getVideoQualityOptions({
currentMaxBitrate: appSettings.maxChromecastBitrate(),
isAutomaticBitrateEnabled: !appSettings.maxChromecastBitrate(),
enableAuto: true
});
select.innerHTML = options.map(function (i) {
select.innerHTML = options.map(i => {
// render empty string instead of 0 for the auto option
return '<option value="' + (i.bitrate || '') + '">' + i.name + '</option>';
return `<option value="${i.bitrate || ''}">${i.name}</option>`;
}).join('');
select.value = appSettings.maxChromecastBitrate() || '';
}
function setMaxBitrateFromField(select, isInNetwork, mediatype, value) {
function setMaxBitrateFromField(select, isInNetwork, mediatype) {
if (select.value) {
appSettings.maxStreamingBitrate(isInNetwork, mediatype, select.value);
@ -110,7 +121,7 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality
return;
}
apiClient.getEndpointInfo().then(function (endpointInfo) {
apiClient.getEndpointInfo().then(endpointInfo => {
if (endpointInfo.IsInNetwork) {
@ -133,7 +144,7 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality
});
}
function showOrHideEpisodesField(context, user, apiClient) {
function showOrHideEpisodesField(context) {
if (browser.tizen || browser.web0s) {
context.querySelector('.fldEpisodeAutoPlay').classList.add('hide');
@ -145,12 +156,12 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality
function loadForm(context, user, userSettings, apiClient) {
var loggedInUserId = apiClient.getCurrentUserId();
var userId = user.Id;
const loggedInUserId = apiClient.getCurrentUserId();
const userId = user.Id;
showHideQualityFields(context, user, apiClient);
apiClient.getCultures().then(function (allCultures) {
apiClient.getCultures().then(allCultures => {
populateLanguages(context.querySelector('#selectAudioLanguage'), allCultures);
@ -159,7 +170,7 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality
});
// hide cinema mode options if disabled at server level
apiClient.getNamedConfiguration('cinemamode').then(function (cinemaConfig) {
apiClient.getNamedConfiguration('cinemamode').then(cinemaConfig => {
if (cinemaConfig.EnableIntrosForMovies || cinemaConfig.EnableIntrosForEpisodes) {
context.querySelector('.cinemaModeOptions').classList.remove('hide');
@ -204,18 +215,18 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality
fillChromecastQuality(context.querySelector('.selectChromecastVideoQuality'));
var selectChromecastVersion = context.querySelector('.selectChromecastVersion');
const selectChromecastVersion = context.querySelector('.selectChromecastVersion');
selectChromecastVersion.value = userSettings.chromecastVersion();
var selectSkipForwardLength = context.querySelector('.selectSkipForwardLength');
const selectSkipForwardLength = context.querySelector('.selectSkipForwardLength');
fillSkipLengths(selectSkipForwardLength);
selectSkipForwardLength.value = userSettings.skipForwardLength();
var selectSkipBackLength = context.querySelector('.selectSkipBackLength');
const selectSkipBackLength = context.querySelector('.selectSkipBackLength');
fillSkipLengths(selectSkipBackLength);
selectSkipBackLength.value = userSettings.skipBackLength();
showOrHideEpisodesField(context, user, apiClient);
showOrHideEpisodesField(context);
loading.hide();
}
@ -248,20 +259,20 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality
loading.show();
apiClient.getUser(userId).then(function (user) {
apiClient.getUser(userId).then(user => {
saveUser(context, user, userSettings, apiClient).then(function () {
saveUser(context, user, userSettings, apiClient).then(() => {
loading.hide();
if (enableSaveConfirmation) {
require(['toast'], function (toast) {
import('toast').then(({default: toast}) => {
toast(globalize.translate('SettingsSaved'));
});
}
events.trigger(instance, 'saved');
}, function () {
}, () => {
loading.hide();
});
});
@ -269,14 +280,14 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality
function onSubmit(e) {
var self = this;
var apiClient = connectionManager.getApiClient(self.options.serverId);
var userId = self.options.userId;
var userSettings = self.options.userSettings;
const self = this;
const apiClient = connectionManager.getApiClient(self.options.serverId);
const userId = self.options.userId;
const userSettings = self.options.userSettings;
userSettings.setUserInfo(userId, apiClient).then(function () {
userSettings.setUserInfo(userId, apiClient).then(() => {
var enableSaveConfirmation = self.options.enableSaveConfirmation;
const enableSaveConfirmation = self.options.enableSaveConfirmation;
save(self, self.options.element, userId, userSettings, apiClient, enableSaveConfirmation);
});
@ -289,7 +300,7 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality
function embed(options, self) {
require(['text!./playbackSettings.template.html'], function (template) {
return import('text!./playbackSettings.template.html').then(({default: template}) => {
options.element.innerHTML = globalize.translateDocument(template, 'core');
@ -307,43 +318,43 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality
});
}
function PlaybackSettings(options) {
class PlaybackSettings {
constructor(options) {
this.options = options;
embed(options, this);
}
this.options = options;
loadData() {
embed(options, this);
const self = this;
const context = self.options.element;
loading.show();
const userId = self.options.userId;
const apiClient = connectionManager.getApiClient(self.options.serverId);
const userSettings = self.options.userSettings;
apiClient.getUser(userId).then(user => {
userSettings.setUserInfo(userId, apiClient).then(() => {
self.dataLoaded = true;
loadForm(context, user, userSettings, apiClient);
});
});
}
submit() {
onSubmit.call(this);
}
destroy() {
this.options = null;
}
}
PlaybackSettings.prototype.loadData = function () {
var self = this;
var context = self.options.element;
loading.show();
var userId = self.options.userId;
var apiClient = connectionManager.getApiClient(self.options.serverId);
var userSettings = self.options.userSettings;
apiClient.getUser(userId).then(function (user) {
userSettings.setUserInfo(userId, apiClient).then(function () {
self.dataLoaded = true;
loadForm(context, user, userSettings, apiClient);
});
});
};
PlaybackSettings.prototype.submit = function () {
onSubmit.call(this);
};
PlaybackSettings.prototype.destroy = function () {
this.options = null;
};
return PlaybackSettings;
});
/* eslint-enable indent */
export default PlaybackSettings;

View file

@ -1,13 +1,28 @@
define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager', 'connectionManager', 'userSettings', 'appRouter', 'globalize', 'emby-input', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button'], function (dom, shell, dialogHelper, loading, layoutManager, playbackManager, connectionManager, userSettings, appRouter, globalize) {
'use strict';
import dom from 'dom';
import dialogHelper from 'dialogHelper';
import loading from 'loading';
import layoutManager from 'layoutManager';
import playbackManager from 'playbackManager';
import connectionManager from 'connectionManager';
import * as userSettings from 'userSettings';
import appRouter from 'appRouter';
import globalize from 'globalize';
import 'emby-input';
import 'paper-icon-button-light';
import 'emby-select';
import 'material-icons';
import 'css!./../formdialog';
import 'emby-button';
var currentServerId;
/* eslint-disable indent */
let currentServerId;
function onSubmit(e) {
var panel = dom.parentWithClass(this, 'dialog');
const panel = dom.parentWithClass(this, 'dialog');
var playlistId = panel.querySelector('#selectPlaylistToAddTo').value;
var apiClient = connectionManager.getApiClient(currentServerId);
const playlistId = panel.querySelector('#selectPlaylistToAddTo').value;
const apiClient = connectionManager.getApiClient(currentServerId);
if (playlistId) {
userSettings.set('playlisteditor-lastplaylistid', playlistId);
@ -23,7 +38,7 @@ define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackMan
function createPlaylist(apiClient, dlg) {
loading.show();
var url = apiClient.getUrl('Playlists', {
const url = apiClient.getUrl('Playlists', {
Name: dlg.querySelector('#txtNewPlaylistName').value,
Ids: dlg.querySelector('.fldSelectedItemIds').value || '',
userId: apiClient.getCurrentUserId()
@ -34,10 +49,10 @@ define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackMan
type: 'POST',
url: url,
dataType: 'json'
}).then(function (result) {
}).then(result => {
loading.hide();
var id = result.Id;
const id = result.Id;
dlg.submitted = true;
dialogHelper.close(dlg);
redirectToPlaylist(apiClient, id);
@ -49,7 +64,7 @@ define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackMan
}
function addToPlaylist(apiClient, dlg, id) {
var itemIds = dlg.querySelector('.fldSelectedItemIds').value || '';
const itemIds = dlg.querySelector('.fldSelectedItemIds').value || '';
if (id === 'queue') {
playbackManager.queue({
@ -63,7 +78,7 @@ define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackMan
loading.show();
var url = apiClient.getUrl('Playlists/' + id + '/Items', {
const url = apiClient.getUrl(`Playlists/${id}/Items`, {
Ids: itemIds,
userId: apiClient.getCurrentUserId()
});
@ -72,7 +87,7 @@ define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackMan
type: 'POST',
url: url
}).then(function () {
}).then(() => {
loading.hide();
dlg.submitted = true;
@ -85,36 +100,36 @@ define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackMan
}
function populatePlaylists(editorOptions, panel) {
var select = panel.querySelector('#selectPlaylistToAddTo');
const select = panel.querySelector('#selectPlaylistToAddTo');
loading.hide();
panel.querySelector('.newPlaylistInfo').classList.add('hide');
var options = {
const options = {
Recursive: true,
IncludeItemTypes: 'Playlist',
SortBy: 'SortName',
EnableTotalRecordCount: false
};
var apiClient = connectionManager.getApiClient(currentServerId);
apiClient.getItems(apiClient.getCurrentUserId(), options).then(function (result) {
var html = '';
const apiClient = connectionManager.getApiClient(currentServerId);
apiClient.getItems(apiClient.getCurrentUserId(), options).then(result => {
let html = '';
if (editorOptions.enableAddToPlayQueue !== false && playbackManager.isPlaying()) {
html += '<option value="queue">' + globalize.translate('AddToPlayQueue') + '</option>';
html += `<option value="queue">${globalize.translate('AddToPlayQueue')}</option>`;
}
html += '<option value="">' + globalize.translate('OptionNew') + '</option>';
html += `<option value="">${globalize.translate('OptionNew')}</option>`;
html += result.Items.map(function (i) {
return '<option value="' + i.Id + '">' + i.Name + '</option>';
html += result.Items.map(i => {
return `<option value="${i.Id}">${i.Name}</option>`;
});
select.innerHTML = html;
var defaultValue = editorOptions.defaultValue;
let defaultValue = editorOptions.defaultValue;
if (!defaultValue) {
defaultValue = userSettings.get('playlisteditor-lastplaylistid') || '';
}
@ -132,29 +147,29 @@ define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackMan
}
function getEditorHtml(items) {
var html = '';
let html = '';
html += '<div class="formDialogContent smoothScrollY" style="padding-top:2em;">';
html += '<div class="dialogContentInner dialog-content-centered">';
html += '<form style="margin:auto;">';
html += '<div class="fldSelectPlaylist selectContainer">';
var autoFocus = items.length ? ' autofocus' : '';
html += '<select is="emby-select" id="selectPlaylistToAddTo" label="' + globalize.translate('LabelPlaylist') + '"' + autoFocus + '></select>';
let autoFocus = items.length ? ' autofocus' : '';
html += `<select is="emby-select" id="selectPlaylistToAddTo" label="${globalize.translate('LabelPlaylist')}"${autoFocus}></select>`;
html += '</div>';
html += '<div class="newPlaylistInfo">';
html += '<div class="inputContainer">';
autoFocus = items.length ? '' : ' autofocus';
html += '<input is="emby-input" type="text" id="txtNewPlaylistName" required="required" label="' + globalize.translate('LabelName') + '"' + autoFocus + ' />';
html += `<input is="emby-input" type="text" id="txtNewPlaylistName" required="required" label="${globalize.translate('LabelName')}"${autoFocus} />`;
html += '</div>';
// newPlaylistInfo
html += '</div>';
html += '<div class="formDialogFooter">';
html += '<button is="emby-button" type="submit" class="raised btnSubmit block formDialogFooterItem button-submit">' + globalize.translate('Add') + '</button>';
html += `<button is="emby-button" type="submit" class="raised btnSubmit block formDialogFooterItem button-submit">${globalize.translate('Add')}</button>`;
html += '</div>';
html += '<input type="hidden" class="fldSelectedItemIds" />';
@ -187,7 +202,7 @@ define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackMan
} else {
content.querySelector('.fldSelectPlaylist').classList.add('hide');
var selectPlaylistToAddTo = content.querySelector('#selectPlaylistToAddTo');
const selectPlaylistToAddTo = content.querySelector('#selectPlaylistToAddTo');
selectPlaylistToAddTo.innerHTML = '';
selectPlaylistToAddTo.value = '';
triggerChange(selectPlaylistToAddTo);
@ -195,72 +210,70 @@ define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackMan
}
function centerFocus(elem, horiz, on) {
require(['scrollHelper'], function (scrollHelper) {
var fn = on ? 'on' : 'off';
import('scrollHelper').then(scrollHelper => {
const fn = on ? 'on' : 'off';
scrollHelper.centerFocus[fn](elem, horiz);
});
}
function PlaylistEditor() {
export class showEditor {
constructor(options) {
const items = options.items || {};
currentServerId = options.serverId;
const dialogOptions = {
removeOnClose: true,
scrollY: false
};
if (layoutManager.tv) {
dialogOptions.size = 'fullscreen';
} else {
dialogOptions.size = 'small';
}
const dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog');
let html = '';
const title = globalize.translate('HeaderAddToPlaylist');
html += '<div class="formDialogHeader">';
html += '<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><span class="material-icons arrow_back"></span></button>';
html += '<h3 class="formDialogHeaderTitle">';
html += title;
html += '</h3>';
html += '</div>';
html += getEditorHtml(items);
dlg.innerHTML = html;
initEditor(dlg, options, items);
dlg.querySelector('.btnCancel').addEventListener('click', () => {
dialogHelper.close(dlg);
});
if (layoutManager.tv) {
centerFocus(dlg.querySelector('.formDialogContent'), false, true);
}
return dialogHelper.open(dlg).then(() => {
if (layoutManager.tv) {
centerFocus(dlg.querySelector('.formDialogContent'), false, false);
}
if (dlg.submitted) {
return Promise.resolve();
}
return Promise.reject();
});
}
}
PlaylistEditor.prototype.show = function (options) {
var items = options.items || {};
currentServerId = options.serverId;
var dialogOptions = {
removeOnClose: true,
scrollY: false
};
if (layoutManager.tv) {
dialogOptions.size = 'fullscreen';
} else {
dialogOptions.size = 'small';
}
var dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog');
var html = '';
var title = globalize.translate('HeaderAddToPlaylist');
html += '<div class="formDialogHeader">';
html += '<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><span class="material-icons arrow_back"></span></button>';
html += '<h3 class="formDialogHeaderTitle">';
html += title;
html += '</h3>';
html += '</div>';
html += getEditorHtml(items);
dlg.innerHTML = html;
initEditor(dlg, options, items);
dlg.querySelector('.btnCancel').addEventListener('click', function () {
dialogHelper.close(dlg);
});
if (layoutManager.tv) {
centerFocus(dlg.querySelector('.formDialogContent'), false, true);
}
return dialogHelper.open(dlg).then(function () {
if (layoutManager.tv) {
centerFocus(dlg.querySelector('.formDialogContent'), false, false);
}
if (dlg.submitted) {
return Promise.resolve();
}
return Promise.reject();
});
};
return PlaylistEditor;
});
/* eslint-enable indent */
export default showEditor;

View file

@ -2,12 +2,12 @@
<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1">
<span class="material-icons arrow_back"></span>
</button>
<h3 class="formDialogHeaderTitle"></h3>
</div>
<div class="formDialogContent smoothScrollY">
<div class="dialogContentInner dialog-content-centered" style="padding-top:2em;">
<form>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtInput" label="" />
@ -19,7 +19,6 @@
<span class="submitText"></span>
</button>
</div>
</form>
</div>
</div>

View file

@ -175,7 +175,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
context.querySelector('.nowPlayingPageUserDataButtons').innerHTML = '<button is="emby-ratingbutton" type="button" class="listItemButton paper-icon-button-light" data-id="' + fullItem.Id + '" data-serverid="' + fullItem.ServerId + '" data-itemtype="' + fullItem.Type + '" data-likes="' + likes + '" data-isfavorite="' + userData.IsFavorite + '"><span class="material-icons favorite"></span></button>';
});
} else {
backdrop.clear();
backdrop.clearBackdrop();
context.querySelector('.nowPlayingPageUserDataButtons').innerHTML = '';
}
}
@ -196,7 +196,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
context.querySelector('.nowPlayingPageImage').classList.remove('nowPlayingPageImageAudio');
}
} else {
imgContainer.innerHTML = '<div class="nowPlayingPageImageContainerNoAlbum"><button data-action="link" class="cardContent-button cardImageContainer coveredImage ' + cardBuilder.getDefaultBackgroundClass(item.Name) + ' cardContent cardContent-shadow itemAction"><span class="cardImageIcon material-icons album"></span></button></div>';
imgContainer.innerHTML = '<div class="nowPlayingPageImageContainerNoAlbum"><button data-action="link" class="cardImageContainer coveredImage ' + cardBuilder.getDefaultBackgroundClass(item.Name) + ' cardContent cardContent-shadow itemAction"><span class="cardImageIcon material-icons album"></span></button></div>';
}
}
@ -589,7 +589,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
require(['playlistEditor'], function (playlistEditor) {
getSaveablePlaylistItems().then(function (items) {
var serverId = items.length ? items[0].ServerId : ApiClient.serverId();
new playlistEditor().show({
new playlistEditor.showEditor({
items: items.map(function (i) {
return i.Id;
}),

View file

@ -43,7 +43,7 @@ define(['layoutManager', 'globalize', 'require', 'events', 'browser', 'alphaPick
function initAlphaPicker(alphaPickerElement, instance) {
instance.alphaPicker = new AlphaPicker({
instance.alphaPicker = new AlphaPicker.default({
element: alphaPickerElement,
mode: 'keyboard'
});

View file

@ -1,158 +0,0 @@
define(['loading', 'events', 'dialogHelper', 'dom', 'layoutManager', 'scrollHelper', 'globalize', 'require', 'material-icons', 'emby-button', 'paper-icon-button-light', 'emby-input', 'formDialogStyle', 'flexStyles'], function (loading, events, dialogHelper, dom, layoutManager, scrollHelper, globalize, require) {
'use strict';
var currentApiClient;
var currentDlg;
var currentInstance;
function reloadPageWhenServerAvailable(retryCount) {
var apiClient = currentApiClient;
if (!apiClient) {
return;
}
// Don't use apiclient method because we don't want it reporting authentication under the old version
apiClient.getJSON(apiClient.getUrl('System/Info')).then(function (info) {
// If this is back to false, the restart completed
if (!info.IsShuttingDown) {
currentInstance.restarted = true;
dialogHelper.close(currentDlg);
} else {
retryReload(retryCount);
}
}, function () {
retryReload(retryCount);
});
}
function retryReload(retryCount) {
setTimeout(function () {
retryCount = retryCount || 0;
retryCount++;
if (retryCount < 150) {
reloadPageWhenServerAvailable(retryCount);
}
}, 500);
}
function startRestart(instance, apiClient, dlg) {
currentApiClient = apiClient;
currentDlg = dlg;
currentInstance = instance;
apiClient.restartServer().then(function () {
setTimeout(reloadPageWhenServerAvailable, 250);
});
}
function showDialog(instance, options, template) {
var dialogOptions = {
removeOnClose: true,
scrollY: false
};
var enableTvLayout = layoutManager.tv;
if (enableTvLayout) {
dialogOptions.size = 'fullscreen';
}
var dlg = dialogHelper.createDialog(dialogOptions);
var configuredButtons = [];
dlg.classList.add('formDialog');
dlg.innerHTML = globalize.translateHtml(template, 'core');
dlg.classList.add('align-items-center');
dlg.classList.add('justify-items-center');
var formDialogContent = dlg.querySelector('.formDialogContent');
formDialogContent.style['flex-grow'] = 'initial';
if (enableTvLayout) {
formDialogContent.style['max-width'] = '50%';
formDialogContent.style['max-height'] = '60%';
scrollHelper.centerFocus.on(formDialogContent, false);
} else {
formDialogContent.style.maxWidth = (Math.min((configuredButtons.length * 150) + 200, dom.getWindowSize().innerWidth - 50)) + 'px';
dlg.classList.add('dialog-fullscreen-lowres');
}
dlg.querySelector('.formDialogHeaderTitle').innerHTML = globalize.translate('HeaderRestartingServer');
dlg.querySelector('.text').innerHTML = globalize.translate('RestartPleaseWaitMessage');
var i;
var length;
var html = '';
for (i = 0, length = configuredButtons.length; i < length; i++) {
var item = configuredButtons[i];
var autoFocus = i === 0 ? ' autofocus' : '';
var buttonClass = 'btnOption raised formDialogFooterItem formDialogFooterItem-autosize';
if (item.type) {
buttonClass += ' button-' + item.type;
}
html += '<button is="emby-button" type="button" class="' + buttonClass + '" data-id="' + item.id + '"' + autoFocus + '>' + item.name + '</button>';
}
dlg.querySelector('.formDialogFooter').innerHTML = html;
function onButtonClick() {
dialogHelper.close(dlg);
}
var buttons = dlg.querySelectorAll('.btnOption');
for (i = 0, length = buttons.length; i < length; i++) {
buttons[i].addEventListener('click', onButtonClick);
}
var dlgPromise = dialogHelper.open(dlg);
startRestart(instance, options.apiClient, dlg);
return dlgPromise.then(function () {
if (enableTvLayout) {
scrollHelper.centerFocus.off(dlg.querySelector('.formDialogContent'), false);
}
instance.destroy();
loading.hide();
if (instance.restarted) {
events.trigger(instance, 'restarted');
}
});
}
function ServerRestartDialog(options) {
this.options = options;
}
ServerRestartDialog.prototype.show = function () {
var instance = this;
loading.show();
return new Promise(function (resolve, reject) {
require(['text!./../dialog/dialog.template.html'], function (template) {
showDialog(instance, instance.options, template).then(resolve, reject);
});
});
};
ServerRestartDialog.prototype.destroy = function () {
currentApiClient = null;
currentDlg = null;
currentInstance = null;
this.options = null;
};
return ServerRestartDialog;
});

View file

@ -0,0 +1,22 @@
import globalize from 'globalize';
/**
* Helper for handling settings.
* @module components/settingsHelper
*/
export function populateLanguages(select, languages) {
let html = '';
html += "<option value=''>" + globalize.translate('AnyLanguage') + '</option>';
for (let i = 0, length = languages.length; i < length; i++) {
const culture = languages[i];
html += "<option value='" + culture.ThreeLetterISOLanguageName + "'>" + culture.DisplayName + '</option>';
}
select.innerHTML = html;
}
export default {
populateLanguages: populateLanguages
};

View file

@ -1,18 +1,30 @@
define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'globalize', 'loading', 'dom', 'recordingHelper'], function (playbackManager, inputManager, connectionManager, appRouter, globalize, loading, dom, recordingHelper) {
'use strict';
/* eslint-disable indent */
/**
* Module shortcuts.
* @module components/shortcuts
*/
import playbackManager from 'playbackManager';
import inputManager from 'inputManager';
import connectionManager from 'connectionManager';
import appRouter from 'appRouter';
import globalize from 'globalize';
import dom from 'dom';
import recordingHelper from 'recordingHelper';
function playAllFromHere(card, serverId, queue) {
var parent = card.parentNode;
var className = card.classList.length ? ('.' + card.classList[0]) : '';
var cards = parent.querySelectorAll(className + '[data-id]');
const parent = card.parentNode;
const className = card.classList.length ? (`.${card.classList[0]}`) : '';
const cards = parent.querySelectorAll(`${className}[data-id]`);
var ids = [];
const ids = [];
var foundCard = false;
var startIndex;
let foundCard = false;
let startIndex;
for (var i = 0, length = cards.length; i < length; i++) {
for (let i = 0, length = cards.length; i < length; i++) {
if (cards[i] === card) {
foundCard = true;
startIndex = i;
@ -22,12 +34,12 @@ define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'gl
}
}
var itemsContainer = dom.parentWithClass(card, 'itemsContainer');
const itemsContainer = dom.parentWithClass(card, 'itemsContainer');
if (itemsContainer && itemsContainer.fetchData) {
var queryOptions = queue ? { StartIndex: startIndex } : {};
const queryOptions = queue ? { StartIndex: startIndex } : {};
return itemsContainer.fetchData(queryOptions).then(function (result) {
return itemsContainer.fetchData(queryOptions).then(result => {
if (queue) {
return playbackManager.queue({
@ -64,7 +76,7 @@ define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'gl
function showProgramDialog(item) {
require(['recordingCreator'], function (recordingCreator) {
import('recordingCreator').then(({default:recordingCreator}) => {
recordingCreator.show(item.Id, item.ServerId);
});
@ -73,11 +85,11 @@ define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'gl
function getItem(button) {
button = dom.parentWithAttribute(button, 'data-id');
var serverId = button.getAttribute('data-serverid');
var id = button.getAttribute('data-id');
var type = button.getAttribute('data-type');
const serverId = button.getAttribute('data-serverid');
const id = button.getAttribute('data-id');
const type = button.getAttribute('data-type');
var apiClient = connectionManager.getApiClient(serverId);
const apiClient = connectionManager.getApiClient(serverId);
if (type === 'Timer') {
return apiClient.getLiveTvTimer(id);
@ -99,19 +111,19 @@ define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'gl
function showContextMenu(card, options) {
getItem(card).then(function (item) {
getItem(card).then(item => {
var playlistId = card.getAttribute('data-playlistid');
var collectionId = card.getAttribute('data-collectionid');
const playlistId = card.getAttribute('data-playlistid');
const collectionId = card.getAttribute('data-collectionid');
if (playlistId) {
var elem = dom.parentWithAttribute(card, 'data-playlistitemid');
const elem = dom.parentWithAttribute(card, 'data-playlistitemid');
item.PlaylistItemId = elem ? elem.getAttribute('data-playlistitemid') : null;
}
require(['itemContextMenu'], function (itemContextMenu) {
import('itemContextMenu').then(({default: itemContextMenu}) => {
connectionManager.getApiClient(item.ServerId).getCurrentUser().then(function (user) {
connectionManager.getApiClient(item.ServerId).getCurrentUser().then(user => {
itemContextMenu.show(Object.assign({
item: item,
play: true,
@ -122,9 +134,7 @@ define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'gl
collectionId: collectionId,
user: user
}, options || {})).then(function (result) {
var itemsContainer;
}, options || {})).then(result => {
if (result.command === 'playallfromhere' || result.command === 'queueallfromhere') {
executeAction(card, options.positionTo, result.command);
@ -157,9 +167,9 @@ define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'gl
function showPlayMenu(card, target) {
var item = getItemInfoFromCard(card);
const item = getItemInfoFromCard(card);
require(['playMenu'], function (playMenu) {
import('playMenu').then(({default: playMenu}) => {
playMenu.show({
@ -170,7 +180,7 @@ define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'gl
}
function sendToast(text) {
require(['toast'], function (toast) {
import('toast').then(({default: toast}) => {
toast(text);
});
}
@ -179,19 +189,19 @@ define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'gl
target = target || card;
var id = card.getAttribute('data-id');
let id = card.getAttribute('data-id');
if (!id) {
card = dom.parentWithAttribute(card, 'data-id');
id = card.getAttribute('data-id');
}
var item = getItemInfoFromCard(card);
const item = getItemInfoFromCard(card);
var serverId = item.ServerId;
var type = item.Type;
const serverId = item.ServerId;
const type = item.Type;
var playableItemId = type === 'Program' ? item.ChannelId : item.Id;
const playableItemId = type === 'Program' ? item.ChannelId : item.Id;
if (item.MediaType === 'Photo' && action === 'link') {
action = 'play';
@ -213,7 +223,7 @@ define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'gl
});
} else if (action === 'play' || action === 'resume') {
var startPositionTicks = parseInt(card.getAttribute('data-positionticks') || '0');
const startPositionTicks = parseInt(card.getAttribute('data-positionticks') || '0');
playbackManager.play({
ids: [playableItemId],
@ -244,7 +254,7 @@ define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'gl
onRecordCommand(serverId, id, type, card.getAttribute('data-timerid'), card.getAttribute('data-seriestimerid'));
} else if (action === 'menu') {
var options = target.getAttribute('data-playoptions') === 'false' ?
const options = target.getAttribute('data-playoptions') === 'false' ?
{
shuffle: false,
instantMix: false,
@ -261,7 +271,7 @@ define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'gl
} else if (action === 'playmenu') {
showPlayMenu(card, target);
} else if (action === 'edit') {
getItem(target).then(function (item) {
getItem(target).then(item => {
editItem(item, serverId);
});
} else if (action === 'playtrailer') {
@ -270,9 +280,9 @@ define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'gl
getItem(target).then(addToPlaylist);
} else if (action === 'custom') {
var customAction = target.getAttribute('data-customaction');
const customAction = target.getAttribute('data-customaction');
card.dispatchEvent(new CustomEvent('action-' + customAction, {
card.dispatchEvent(new CustomEvent(`action-${customAction}`, {
detail: {
playlistItemId: card.getAttribute('data-playlistitemid')
},
@ -283,7 +293,7 @@ define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'gl
}
function addToPlaylist(item) {
require(['playlistEditor'], function (playlistEditor) {
import('playlistEditor').then(({default: playlistEditor}) => {
new playlistEditor().show({
items: [item.Id],
@ -295,35 +305,35 @@ define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'gl
function playTrailer(item) {
var apiClient = connectionManager.getApiClient(item.ServerId);
const apiClient = connectionManager.getApiClient(item.ServerId);
apiClient.getLocalTrailers(apiClient.getCurrentUserId(), item.Id).then(function (trailers) {
apiClient.getLocalTrailers(apiClient.getCurrentUserId(), item.Id).then(trailers => {
playbackManager.play({ items: trailers });
});
}
function editItem(item, serverId) {
var apiClient = connectionManager.getApiClient(serverId);
const apiClient = connectionManager.getApiClient(serverId);
return new Promise(function (resolve, reject) {
return new Promise((resolve, reject) => {
var serverId = apiClient.serverInfo().Id;
const serverId = apiClient.serverInfo().Id;
if (item.Type === 'Timer') {
if (item.ProgramId) {
require(['recordingCreator'], function (recordingCreator) {
import('recordingCreator').then(({default: recordingCreator}) => {
recordingCreator.show(item.ProgramId, serverId).then(resolve, reject);
});
} else {
require(['recordingEditor'], function (recordingEditor) {
import('recordingEditor').then(({default: recordingEditor}) => {
recordingEditor.show(item.Id, serverId).then(resolve, reject);
});
}
} else {
require(['metadataEditor'], function (metadataEditor) {
import('metadataEditor').then(({default: metadataEditor}) => {
metadataEditor.show(item.Id, serverId).then(resolve, reject);
});
@ -335,19 +345,19 @@ define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'gl
if (type === 'Program' || timerId || seriesTimerId) {
var programId = type === 'Program' ? id : null;
const programId = type === 'Program' ? id : null;
recordingHelper.toggleRecording(serverId, programId, timerId, seriesTimerId);
}
}
function onClick(e) {
export function onClick(e) {
var card = dom.parentWithClass(e.target, 'itemAction');
const card = dom.parentWithClass(e.target, 'itemAction');
if (card) {
var actionElement = card;
var action = actionElement.getAttribute('data-action');
let actionElement = card;
let action = actionElement.getAttribute('data-action');
if (!action) {
actionElement = dom.parentWithAttribute(actionElement, 'data-action');
@ -368,12 +378,12 @@ define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'gl
function onCommand(e) {
var cmd = e.detail.command;
const cmd = e.detail.command;
if (cmd === 'play' || cmd === 'resume' || cmd === 'record' || cmd === 'menu' || cmd === 'info') {
var target = e.target;
var card = dom.parentWithClass(target, 'itemAction') || dom.parentWithAttribute(target, 'data-id');
const target = e.target;
const card = dom.parentWithClass(target, 'itemAction') || dom.parentWithAttribute(target, 'data-id');
if (card) {
e.preventDefault();
@ -383,7 +393,7 @@ define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'gl
}
}
function on(context, options) {
export function on(context, options) {
options = options || {};
@ -396,7 +406,7 @@ define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'gl
}
}
function off(context, options) {
export function off(context, options) {
options = options || {};
context.removeEventListener('click', onClick);
@ -406,23 +416,24 @@ define(['playbackManager', 'inputManager', 'connectionManager', 'appRouter', 'gl
}
}
function getShortcutAttributesHtml(item, serverId) {
export function getShortcutAttributesHtml(item, serverId) {
var html = 'data-id="' + item.Id + '" data-serverid="' + (serverId || item.ServerId) + '" data-type="' + item.Type + '" data-mediatype="' + item.MediaType + '" data-channelid="' + item.ChannelId + '" data-isfolder="' + item.IsFolder + '"';
let html = `data-id="${item.Id}" data-serverid="${serverId || item.ServerId}" data-type="${item.Type}" data-mediatype="${item.MediaType}" data-channelid="${item.ChannelId}" data-isfolder="${item.IsFolder}"`;
var collectionType = item.CollectionType;
const collectionType = item.CollectionType;
if (collectionType) {
html += ' data-collectiontype="' + collectionType + '"';
html += ` data-collectiontype="${collectionType}"`;
}
return html;
}
return {
on: on,
off: off,
onClick: onClick,
getShortcutAttributesHtml: getShortcutAttributesHtml
};
/* eslint-enable indent */
export default {
on: on,
off: off,
onClick: onClick,
getShortcutAttributesHtml: getShortcutAttributesHtml
};
});

View file

@ -84,7 +84,7 @@ define(['apphost', 'userSettings', 'browser', 'events', 'backdrop', 'globalize',
currentSound = null;
}
backdrop.clear();
backdrop.clearBackdrop();
}
function onThemeLoaded() {

View file

@ -1,159 +1,154 @@
define([], function () {
'use strict';
/**
* Subtitle settings visual helper.
* @module components/subtitleSettings/subtitleAppearanceHelper
*/
function getTextStyles(settings, isCue) {
function getTextStyles(settings, isCue) {
var list = [];
let list = [];
if (isCue) {
switch (settings.textSize || '') {
if (isCue) {
switch (settings.textSize || '') {
case 'smaller':
list.push({ name: 'font-size', value: '.5em' });
break;
case 'small':
list.push({ name: 'font-size', value: '.7em' });
break;
case 'large':
list.push({ name: 'font-size', value: '1.3em' });
break;
case 'larger':
list.push({ name: 'font-size', value: '1.72em' });
break;
case 'extralarge':
list.push({ name: 'font-size', value: '2em' });
break;
default:
case 'medium':
break;
}
} else {
switch (settings.textSize || '') {
case 'smaller':
list.push({ name: 'font-size', value: '.8em' });
break;
case 'small':
list.push({ name: 'font-size', value: 'inherit' });
break;
case 'larger':
list.push({ name: 'font-size', value: '2em' });
break;
case 'extralarge':
list.push({ name: 'font-size', value: '2.2em' });
break;
case 'large':
list.push({ name: 'font-size', value: '1.72em' });
break;
default:
case 'medium':
list.push({ name: 'font-size', value: '1.36em' });
break;
}
}
switch (settings.dropShadow || '') {
case 'raised':
list.push({ name: 'text-shadow', value: '-1px -1px white, 0px -1px white, -1px 0px white, 1px 1px black, 0px 1px black, 1px 0px black' });
case 'smaller':
list.push({ name: 'font-size', value: '.5em' });
break;
case 'depressed':
list.push({ name: 'text-shadow', value: '1px 1px white, 0px 1px white, 1px 0px white, -1px -1px black, 0px -1px black, -1px 0px black' });
case 'small':
list.push({ name: 'font-size', value: '.7em' });
break;
case 'uniform':
list.push({ name: 'text-shadow', value: '-1px 0px #000000, 0px 1px #000000, 1px 0px #000000, 0px -1px #000000' });
case 'large':
list.push({ name: 'font-size', value: '1.3em' });
break;
case 'none':
list.push({ name: 'text-shadow', value: 'none' });
case 'larger':
list.push({ name: 'font-size', value: '1.72em' });
break;
case 'extralarge':
list.push({ name: 'font-size', value: '2em' });
break;
default:
case 'dropshadow':
list.push({ name: 'text-shadow', value: '#000000 0px 0px 7px' });
case 'medium':
break;
}
} else {
switch (settings.textSize || '') {
var background = settings.textBackground || 'transparent';
if (background) {
list.push({ name: 'background-color', value: background });
}
var textColor = settings.textColor || '#ffffff';
if (textColor) {
list.push({ name: 'color', value: textColor });
}
switch (settings.font || '') {
case 'typewriter':
list.push({ name: 'font-family', value: '"Courier New",monospace' });
list.push({ name: 'font-variant', value: 'none' });
case 'smaller':
list.push({ name: 'font-size', value: '.8em' });
break;
case 'print':
list.push({ name: 'font-family', value: 'Georgia,Times New Roman,Arial,Helvetica,serif' });
list.push({ name: 'font-variant', value: 'none' });
case 'small':
list.push({ name: 'font-size', value: 'inherit' });
break;
case 'console':
list.push({ name: 'font-family', value: 'Consolas,Lucida Console,Menlo,Monaco,monospace' });
list.push({ name: 'font-variant', value: 'none' });
case 'larger':
list.push({ name: 'font-size', value: '2em' });
break;
case 'cursive':
list.push({ name: 'font-family', value: 'Lucida Handwriting,Brush Script MT,Segoe Script,cursive,Quintessential,system-ui,-apple-system,BlinkMacSystemFont,sans-serif' });
list.push({ name: 'font-variant', value: 'none' });
case 'extralarge':
list.push({ name: 'font-size', value: '2.2em' });
break;
case 'casual':
list.push({ name: 'font-family', value: 'Gabriola,Segoe Print,Comic Sans MS,Chalkboard,Short Stack,system-ui,-apple-system,BlinkMacSystemFont,sans-serif' });
list.push({ name: 'font-variant', value: 'none' });
break;
case 'smallcaps':
list.push({ name: 'font-family', value: 'Copperplate Gothic,Copperplate Gothic Bold,Copperplate,system-ui,-apple-system,BlinkMacSystemFont,sans-serif' });
list.push({ name: 'font-variant', value: 'small-caps' });
case 'large':
list.push({ name: 'font-size', value: '1.72em' });
break;
default:
list.push({ name: 'font-family', value: 'inherit' });
list.push({ name: 'font-variant', value: 'none' });
case 'medium':
list.push({ name: 'font-size', value: '1.36em' });
break;
}
return list;
}
function getWindowStyles(settings) {
switch (settings.dropShadow || '') {
return [];
case 'raised':
list.push({ name: 'text-shadow', value: '-1px -1px white, 0px -1px white, -1px 0px white, 1px 1px black, 0px 1px black, 1px 0px black' });
break;
case 'depressed':
list.push({ name: 'text-shadow', value: '1px 1px white, 0px 1px white, 1px 0px white, -1px -1px black, 0px -1px black, -1px 0px black' });
break;
case 'uniform':
list.push({ name: 'text-shadow', value: '-1px 0px #000000, 0px 1px #000000, 1px 0px #000000, 0px -1px #000000' });
break;
case 'none':
list.push({ name: 'text-shadow', value: 'none' });
break;
default:
case 'dropshadow':
list.push({ name: 'text-shadow', value: '#000000 0px 0px 7px' });
break;
}
function getStyles(settings, isCue) {
return {
text: getTextStyles(settings, isCue),
window: getWindowStyles(settings)
};
const background = settings.textBackground || 'transparent';
if (background) {
list.push({ name: 'background-color', value: background });
}
function applyStyleList(styles, elem) {
for (var i = 0, length = styles.length; i < length; i++) {
var style = styles[i];
elem.style[style.name] = style.value;
}
const textColor = settings.textColor || '#ffffff';
if (textColor) {
list.push({ name: 'color', value: textColor });
}
function applyStyles(elements, appearanceSettings) {
switch (settings.font || '') {
var styles = getStyles(appearanceSettings);
if (elements.text) {
applyStyleList(styles.text, elements.text);
}
if (elements.window) {
applyStyleList(styles.window, elements.window);
}
case 'typewriter':
list.push({ name: 'font-family', value: '"Courier New",monospace' });
list.push({ name: 'font-variant', value: 'none' });
break;
case 'print':
list.push({ name: 'font-family', value: 'Georgia,Times New Roman,Arial,Helvetica,serif' });
list.push({ name: 'font-variant', value: 'none' });
break;
case 'console':
list.push({ name: 'font-family', value: 'Consolas,Lucida Console,Menlo,Monaco,monospace' });
list.push({ name: 'font-variant', value: 'none' });
break;
case 'cursive':
list.push({ name: 'font-family', value: 'Lucida Handwriting,Brush Script MT,Segoe Script,cursive,Quintessential,system-ui,-apple-system,BlinkMacSystemFont,sans-serif' });
list.push({ name: 'font-variant', value: 'none' });
break;
case 'casual':
list.push({ name: 'font-family', value: 'Gabriola,Segoe Print,Comic Sans MS,Chalkboard,Short Stack,system-ui,-apple-system,BlinkMacSystemFont,sans-serif' });
list.push({ name: 'font-variant', value: 'none' });
break;
case 'smallcaps':
list.push({ name: 'font-family', value: 'Copperplate Gothic,Copperplate Gothic Bold,Copperplate,system-ui,-apple-system,BlinkMacSystemFont,sans-serif' });
list.push({ name: 'font-variant', value: 'small-caps' });
break;
default:
list.push({ name: 'font-family', value: 'inherit' });
list.push({ name: 'font-variant', value: 'none' });
break;
}
return list;
}
export function getStyles(settings, isCue) {
return {
getStyles: getStyles,
applyStyles: applyStyles
text: getTextStyles(settings, isCue),
window: []
};
});
}
function applyStyleList(styles, elem) {
for (let i = 0, length = styles.length; i < length; i++) {
let style = styles[i];
elem.style[style.name] = style.value;
}
}
export function applyStyles(elements, appearanceSettings) {
let styles = getStyles(appearanceSettings);
if (elements.text) {
applyStyleList(styles.text, elements.text);
}
if (elements.window) {
applyStyleList(styles.window, elements.window);
}
}
export default {
getStyles: getStyles,
applyStyles: applyStyles
};

View file

@ -1,109 +1,212 @@
define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loading', 'connectionManager', 'subtitleAppearanceHelper', 'dom', 'events', 'listViewStyle', 'emby-select', 'emby-input', 'emby-checkbox', 'flexStyles'], function (require, globalize, appSettings, appHost, focusManager, loading, connectionManager, subtitleAppearanceHelper, dom, events) {
'use strict';
import require from 'require';
import globalize from 'globalize';
import appHost from 'apphost';
import appSettings from 'appSettings';
import focusManager from 'focusManager';
import loading from 'loading';
import connectionManager from 'connectionManager';
import subtitleAppearanceHelper from 'subtitleAppearanceHelper';
import settingsHelper from 'settingsHelper';
import dom from 'dom';
import events from 'events';
import 'listViewStyle';
import 'emby-select';
import 'emby-input';
import 'emby-checkbox';
import 'flexStyles';
function populateLanguages(select, languages) {
var html = '';
/**
* Subtitle settings.
* @module components/subtitleSettings/subtitleSettings
*/
html += "<option value=''>" + globalize.translate('AnyLanguage') + '</option>';
for (var i = 0, length = languages.length; i < length; i++) {
var culture = languages[i];
html += "<option value='" + culture.ThreeLetterISOLanguageName + "'>" + culture.DisplayName + '</option>';
function getSubtitleAppearanceObject(context) {
let appearanceSettings = {};
appearanceSettings.textSize = context.querySelector('#selectTextSize').value;
appearanceSettings.dropShadow = context.querySelector('#selectDropShadow').value;
appearanceSettings.font = context.querySelector('#selectFont').value;
appearanceSettings.textBackground = context.querySelector('#inputTextBackground').value;
appearanceSettings.textColor = context.querySelector('#inputTextColor').value;
return appearanceSettings;
}
function loadForm(context, user, userSettings, appearanceSettings, apiClient) {
apiClient.getCultures().then(function (allCultures) {
if (appHost.supports('subtitleburnsettings') && user.Policy.EnableVideoPlaybackTranscoding) {
context.querySelector('.fldBurnIn').classList.remove('hide');
}
select.innerHTML = html;
}
let selectSubtitleLanguage = context.querySelector('#selectSubtitleLanguage');
function getSubtitleAppearanceObject(context) {
var appearanceSettings = {};
settingsHelper.populateLanguages(selectSubtitleLanguage, allCultures);
appearanceSettings.textSize = context.querySelector('#selectTextSize').value;
appearanceSettings.dropShadow = context.querySelector('#selectDropShadow').value;
appearanceSettings.font = context.querySelector('#selectFont').value;
appearanceSettings.textBackground = context.querySelector('#inputTextBackground').value;
appearanceSettings.textColor = context.querySelector('#inputTextColor').value;
selectSubtitleLanguage.value = user.Configuration.SubtitleLanguagePreference || '';
context.querySelector('#selectSubtitlePlaybackMode').value = user.Configuration.SubtitleMode || '';
return appearanceSettings;
}
context.querySelector('#selectSubtitlePlaybackMode').dispatchEvent(new CustomEvent('change', {}));
function loadForm(context, user, userSettings, appearanceSettings, apiClient) {
context.querySelector('#selectTextSize').value = appearanceSettings.textSize || '';
context.querySelector('#selectDropShadow').value = appearanceSettings.dropShadow || '';
context.querySelector('#inputTextBackground').value = appearanceSettings.textBackground || 'transparent';
context.querySelector('#inputTextColor').value = appearanceSettings.textColor || '#ffffff';
context.querySelector('#selectFont').value = appearanceSettings.font || '';
apiClient.getCultures().then(function (allCultures) {
context.querySelector('#selectSubtitleBurnIn').value = appSettings.get('subtitleburnin') || '';
if (appHost.supports('subtitleburnsettings') && user.Policy.EnableVideoPlaybackTranscoding) {
context.querySelector('.fldBurnIn').classList.remove('hide');
}
onAppearanceFieldChange({
target: context.querySelector('#selectTextSize')
});
var selectSubtitleLanguage = context.querySelector('#selectSubtitleLanguage');
loading.hide();
});
}
populateLanguages(selectSubtitleLanguage, allCultures);
function saveUser(context, user, userSettingsInstance, appearanceKey, apiClient) {
selectSubtitleLanguage.value = user.Configuration.SubtitleLanguagePreference || '';
context.querySelector('#selectSubtitlePlaybackMode').value = user.Configuration.SubtitleMode || '';
let appearanceSettings = userSettingsInstance.getSubtitleAppearanceSettings(appearanceKey);
appearanceSettings = Object.assign(appearanceSettings, getSubtitleAppearanceObject(context));
context.querySelector('#selectSubtitlePlaybackMode').dispatchEvent(new CustomEvent('change', {}));
userSettingsInstance.setSubtitleAppearanceSettings(appearanceSettings, appearanceKey);
context.querySelector('#selectTextSize').value = appearanceSettings.textSize || '';
context.querySelector('#selectDropShadow').value = appearanceSettings.dropShadow || '';
context.querySelector('#inputTextBackground').value = appearanceSettings.textBackground || 'transparent';
context.querySelector('#inputTextColor').value = appearanceSettings.textColor || '#ffffff';
context.querySelector('#selectFont').value = appearanceSettings.font || '';
user.Configuration.SubtitleLanguagePreference = context.querySelector('#selectSubtitleLanguage').value;
user.Configuration.SubtitleMode = context.querySelector('#selectSubtitlePlaybackMode').value;
context.querySelector('#selectSubtitleBurnIn').value = appSettings.get('subtitleburnin') || '';
return apiClient.updateUserConfiguration(user.Id, user.Configuration);
}
onAppearanceFieldChange({
target: context.querySelector('#selectTextSize')
});
function save(instance, context, userId, userSettings, apiClient, enableSaveConfirmation) {
loading.show();
appSettings.set('subtitleburnin', context.querySelector('#selectSubtitleBurnIn').value);
apiClient.getUser(userId).then(function (user) {
saveUser(context, user, userSettings, instance.appearanceKey, apiClient).then(function () {
loading.hide();
if (enableSaveConfirmation) {
require(['toast'], function (toast) {
toast(globalize.translate('SettingsSaved'));
});
}
events.trigger(instance, 'saved');
}, function () {
loading.hide();
});
});
}
function onSubtitleModeChange(e) {
let view = dom.parentWithClass(e.target, 'subtitlesettings');
let subtitlesHelp = view.querySelectorAll('.subtitlesHelp');
for (let i = 0, length = subtitlesHelp.length; i < length; i++) {
subtitlesHelp[i].classList.add('hide');
}
view.querySelector('.subtitles' + this.value + 'Help').classList.remove('hide');
}
function onAppearanceFieldChange(e) {
let view = dom.parentWithClass(e.target, 'subtitlesettings');
let appearanceSettings = getSubtitleAppearanceObject(view);
let elements = {
window: view.querySelector('.subtitleappearance-preview-window'),
text: view.querySelector('.subtitleappearance-preview-text')
};
subtitleAppearanceHelper.applyStyles(elements, appearanceSettings);
}
function embed(options, self) {
require(['text!./subtitlesettings.template.html'], function (template) {
options.element.classList.add('subtitlesettings');
options.element.innerHTML = globalize.translateDocument(template, 'core');
options.element.querySelector('form').addEventListener('submit', self.onSubmit.bind(self));
options.element.querySelector('#selectSubtitlePlaybackMode').addEventListener('change', onSubtitleModeChange);
options.element.querySelector('#selectTextSize').addEventListener('change', onAppearanceFieldChange);
options.element.querySelector('#selectDropShadow').addEventListener('change', onAppearanceFieldChange);
options.element.querySelector('#selectFont').addEventListener('change', onAppearanceFieldChange);
options.element.querySelector('#inputTextColor').addEventListener('change', onAppearanceFieldChange);
options.element.querySelector('#inputTextBackground').addEventListener('change', onAppearanceFieldChange);
if (options.enableSaveButton) {
options.element.querySelector('.btnSave').classList.remove('hide');
}
if (appHost.supports('subtitleappearancesettings')) {
options.element.querySelector('.subtitleAppearanceSection').classList.remove('hide');
}
self.loadData();
if (options.autoFocus) {
focusManager.autoFocus(options.element);
}
});
}
export class SubtitleSettings {
constructor(options) {
this.options = options;
embed(options, this);
}
function saveUser(context, user, userSettingsInstance, appearanceKey, apiClient) {
var appearanceSettings = userSettingsInstance.getSubtitleAppearanceSettings(appearanceKey);
appearanceSettings = Object.assign(appearanceSettings, getSubtitleAppearanceObject(context));
userSettingsInstance.setSubtitleAppearanceSettings(appearanceSettings, appearanceKey);
user.Configuration.SubtitleLanguagePreference = context.querySelector('#selectSubtitleLanguage').value;
user.Configuration.SubtitleMode = context.querySelector('#selectSubtitlePlaybackMode').value;
return apiClient.updateUserConfiguration(user.Id, user.Configuration);
}
function save(instance, context, userId, userSettings, apiClient, enableSaveConfirmation) {
loadData() {
let self = this;
let context = self.options.element;
loading.show();
appSettings.set('subtitleburnin', context.querySelector('#selectSubtitleBurnIn').value);
let userId = self.options.userId;
let apiClient = connectionManager.getApiClient(self.options.serverId);
let userSettings = self.options.userSettings;
apiClient.getUser(userId).then(function (user) {
userSettings.setUserInfo(userId, apiClient).then(function () {
self.dataLoaded = true;
saveUser(context, user, userSettings, instance.appearanceKey, apiClient).then(function () {
let appearanceSettings = userSettings.getSubtitleAppearanceSettings(self.options.appearanceKey);
loading.hide();
if (enableSaveConfirmation) {
require(['toast'], function (toast) {
toast(globalize.translate('SettingsSaved'));
});
}
events.trigger(instance, 'saved');
}, function () {
loading.hide();
loadForm(context, user, userSettings, appearanceSettings, apiClient);
});
});
}
function onSubmit(e) {
var self = this;
var apiClient = connectionManager.getApiClient(self.options.serverId);
var userId = self.options.userId;
var userSettings = self.options.userSettings;
submit() {
this.onSubmit(null);
}
destroy() {
this.options = null;
}
onSubmit(e) {
const self = this;
let apiClient = connectionManager.getApiClient(self.options.serverId);
let userId = self.options.userId;
let userSettings = self.options.userSettings;
userSettings.setUserInfo(userId, apiClient).then(function () {
var enableSaveConfirmation = self.options.enableSaveConfirmation;
let enableSaveConfirmation = self.options.enableSaveConfirmation;
save(self, self.options.element, userId, userSettings, apiClient, enableSaveConfirmation);
});
@ -111,103 +214,8 @@ define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loadi
if (e) {
e.preventDefault();
}
return false;
}
}
function onSubtitleModeChange(e) {
var view = dom.parentWithClass(e.target, 'subtitlesettings');
var subtitlesHelp = view.querySelectorAll('.subtitlesHelp');
for (var i = 0, length = subtitlesHelp.length; i < length; i++) {
subtitlesHelp[i].classList.add('hide');
}
view.querySelector('.subtitles' + this.value + 'Help').classList.remove('hide');
}
function onAppearanceFieldChange(e) {
var view = dom.parentWithClass(e.target, 'subtitlesettings');
var appearanceSettings = getSubtitleAppearanceObject(view);
var elements = {
window: view.querySelector('.subtitleappearance-preview-window'),
text: view.querySelector('.subtitleappearance-preview-text')
};
subtitleAppearanceHelper.applyStyles(elements, appearanceSettings);
}
function embed(options, self) {
require(['text!./subtitlesettings.template.html'], function (template) {
options.element.classList.add('subtitlesettings');
options.element.innerHTML = globalize.translateDocument(template, 'core');
options.element.querySelector('form').addEventListener('submit', onSubmit.bind(self));
options.element.querySelector('#selectSubtitlePlaybackMode').addEventListener('change', onSubtitleModeChange);
options.element.querySelector('#selectTextSize').addEventListener('change', onAppearanceFieldChange);
options.element.querySelector('#selectDropShadow').addEventListener('change', onAppearanceFieldChange);
options.element.querySelector('#selectFont').addEventListener('change', onAppearanceFieldChange);
options.element.querySelector('#inputTextColor').addEventListener('change', onAppearanceFieldChange);
options.element.querySelector('#inputTextBackground').addEventListener('change', onAppearanceFieldChange);
if (options.enableSaveButton) {
options.element.querySelector('.btnSave').classList.remove('hide');
}
if (appHost.supports('subtitleappearancesettings')) {
options.element.querySelector('.subtitleAppearanceSection').classList.remove('hide');
}
self.loadData();
if (options.autoFocus) {
focusManager.autoFocus(options.element);
}
});
}
function SubtitleSettings(options) {
this.options = options;
embed(options, this);
}
SubtitleSettings.prototype.loadData = function () {
var self = this;
var context = self.options.element;
loading.show();
var userId = self.options.userId;
var apiClient = connectionManager.getApiClient(self.options.serverId);
var userSettings = self.options.userSettings;
apiClient.getUser(userId).then(function (user) {
userSettings.setUserInfo(userId, apiClient).then(function () {
self.dataLoaded = true;
var appearanceSettings = userSettings.getSubtitleAppearanceSettings(self.options.appearanceKey);
loadForm(context, user, userSettings, appearanceSettings, apiClient);
});
});
};
SubtitleSettings.prototype.submit = function () {
onSubmit.call(this);
};
SubtitleSettings.prototype.destroy = function () {
this.options = null;
};
return SubtitleSettings;
});
export default SubtitleSettings;

View file

@ -65,8 +65,6 @@ class TimeSyncManager {
this.pings = 0; // number of pings
this.measurement = null; // current time sync
this.measurements = [];
this.startPing();
}
/**

View file

@ -51,7 +51,7 @@ define(['playbackManager', 'userSettings', 'alphaPicker', 'alphaNumericShortcuts
instance.itemsContainer = view.querySelector('.itemsContainer');
instance.alphaPicker = new AlphaPicker({
instance.alphaPicker = new AlphaPicker.default({
element: instance.alphaPickerElement,
itemsContainer: instance.itemsContainer,
itemClass: 'card'

View file

@ -95,7 +95,7 @@ define(['backdrop', 'mainTabsManager', 'layoutManager', 'emby-tabs'], function (
TabbedView.prototype.onResume = function (options) {
this.setTitle();
backdrop.clear();
backdrop.clearBackdrop();
var currentTabController = this.currentTabController;

View file

@ -139,7 +139,7 @@ define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'emby-input', 'listVi
var page = $(e.target).parents('.xmltvForm')[0];
require(['directorybrowser'], function (directoryBrowser) {
var picker = new directoryBrowser();
var picker = new directoryBrowser.default();
picker.show({
includeFiles: true,
callback: function (path) {

View file

@ -1,9 +1,15 @@
define(['datetime', 'loading', 'libraryMenu', 'dom', 'globalize', 'emby-button'], function (datetime, loading, libraryMenu, dom, globalize) {
'use strict';
import datetime from 'datetime';
import loading from 'loading';
import libraryMenu from 'libraryMenu';
import dom from 'dom';
import globalize from 'globalize';
import 'emby-button';
/* eslint-disable indent */
function revoke(page, key) {
require(['confirm'], function (confirm) {
confirm.default(globalize.translate('MessageConfirmRevokeApiKey'), globalize.translate('HeaderConfirmRevokeApiKey')).then(function () {
import('confirm').then(({default: confirm}) => {
confirm(globalize.translate('MessageConfirmRevokeApiKey'), globalize.translate('HeaderConfirmRevokeApiKey')).then(function () {
loading.show();
ApiClient.ajax({
type: 'DELETE',
@ -16,8 +22,8 @@ define(['datetime', 'loading', 'libraryMenu', 'dom', 'globalize', 'emby-button']
}
function renderKeys(page, keys) {
var rows = keys.map(function (item) {
var html = '';
const rows = keys.map(function (item) {
let html = '';
html += '<tr class="detailTableBodyRow detailTableBodyRow-shaded">';
html += '<td class="detailTableBodyCell">';
html += '<button type="button" is="emby-button" data-token="' + item.AccessToken + '" class="raised raised-mini btnRevoke" data-mini="true" title="' + globalize.translate('ButtonRevoke') + '" style="margin:0;">' + globalize.translate('ButtonRevoke') + '</button>';
@ -29,7 +35,7 @@ define(['datetime', 'loading', 'libraryMenu', 'dom', 'globalize', 'emby-button']
html += item.AppName || '';
html += '</td>';
html += '<td class="detailTableBodyCell" style="vertical-align:middle;">';
var date = datetime.parseISO8601Date(item.DateCreated, true);
const date = datetime.parseISO8601Date(item.DateCreated, true);
html += datetime.toLocaleDateString(date) + ' ' + datetime.getDisplayTime(date);
html += '</td>';
return html += '</tr>';
@ -46,8 +52,8 @@ define(['datetime', 'loading', 'libraryMenu', 'dom', 'globalize', 'emby-button']
}
function showNewKeyPrompt(page) {
require(['prompt'], function (prompt) {
prompt.default({
import('prompt').then(({default: prompt}) => {
prompt({
title: globalize.translate('HeaderNewApiKey'),
label: globalize.translate('LabelAppName'),
description: globalize.translate('LabelAppNameExample')
@ -65,12 +71,12 @@ define(['datetime', 'loading', 'libraryMenu', 'dom', 'globalize', 'emby-button']
}
pageIdOn('pageinit', 'apiKeysPage', function () {
var page = this;
const page = this;
page.querySelector('.btnNewKey').addEventListener('click', function () {
showNewKeyPrompt(page);
});
page.querySelector('.tblApiKeys').addEventListener('click', function (e) {
var btnRevoke = dom.parentWithClass(e.target, 'btnRevoke');
const btnRevoke = dom.parentWithClass(e.target, 'btnRevoke');
if (btnRevoke) {
revoke(page, btnRevoke.getAttribute('data-token'));
@ -80,4 +86,5 @@ define(['datetime', 'loading', 'libraryMenu', 'dom', 'globalize', 'emby-button']
pageIdOn('pagebeforeshow', 'apiKeysPage', function () {
loadData(this);
});
});
/* eslint-enable indent */

View file

@ -1,11 +1,31 @@
define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globalize', 'date-fns', 'dfnshelper', 'loading', 'connectionManager', 'playMethodHelper', 'cardBuilder', 'imageLoader', 'components/activitylog', 'scripts/imagehelper', 'indicators', 'listViewStyle', 'emby-button', 'flexStyles', 'emby-button', 'emby-itemscontainer'], function (datetime, events, itemHelper, serverNotifications, dom, globalize, datefns, dfnshelper, loading, connectionManager, playMethodHelper, cardBuilder, imageLoader, ActivityLog, imageHelper, indicators) {
'use strict';
import datetime from 'datetime';
import events from 'events';
import itemHelper from 'itemHelper';
import serverNotifications from 'serverNotifications';
import dom from 'dom';
import globalize from 'globalize';
import * as datefns from 'date-fns';
import dfnshelper from 'dfnshelper';
import loading from 'loading';
import connectionManager from 'connectionManager';
import playMethodHelper from 'playMethodHelper';
import cardBuilder from 'cardBuilder';
import imageLoader from 'imageLoader';
import ActivityLog from 'components/activitylog';
import imageHelper from 'scripts/imagehelper';
import indicators from 'indicators';
import 'listViewStyle';
import 'emby-button';
import 'flexStyles';
import 'emby-itemscontainer';
/* eslint-disable indent */
function showPlaybackInfo(btn, session) {
require(['alert'], function (alert) {
var title;
var text = [];
var displayPlayMethod = playMethodHelper.getDisplayPlayMethod(session);
import('alert').then(({default: alert}) => {
let title;
let text = [];
const displayPlayMethod = playMethodHelper.getDisplayPlayMethod(session);
if (displayPlayMethod === 'DirectStream') {
title = globalize.translate('DirectStreaming');
@ -33,8 +53,8 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
}
function showSendMessageForm(btn, session) {
require(['prompt'], function (prompt) {
prompt.default({
import('prompt').then(({default: prompt}) => {
prompt({
title: globalize.translate('HeaderSendMessage'),
label: globalize.translate('LabelMessageText'),
confirmText: globalize.translate('ButtonSend')
@ -50,8 +70,8 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
}
function showOptionsMenu(btn, session) {
require(['actionsheet'], function (actionsheet) {
var menuItems = [];
import('actionsheet').then(({default: actionsheet}) => {
const menuItems = [];
if (session.ServerId && session.DeviceId !== connectionManager.deviceId()) {
menuItems.push({
@ -84,14 +104,14 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
}
function onActiveDevicesClick(evt) {
var btn = dom.parentWithClass(evt.target, 'sessionCardButton');
const btn = dom.parentWithClass(evt.target, 'sessionCardButton');
if (btn) {
var card = dom.parentWithClass(btn, 'card');
const card = dom.parentWithClass(btn, 'card');
if (card) {
var sessionId = card.id;
var session = (DashboardPage.sessionsList || []).filter(function (dashboardSession) {
const sessionId = card.id;
const session = (DashboardPage.sessionsList || []).filter(function (dashboardSession) {
return 'session' + dashboardSession.Id === sessionId;
})[0];
@ -113,11 +133,11 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
}
function filterSessions(sessions) {
var list = [];
var minActiveDate = new Date().getTime() - 9e5;
const list = [];
const minActiveDate = new Date().getTime() - 9e5;
for (var i = 0, length = sessions.length; i < length; i++) {
var session = sessions[i];
for (let i = 0, length = sessions.length; i < length; i++) {
const session = sessions[i];
if (!session.NowPlayingItem && !session.UserId) {
continue;
@ -139,7 +159,7 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
EnableTotalRecordCount: false,
EnableImageTypes: 'Primary,Thumb,Backdrop'
}).then(function (result) {
var itemsContainer = view.querySelector('.activeRecordingItems');
const itemsContainer = view.querySelector('.activeRecordingItems');
if (!result.Items.length) {
view.querySelector('.activeRecordingsSection').classList.add('hide');
@ -169,7 +189,7 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
function reloadSystemInfo(view, apiClient) {
apiClient.getSystemInfo().then(function (systemInfo) {
view.querySelector('#serverName').innerHTML = globalize.translate('DashboardServerName', systemInfo.ServerName);
var localizedVersion = globalize.translate('DashboardVersionNumber', systemInfo.Version);
let localizedVersion = globalize.translate('DashboardVersionNumber', systemInfo.Version);
if (systemInfo.SystemUpdateLevel !== 'Release') {
localizedVersion += ' ' + systemInfo.SystemUpdateLevel;
@ -205,31 +225,31 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
}
function renderActiveConnections(view, sessions) {
var html = '';
let html = '';
DashboardPage.sessionsList = sessions;
var parentElement = view.querySelector('.activeDevices');
var cardElem = parentElement.querySelector('.card');
const parentElement = view.querySelector('.activeDevices');
const cardElem = parentElement.querySelector('.card');
if (cardElem) {
cardElem.classList.add('deadSession');
}
for (var i = 0, length = sessions.length; i < length; i++) {
var session = sessions[i];
var rowId = 'session' + session.Id;
var elem = view.querySelector('#' + rowId);
for (let i = 0, length = sessions.length; i < length; i++) {
const session = sessions[i];
const rowId = 'session' + session.Id;
const elem = view.querySelector('#' + rowId);
if (elem) {
DashboardPage.updateSession(elem, session);
} else {
var nowPlayingItem = session.NowPlayingItem;
var className = 'scalableCard card activeSession backdropCard backdropCard-scalable';
const nowPlayingItem = session.NowPlayingItem;
const className = 'scalableCard card activeSession backdropCard backdropCard-scalable';
html += '<div class="' + className + '" id="' + rowId + '">';
html += '<div class="cardBox visualCardBox">';
html += '<div class="cardScalable visualCardBox-cardScalable">';
html += '<div class="cardPadder cardPadder-backdrop"></div>';
html += '<div class="cardContent">';
var imgUrl = DashboardPage.getNowPlayingImageUrl(nowPlayingItem);
const imgUrl = DashboardPage.getNowPlayingImageUrl(nowPlayingItem);
if (imgUrl) {
html += '<div class="sessionNowPlayingContent sessionNowPlayingContent-withbackground"';
@ -240,7 +260,7 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
html += '<div class="sessionNowPlayingInnerContent">';
html += '<div class="sessionAppInfo">';
var clientImage = DashboardPage.getClientImage(session);
const clientImage = DashboardPage.getClientImage(session);
if (clientImage) {
html += clientImage;
@ -253,7 +273,7 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
html += '</div>';
html += '<div class="sessionNowPlayingDetails">';
var nowPlayingName = DashboardPage.getNowPlayingName(session);
const nowPlayingName = DashboardPage.getNowPlayingName(session);
html += '<div class="sessionNowPlayingInfo" data-imgsrc="' + nowPlayingName.image + '">';
html += nowPlayingName.html;
html += '</div>';
@ -261,7 +281,7 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
html += '</div>';
if (nowPlayingItem && nowPlayingItem.RunTimeTicks) {
var percent = 100 * (session.PlayState.PositionTicks || 0) / nowPlayingItem.RunTimeTicks;
const percent = 100 * (session.PlayState.PositionTicks || 0) / nowPlayingItem.RunTimeTicks;
html += indicators.getProgressHtml(percent, {
containerClass: 'playbackProgress'
});
@ -273,7 +293,7 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
}
if (session.TranscodingInfo && session.TranscodingInfo.CompletionPercentage) {
var percent = session.TranscodingInfo.CompletionPercentage.toFixed(1);
const percent = session.TranscodingInfo.CompletionPercentage.toFixed(1);
html += indicators.getProgressHtml(percent, {
containerClass: 'transcodingProgress'
});
@ -290,7 +310,7 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
html += '<div class="sessionCardFooter cardFooter">';
html += '<div class="sessionCardButtons flex align-items-center justify-content-center">';
var btnCssClass = session.ServerId && session.NowPlayingItem && session.SupportsRemoteControl ? '' : ' hide';
let btnCssClass = session.ServerId && session.NowPlayingItem && session.SupportsRemoteControl ? '' : ' hide';
const playIcon = session.PlayState.IsPaused ? 'pause' : 'play_arrow';
html += '<button is="paper-icon-button-light" class="sessionCardButton btnSessionPlayPause paper-icon-button-light ' + btnCssClass + '"><span class="material-icons ' + playIcon + '"></span></button>';
@ -308,7 +328,7 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
html += '</div>';
html += '<div class="flex align-items-center justify-content-center">';
var userImage = DashboardPage.getUserImage(session);
const userImage = DashboardPage.getUserImage(session);
html += userImage ? '<div class="activitylogUserPhoto" style="background-image:url(\'' + userImage + "');\"></div>" : '<div style="height:1.71em;"></div>';
html += '<div class="sessionUserName">';
html += DashboardPage.getUsersHtml(session);
@ -322,7 +342,7 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
}
parentElement.insertAdjacentHTML('beforeend', html);
var deadSessionElem = parentElement.querySelector('.deadSession');
const deadSessionElem = parentElement.querySelector('.deadSession');
if (deadSessionElem) {
deadSessionElem.parentNode.removeChild(deadSessionElem);
@ -330,7 +350,7 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
}
function renderRunningTasks(view, tasks) {
var html = '';
let html = '';
tasks = tasks.filter(function (task) {
if ('Idle' != task.State) {
return !task.IsHidden;
@ -345,13 +365,13 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
view.querySelector('.runningTasksContainer').classList.add('hide');
}
for (var i = 0, length = tasks.length; i < length; i++) {
var task = tasks[i];
for (let i = 0, length = tasks.length; i < length; i++) {
const task = tasks[i];
html += '<p>';
html += task.Name + '<br/>';
if (task.State === 'Running') {
var progress = (task.CurrentProgressPercentage || 0).toFixed(1);
const progress = (task.CurrentProgressPercentage || 0).toFixed(1);
html += '<progress max="100" value="' + progress + '" title="' + progress + '%">';
html += progress + '%';
html += '</progress>';
@ -377,9 +397,9 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
apiClient.sendMessage('ScheduledTasksInfoStop');
},
getSessionNowPlayingStreamInfo: function (session) {
var html = '';
var showTranscodingInfo = false;
var displayPlayMethod = playMethodHelper.getDisplayPlayMethod(session);
let html = '';
let showTranscodingInfo = false;
const displayPlayMethod = playMethodHelper.getDisplayPlayMethod(session);
if (displayPlayMethod === 'DirectStream') {
html += globalize.translate('DirectStreaming');
@ -396,7 +416,7 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
}
if (showTranscodingInfo) {
var line = [];
const line = [];
if (session.TranscodingInfo) {
if (session.TranscodingInfo.Bitrate) {
@ -428,8 +448,8 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
return html;
},
getSessionNowPlayingTime: function (session) {
var nowPlayingItem = session.NowPlayingItem;
var html = '';
const nowPlayingItem = session.NowPlayingItem;
let html = '';
if (nowPlayingItem) {
if (session.PlayState.PositionTicks) {
@ -453,8 +473,8 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
return session.Client + ' ' + session.ApplicationVersion;
},
getNowPlayingName: function (session) {
var imgUrl = '';
var nowPlayingItem = session.NowPlayingItem;
let imgUrl = '';
const nowPlayingItem = session.NowPlayingItem;
// FIXME: It seems that, sometimes, server sends date in the future, so date-fns displays messages like 'in less than a minute'. We should fix
// how dates are returned by the server when the session is active and show something like 'Active now', instead of past/future sentences
if (!nowPlayingItem) {
@ -464,8 +484,8 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
};
}
var topText = itemHelper.getDisplayName(nowPlayingItem);
var bottomText = '';
let topText = itemHelper.getDisplayName(nowPlayingItem);
let bottomText = '';
if (nowPlayingItem.Artists && nowPlayingItem.Artists.length) {
bottomText = topText;
@ -505,13 +525,13 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
};
},
getUsersHtml: function (session) {
var html = [];
const html = [];
if (session.UserId) {
html.push(session.UserName);
}
for (var i = 0, length = session.AdditionalUsers.length; i < length; i++) {
for (let i = 0, length = session.AdditionalUsers.length; i < length; i++) {
html.push(session.AdditionalUsers[i].UserName);
}
@ -529,7 +549,7 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
},
updateSession: function (row, session) {
row.classList.remove('deadSession');
var nowPlayingItem = session.NowPlayingItem;
const nowPlayingItem = session.NowPlayingItem;
if (nowPlayingItem) {
row.classList.add('playingSession');
@ -549,7 +569,7 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
row.querySelector('.btnSessionInfo').classList.add('hide');
}
var btnSessionPlayPause = row.querySelector('.btnSessionPlayPause');
const btnSessionPlayPause = row.querySelector('.btnSessionPlayPause');
if (session.ServerId && nowPlayingItem && session.SupportsRemoteControl && session.DeviceId !== connectionManager.deviceId()) {
btnSessionPlayPause.classList.remove('hide');
@ -567,19 +587,18 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
row.querySelector('.sessionNowPlayingTime').innerHTML = DashboardPage.getSessionNowPlayingTime(session);
row.querySelector('.sessionUserName').innerHTML = DashboardPage.getUsersHtml(session);
row.querySelector('.sessionAppSecondaryText').innerHTML = DashboardPage.getAppSecondaryText(session);
row.querySelector('.sessionTranscodingFramerate').innerHTML = session.TranscodingInfo && session.TranscodingInfo.Framerate ? session.TranscodingInfo.Framerate + ' fps' : '';
var nowPlayingName = DashboardPage.getNowPlayingName(session);
var nowPlayingInfoElem = row.querySelector('.sessionNowPlayingInfo');
const nowPlayingName = DashboardPage.getNowPlayingName(session);
const nowPlayingInfoElem = row.querySelector('.sessionNowPlayingInfo');
if (!(nowPlayingName.image && nowPlayingName.image == nowPlayingInfoElem.getAttribute('data-imgsrc'))) {
nowPlayingInfoElem.innerHTML = nowPlayingName.html;
nowPlayingInfoElem.setAttribute('data-imgsrc', nowPlayingName.image || '');
}
var playbackProgressElem = row.querySelector('.playbackProgress');
const playbackProgressElem = row.querySelector('.playbackProgress');
if (nowPlayingItem && nowPlayingItem.RunTimeTicks) {
var percent = 100 * (session.PlayState.PositionTicks || 0) / nowPlayingItem.RunTimeTicks;
const percent = 100 * (session.PlayState.PositionTicks || 0) / nowPlayingItem.RunTimeTicks;
playbackProgressElem.outerHTML = indicators.getProgressHtml(percent, {
containerClass: 'playbackProgress'
});
@ -589,10 +608,10 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
});
}
var transcodingProgress = row.querySelector('.transcodingProgress');
const transcodingProgress = row.querySelector('.transcodingProgress');
if (session.TranscodingInfo && session.TranscodingInfo.CompletionPercentage) {
var percent = session.TranscodingInfo.CompletionPercentage.toFixed(1);
const percent = session.TranscodingInfo.CompletionPercentage.toFixed(1);
transcodingProgress.outerHTML = indicators.getProgressHtml(percent, {
containerClass: 'transcodingProgress'
});
@ -602,8 +621,8 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
});
}
var imgUrl = DashboardPage.getNowPlayingImageUrl(nowPlayingItem) || '';
var imgElem = row.querySelector('.sessionNowPlayingContent');
const imgUrl = DashboardPage.getNowPlayingImageUrl(nowPlayingItem) || '';
const imgElem = row.querySelector('.sessionNowPlayingContent');
if (imgUrl != imgElem.getAttribute('data-src')) {
imgElem.style.backgroundImage = imgUrl ? "url('" + imgUrl + "')" : '';
@ -617,7 +636,7 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
}
},
getClientImage: function (connection) {
var iconUrl = imageHelper.getDeviceIcon(connection);
const iconUrl = imageHelper.getDeviceIcon(connection);
return "<img src='" + iconUrl + "' />";
},
getNowPlayingImageUrl: function (item) {
@ -647,7 +666,7 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
});
}
var imageTags = (item || {}).ImageTags || {};
const imageTags = (item || {}).ImageTags || {};
if (item && imageTags.Thumb) {
return ApiClient.getScaledImageUrl(item.Id, {
@ -701,20 +720,20 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
},
systemUpdateTaskKey: 'SystemUpdateTask',
stopTask: function (btn, id) {
var page = dom.parentWithClass(btn, 'page');
const page = dom.parentWithClass(btn, 'page');
ApiClient.stopScheduledTask(id).then(function () {
pollForInfo(page, ApiClient);
});
},
restart: function (btn) {
require(['confirm'], function (confirm) {
confirm.default({
import('confirm').then(({default: confirm}) => {
confirm({
title: globalize.translate('HeaderRestart'),
text: globalize.translate('MessageConfirmRestart'),
confirmText: globalize.translate('ButtonRestart'),
primary: 'delete'
}).then(function () {
var page = dom.parentWithClass(btn, 'page');
const page = dom.parentWithClass(btn, 'page');
page.querySelector('#btnRestartServer').disabled = true;
page.querySelector('#btnShutdown').disabled = true;
ApiClient.restartServer();
@ -722,14 +741,14 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
});
},
shutdown: function (btn) {
require(['confirm'], function (confirm) {
confirm.default({
import('confirm').then(({default: confirm}) => {
confirm({
title: globalize.translate('HeaderShutdown'),
text: globalize.translate('MessageConfirmShutdown'),
confirmText: globalize.translate('ButtonShutdown'),
primary: 'delete'
}).then(function () {
var page = dom.parentWithClass(btn, 'page');
const page = dom.parentWithClass(btn, 'page');
page.querySelector('#btnRestartServer').disabled = true;
page.querySelector('#btnShutdown').disabled = true;
ApiClient.shutdownServer();
@ -737,7 +756,7 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
});
}
};
return function (view, params) {
export default function (view, params) {
function onRestartRequired(evt, apiClient) {
console.debug('onRestartRequired not implemented', evt, apiClient);
}
@ -776,11 +795,11 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
}
}
var serverId = ApiClient.serverId();
const serverId = ApiClient.serverId();
view.querySelector('.activeDevices').addEventListener('click', onActiveDevicesClick);
view.addEventListener('viewshow', function () {
var page = this;
var apiClient = ApiClient;
const page = this;
const apiClient = ApiClient;
if (apiClient) {
loading.show();
@ -817,7 +836,7 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
}
});
view.addEventListener('viewbeforehide', function () {
var apiClient = ApiClient;
const apiClient = ApiClient;
events.off(serverNotifications, 'RestartRequired', onRestartRequired);
events.off(serverNotifications, 'ServerShuttingDown', onServerShuttingDown);
events.off(serverNotifications, 'ServerRestarting', onServerRestarting);
@ -831,18 +850,19 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
}
});
view.addEventListener('viewdestroy', function () {
var page = this;
var userActivityLog = page.userActivityLog;
const page = this;
const userActivityLog = page.userActivityLog;
if (userActivityLog) {
userActivityLog.destroy();
}
var serverActivityLog = page.serverActivityLog;
const serverActivityLog = page.serverActivityLog;
if (serverActivityLog) {
serverActivityLog.destroy();
}
});
};
});
}
/* eslint-enable indent */

View file

@ -1,5 +1,10 @@
define(['jQuery', 'loading', 'globalize', 'dom', 'libraryMenu'], function ($, loading, globalize, dom, libraryMenu) {
'use strict';
import $ from 'jQuery';
import loading from 'loading';
import globalize from 'globalize';
import dom from 'dom';
import libraryMenu from 'libraryMenu';
/* eslint-disable indent */
function loadPage(page, config, systemInfo) {
Array.prototype.forEach.call(page.querySelectorAll('.chkDecodeCodec'), function (c) {
@ -27,10 +32,10 @@ define(['jQuery', 'loading', 'globalize', 'dom', 'libraryMenu'], function ($, lo
function onSaveEncodingPathFailure(response) {
loading.hide();
var msg = '';
let msg = '';
msg = globalize.translate('FFmpegSavePathNotFound');
require(['alert'], function (alert) {
import('alert').then(({default: alert}) => {
alert(msg);
});
}
@ -49,9 +54,9 @@ define(['jQuery', 'loading', 'globalize', 'dom', 'libraryMenu'], function ($, lo
}
function onSubmit() {
var form = this;
const form = this;
var onDecoderConfirmed = function () {
const onDecoderConfirmed = function () {
loading.show();
ApiClient.getNamedConfiguration('encoding').then(function (config) {
config.DownMixAudioBoost = $('#txtDownMixAudioBoost', form).val();
@ -75,7 +80,7 @@ define(['jQuery', 'loading', 'globalize', 'dom', 'libraryMenu'], function ($, lo
ApiClient.updateNamedConfiguration('encoding', config).then(function () {
updateEncoder(form);
}, function () {
require(['alert'], function (alert) {
import('alert').then(({default: alert}) => {
alert(globalize.translate('DefaultErrorMessage'));
});
@ -85,7 +90,7 @@ define(['jQuery', 'loading', 'globalize', 'dom', 'libraryMenu'], function ($, lo
};
if ($('#selectVideoDecoder', form).val()) {
require(['alert'], function (alert) {
import('alert').then(({default: alert}) => {
alert({
title: globalize.translate('TitleHardwareAcceleration'),
text: globalize.translate('HardwareAccelerationWarning')
@ -100,7 +105,7 @@ define(['jQuery', 'loading', 'globalize', 'dom', 'libraryMenu'], function ($, lo
function setDecodingCodecsVisible(context, value) {
value = value || '';
var any;
let any;
Array.prototype.forEach.call(context.querySelectorAll('.chkDecodeCodec'), function (c) {
if (-1 === c.getAttribute('data-types').split(',').indexOf(value)) {
dom.parentWithTag(c, 'LABEL').classList.add('hide');
@ -131,7 +136,7 @@ define(['jQuery', 'loading', 'globalize', 'dom', 'libraryMenu'], function ($, lo
}
$(document).on('pageinit', '#encodingSettingsPage', function () {
var page = this;
const page = this;
page.querySelector('#selectVideoDecoder').addEventListener('change', function () {
if ('vaapi' == this.value) {
page.querySelector('.fldVaapiDevice').classList.remove('hide');
@ -150,8 +155,8 @@ define(['jQuery', 'loading', 'globalize', 'dom', 'libraryMenu'], function ($, lo
setDecodingCodecsVisible(page, this.value);
});
$('#btnSelectEncoderPath', page).on('click.selectDirectory', function () {
require(['directorybrowser'], function (directoryBrowser) {
var picker = new directoryBrowser();
import('directorybrowser').then(({default: directoryBrowser}) => {
const picker = new directoryBrowser();
picker.show({
includeFiles: true,
callback: function (path) {
@ -165,8 +170,8 @@ define(['jQuery', 'loading', 'globalize', 'dom', 'libraryMenu'], function ($, lo
});
});
$('#btnSelectTranscodingTempPath', page).on('click.selectDirectory', function () {
require(['directorybrowser'], function (directoryBrowser) {
var picker = new directoryBrowser();
import('directorybrowser').then(({default: directoryBrowser}) => {
const picker = new directoryBrowser();
picker.show({
callback: function (path) {
if (path) {
@ -185,11 +190,12 @@ define(['jQuery', 'loading', 'globalize', 'dom', 'libraryMenu'], function ($, lo
}).on('pageshow', '#encodingSettingsPage', function () {
loading.show();
libraryMenu.setTabs('playback', 0, getTabs);
var page = this;
const page = this;
ApiClient.getNamedConfiguration('encoding').then(function (config) {
ApiClient.getSystemInfo().then(function (systemInfo) {
loadPage(page, config, systemInfo);
});
});
});
});
/* eslint-enable indent */

View file

@ -59,7 +59,7 @@ define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'emby-textarea', 'emb
return function (view, params) {
$('#btnSelectCachePath', view).on('click.selectDirectory', function () {
require(['directorybrowser'], function (directoryBrowser) {
var picker = new directoryBrowser();
var picker = new directoryBrowser.default();
picker.show({
callback: function (path) {
if (path) {
@ -76,7 +76,7 @@ define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'emby-textarea', 'emb
});
$('#btnSelectMetadataPath', view).on('click.selectDirectory', function () {
require(['directorybrowser'], function (directoryBrowser) {
var picker = new directoryBrowser();
var picker = new directoryBrowser.default();
picker.show({
path: $('#txtMetadataPath', view).val(),
networkSharePath: $('#txtMetadataNetworkPath', view).val(),

View file

@ -1,9 +1,20 @@
define(['jQuery', 'apphost', 'scripts/taskbutton', 'loading', 'libraryMenu', 'globalize', 'dom', 'indicators', 'scripts/imagehelper', 'cardStyle', 'emby-itemrefreshindicator'], function ($, appHost, taskButton, loading, libraryMenu, globalize, dom, indicators, imageHelper) {
'use strict';
import $ from 'jQuery';
import appHost from 'apphost';
import taskButton from 'scripts/taskbutton';
import loading from 'loading';
import libraryMenu from 'libraryMenu';
import globalize from 'globalize';
import dom from 'dom';
import indicators from 'indicators';
import imageHelper from 'scripts/imagehelper';
import 'cardStyle';
import 'emby-itemrefreshindicator';
/* eslint-disable indent */
function addVirtualFolder(page) {
require(['medialibrarycreator'], function (medialibrarycreator) {
new medialibrarycreator().show({
import('medialibrarycreator').then(({default: medialibrarycreator}) => {
new medialibrarycreator({
collectionTypeOptions: getCollectionTypeOptions().filter(function (f) {
return !f.hidden;
}),
@ -17,8 +28,8 @@ define(['jQuery', 'apphost', 'scripts/taskbutton', 'loading', 'libraryMenu', 'gl
}
function editVirtualFolder(page, virtualFolder) {
require(['medialibraryeditor'], function (medialibraryeditor) {
new medialibraryeditor().show({
import('medialibraryeditor').then(({default: medialibraryeditor}) => {
new medialibraryeditor({
refresh: shouldRefreshLibraryAfterChanges(page),
library: virtualFolder
}).then(function (hasChanges) {
@ -30,23 +41,21 @@ define(['jQuery', 'apphost', 'scripts/taskbutton', 'loading', 'libraryMenu', 'gl
}
function deleteVirtualFolder(page, virtualFolder) {
var msg = globalize.translate('MessageAreYouSureYouWishToRemoveMediaFolder');
let msg = globalize.translate('MessageAreYouSureYouWishToRemoveMediaFolder');
if (virtualFolder.Locations.length) {
msg += '<br/><br/>' + globalize.translate('MessageTheFollowingLocationWillBeRemovedFromLibrary') + '<br/><br/>';
msg += virtualFolder.Locations.join('<br/>');
}
require(['confirm'], function (confirm) {
confirm.default({
import('confirm').then(({default: confirm}) => {
confirm({
text: msg,
title: globalize.translate('HeaderRemoveMediaFolder'),
confirmText: globalize.translate('Delete'),
primary: 'delete'
}).then(function () {
var refreshAfterChange = shouldRefreshLibraryAfterChanges(page);
const refreshAfterChange = shouldRefreshLibraryAfterChanges(page);
ApiClient.removeVirtualFolder(virtualFolder.Name, refreshAfterChange).then(function () {
reloadLibrary(page);
});
@ -55,7 +64,7 @@ define(['jQuery', 'apphost', 'scripts/taskbutton', 'loading', 'libraryMenu', 'gl
}
function refreshVirtualFolder(page, virtualFolder) {
require(['refreshDialog'], function (refreshDialog) {
import('refreshDialog').then(({default: refreshDialog}) => {
new refreshDialog({
itemIds: [virtualFolder.ItemId],
serverId: ApiClient.serverId(),
@ -65,13 +74,13 @@ define(['jQuery', 'apphost', 'scripts/taskbutton', 'loading', 'libraryMenu', 'gl
}
function renameVirtualFolder(page, virtualFolder) {
require(['prompt'], function (prompt) {
prompt.default({
import('prompt').then(({default: prompt}) => {
prompt({
label: globalize.translate('LabelNewName'),
confirmText: globalize.translate('ButtonRename')
}).then(function (newName) {
if (newName && newName != virtualFolder.Name) {
var refreshAfterChange = shouldRefreshLibraryAfterChanges(page);
const refreshAfterChange = shouldRefreshLibraryAfterChanges(page);
ApiClient.renameVirtualFolder(virtualFolder.Name, newName, refreshAfterChange).then(function () {
reloadLibrary(page);
});
@ -81,10 +90,10 @@ define(['jQuery', 'apphost', 'scripts/taskbutton', 'loading', 'libraryMenu', 'gl
}
function showCardMenu(page, elem, virtualFolders) {
var card = dom.parentWithClass(elem, 'card');
var index = parseInt(card.getAttribute('data-index'));
var virtualFolder = virtualFolders[index];
var menuItems = [];
const card = dom.parentWithClass(elem, 'card');
const index = parseInt(card.getAttribute('data-index'));
const virtualFolder = virtualFolders[index];
const menuItems = [];
menuItems.push({
name: globalize.translate('ButtonEditImages'),
id: 'editimages',
@ -111,7 +120,7 @@ define(['jQuery', 'apphost', 'scripts/taskbutton', 'loading', 'libraryMenu', 'gl
icon: 'refresh'
});
require(['actionsheet'], function (actionsheet) {
import('actionsheet').then(({default: actionsheet}) => {
actionsheet.show({
items: menuItems,
positionTo: elem,
@ -153,7 +162,7 @@ define(['jQuery', 'apphost', 'scripts/taskbutton', 'loading', 'libraryMenu', 'gl
}
function reloadVirtualFolders(page, virtualFolders) {
var html = '';
let html = '';
virtualFolders.push({
Name: globalize.translate('ButtonAddMediaLibrary'),
icon: 'add_circle',
@ -164,12 +173,12 @@ define(['jQuery', 'apphost', 'scripts/taskbutton', 'loading', 'libraryMenu', 'gl
showNameWithIcon: true
});
for (var i = 0; i < virtualFolders.length; i++) {
var virtualFolder = virtualFolders[i];
for (let i = 0; i < virtualFolders.length; i++) {
const virtualFolder = virtualFolders[i];
html += getVirtualFolderHtml(page, virtualFolder, i);
}
var divVirtualFolders = page.querySelector('#divVirtualFolders');
const divVirtualFolders = page.querySelector('#divVirtualFolders');
divVirtualFolders.innerHTML = html;
divVirtualFolders.classList.add('itemsContainer');
divVirtualFolders.classList.add('vertical-wrap');
@ -180,9 +189,9 @@ define(['jQuery', 'apphost', 'scripts/taskbutton', 'loading', 'libraryMenu', 'gl
addVirtualFolder(page);
});
$('.editLibrary', divVirtualFolders).on('click', function () {
var card = $(this).parents('.card')[0];
var index = parseInt(card.getAttribute('data-index'));
var virtualFolder = virtualFolders[index];
const card = $(this).parents('.card')[0];
const index = parseInt(card.getAttribute('data-index'));
const virtualFolder = virtualFolders[index];
if (virtualFolder.ItemId) {
editVirtualFolder(page, virtualFolder);
@ -192,7 +201,7 @@ define(['jQuery', 'apphost', 'scripts/taskbutton', 'loading', 'libraryMenu', 'gl
}
function editImages(page, virtualFolder) {
require(['imageEditor'], function (imageEditor) {
import('imageEditor').then(({default: imageEditor}) => {
imageEditor.show({
itemId: virtualFolder.ItemId,
serverId: ApiClient.serverId()
@ -240,8 +249,8 @@ define(['jQuery', 'apphost', 'scripts/taskbutton', 'loading', 'libraryMenu', 'gl
}
function getVirtualFolderHtml(page, virtualFolder, index) {
var html = '';
var style = '';
let html = '';
let style = '';
if (page.classList.contains('wizardPage')) {
style += 'min-width:33.3%;';
@ -252,7 +261,7 @@ define(['jQuery', 'apphost', 'scripts/taskbutton', 'loading', 'libraryMenu', 'gl
html += '<div class="cardScalable visualCardBox-cardScalable">';
html += '<div class="cardPadder cardPadder-backdrop"></div>';
html += '<div class="cardContent">';
var imgUrl = '';
let imgUrl = '';
if (virtualFolder.PrimaryImageItemId) {
imgUrl = ApiClient.getScaledImageUrl(virtualFolder.PrimaryImageItemId, {
@ -261,7 +270,7 @@ define(['jQuery', 'apphost', 'scripts/taskbutton', 'loading', 'libraryMenu', 'gl
});
}
var hasCardImageContainer;
let hasCardImageContainer;
if (imgUrl) {
html += '<div class="cardImageContainer editLibrary" style="cursor:pointer;background-image:url(\'' + imgUrl + "');\">";
@ -311,7 +320,7 @@ define(['jQuery', 'apphost', 'scripts/taskbutton', 'loading', 'libraryMenu', 'gl
}
html += '</div>';
var typeName = getCollectionTypeOptions().filter(function (t) {
let typeName = getCollectionTypeOptions().filter(function (t) {
return t.value == virtualFolder.CollectionType;
})[0];
typeName = typeName ? typeName.name : globalize.translate('FolderTypeUnset');
@ -371,7 +380,7 @@ define(['jQuery', 'apphost', 'scripts/taskbutton', 'loading', 'libraryMenu', 'gl
});
pageIdOn('pageshow', 'mediaLibraryPage', function () {
libraryMenu.setTabs('librarysetup', 0, getTabs);
var page = this;
const page = this;
taskButton({
mode: 'on',
progressElem: page.querySelector('.refreshProgress'),
@ -380,7 +389,7 @@ define(['jQuery', 'apphost', 'scripts/taskbutton', 'loading', 'libraryMenu', 'gl
});
});
pageIdOn('pagebeforehide', 'mediaLibraryPage', function () {
var page = this;
const page = this;
taskButton({
mode: 'off',
progressElem: page.querySelector('.refreshProgress'),
@ -388,4 +397,5 @@ define(['jQuery', 'apphost', 'scripts/taskbutton', 'loading', 'libraryMenu', 'gl
button: page.querySelector('.btnRefresh')
});
});
});
/* eslint-enable indent */

View file

@ -1,12 +1,17 @@
define(['loading', 'libraryMenu', 'globalize', 'emby-checkbox', 'emby-select'], function (loading, libraryMenu, globalize) {
'use strict';
import loading from 'loading';
import libraryMenu from 'libraryMenu';
import globalize from 'globalize';
import 'emby-checkbox';
import 'emby-select';
/* eslint-disable indent */
function onSubmit(e) {
var form = this;
var localAddress = form.querySelector('#txtLocalAddress').value;
var enableUpnp = form.querySelector('#chkEnableUpnp').checked;
const form = this;
const localAddress = form.querySelector('#txtLocalAddress').value;
const enableUpnp = form.querySelector('#chkEnableUpnp').checked;
confirmSelections(localAddress, enableUpnp, function () {
var validationResult = getValidationAlert(form);
const validationResult = getValidationAlert(form);
if (validationResult) {
showAlertText(validationResult);
@ -47,7 +52,7 @@ define(['loading', 'libraryMenu', 'globalize', 'emby-checkbox', 'emby-select'],
}
function triggerChange(select) {
var evt = document.createEvent('HTMLEvents');
const evt = document.createEvent('HTMLEvents');
evt.initEvent('change', false, true);
select.dispatchEvent(evt);
}
@ -65,8 +70,8 @@ define(['loading', 'libraryMenu', 'globalize', 'emby-checkbox', 'emby-select'],
}
function validateHttps(form) {
var certPath = form.querySelector('#txtCertificatePath').value || null;
var httpsEnabled = form.querySelector('#chkEnableHttps').checked;
const certPath = form.querySelector('#txtCertificatePath').value || null;
const httpsEnabled = form.querySelector('#chkEnableHttps').checked;
if (httpsEnabled && !certPath) {
return showAlertText({
@ -97,7 +102,7 @@ define(['loading', 'libraryMenu', 'globalize', 'emby-checkbox', 'emby-select'],
}
}
return function (view, params) {
export default function (view, params) {
function loadPage(page, config) {
page.querySelector('#txtPortNumber').value = config.HttpServerPortNumber;
page.querySelector('#txtPublicPort').value = config.PublicPort;
@ -111,7 +116,7 @@ define(['loading', 'libraryMenu', 'globalize', 'emby-checkbox', 'emby-select'],
page.querySelector('#chkEnableHttps').checked = config.EnableHttps;
page.querySelector('#chkRequireHttps').checked = config.RequireHttps;
page.querySelector('#txtBaseUrl').value = config.BaseUrl || '';
var txtCertificatePath = page.querySelector('#txtCertificatePath');
const txtCertificatePath = page.querySelector('#txtCertificatePath');
txtCertificatePath.value = config.CertificatePath || '';
page.querySelector('#txtCertPassword').value = config.CertificatePassword || '';
page.querySelector('#chkEnableUpnp').checked = config.EnableUPnP;
@ -136,7 +141,7 @@ define(['loading', 'libraryMenu', 'globalize', 'emby-checkbox', 'emby-select'],
});
view.querySelector('#btnSelectCertPath').addEventListener('click', function () {
require(['directorybrowser'], function (directoryBrowser) {
var picker = new directoryBrowser();
const picker = new directoryBrowser();
picker.show({
includeFiles: true,
includeDirectories: true,
@ -158,5 +163,6 @@ define(['loading', 'libraryMenu', 'globalize', 'emby-checkbox', 'emby-select'],
loadPage(view, config);
});
});
};
});
}
/* eslint-enable indent */

View file

@ -1,5 +1,9 @@
define(['jQuery', 'loading', 'libraryMenu', 'globalize'], function ($, loading, libraryMenu, globalize) {
'use strict';
import $ from 'jQuery';
import loading from 'loading';
import libraryMenu from 'libraryMenu';
import globalize from 'globalize';
/* eslint-disable indent */
function loadPage(page, config) {
$('#txtMinResumePct', page).val(config.MinResumePct);
@ -10,7 +14,7 @@ define(['jQuery', 'loading', 'libraryMenu', 'globalize'], function ($, loading,
function onSubmit() {
loading.show();
var form = this;
const form = this;
ApiClient.getServerConfiguration().then(function (config) {
config.MinResumePct = $('#txtMinResumePct', form).val();
config.MaxResumePct = $('#txtMaxResumePct', form).val();
@ -40,9 +44,10 @@ define(['jQuery', 'loading', 'libraryMenu', 'globalize'], function ($, loading,
}).on('pageshow', '#playbackConfigurationPage', function () {
loading.show();
libraryMenu.setTabs('playback', 1, getTabs);
var page = this;
const page = this;
ApiClient.getServerConfiguration().then(function (config) {
loadPage(page, config);
});
});
});
/* eslint-enable indent */

View file

@ -123,6 +123,9 @@ define(['loading', 'libraryMenu', 'globalize', 'cardStyle', 'emby-button', 'emby
}, {
href: 'availableplugins.html',
name: globalize.translate('TabCatalog')
}, {
href: 'repositories.html',
name: globalize.translate('TabRepositories')
}];
}

View file

@ -37,18 +37,22 @@ define(['loading', 'libraryMenu', 'dom', 'globalize', 'cardStyle', 'emby-button'
})[0];
var configPageUrl = configPage ? Dashboard.getConfigurationPageUrl(configPage.Name) : null;
var html = '';
html += "<div data-id='" + plugin.Id + "' data-name='" + plugin.Name + "' class='card backdropCard'>";
html += "<div data-id='" + plugin.Id + "' data-name='" + plugin.Name + "' data-removable='" + plugin.CanUninstall + "' class='card backdropCard'>";
html += '<div class="cardBox visualCardBox">';
html += '<div class="cardScalable">';
html += '<div class="cardPadder cardPadder-backdrop"></div>';
html += configPageUrl ? '<a class="cardContent cardImageContainer" is="emby-linkbutton" href="' + configPageUrl + '">' : '<div class="cardContent noConfigPluginCard noHoverEffect cardImageContainer">';
html += configPageUrl ? '<a class="cardContent cardImageContainer" is="emby-linkbutton" href="' + configPageUrl + '">' : '<div class="cardContent noConfigPluginCard noHoverEffect cardImageContainer emby-button">';
html += '<span class="cardImageIcon material-icons folder"></span>';
html += configPageUrl ? '</a>' : '</div>';
html += '</div>';
html += '<div class="cardFooter">';
html += '<div style="text-align:right; float:right;padding-top:5px;">';
html += '<button type="button" is="paper-icon-button-light" class="btnCardMenu autoSize"><span class="material-icons more_vert"></span></button>';
html += '</div>';
if (configPage || plugin.CanUninstall) {
html += '<div style="text-align:right; float:right;padding-top:5px;">';
html += '<button type="button" is="paper-icon-button-light" class="btnCardMenu autoSize"><span class="material-icons more_vert"></span></button>';
html += '</div>';
}
html += "<div class='cardText'>";
html += configPage && configPage.DisplayName ? configPage.DisplayName : plugin.Name;
html += '</div>';
@ -104,6 +108,7 @@ define(['loading', 'libraryMenu', 'dom', 'globalize', 'cardStyle', 'emby-button'
var card = dom.parentWithClass(elem, 'card');
var id = card.getAttribute('data-id');
var name = card.getAttribute('data-name');
var removable = card.getAttribute('data-removable');
var configHref = card.querySelector('.cardContent').getAttribute('href');
var menuItems = [];
@ -115,11 +120,13 @@ define(['loading', 'libraryMenu', 'dom', 'globalize', 'cardStyle', 'emby-button'
});
}
menuItems.push({
name: globalize.translate('ButtonUninstall'),
id: 'delete',
icon: 'delete'
});
if (removable === 'true') {
menuItems.push({
name: globalize.translate('ButtonUninstall'),
id: 'delete',
icon: 'delete'
});
}
require(['actionsheet'], function (actionsheet) {
actionsheet.show({
@ -153,6 +160,9 @@ define(['loading', 'libraryMenu', 'dom', 'globalize', 'cardStyle', 'emby-button'
}, {
href: 'availableplugins.html',
name: globalize.translate('TabCatalog')
}, {
href: 'repositories.html',
name: globalize.translate('TabRepositories')
}];
}

View file

@ -0,0 +1,153 @@
import loading from 'loading';
import libraryMenu from 'libraryMenu';
import globalize from 'globalize';
import dialogHelper from 'dialogHelper';
import 'emby-button';
import 'emby-checkbox';
import 'emby-select';
import 'formDialogStyle';
import 'listViewStyle';
let repositories = [];
function reloadList(page) {
loading.show();
ApiClient.getJSON(ApiClient.getUrl('Repositories')).then(list => {
repositories = list;
populateList({
listElement: page.querySelector('#repositories'),
noneElement: page.querySelector('#none'),
repositories: repositories
});
}).catch(error => {
console.error('error loading repositories');
page.querySelector('#none').classList.remove('hide');
loading.hide();
});
}
function saveList(page) {
loading.show();
ApiClient.ajax({
type: 'POST',
url: ApiClient.getUrl('Repositories'),
data: JSON.stringify(repositories),
contentType: 'application/json'
}).then(response => {
reloadList(page);
}).catch(error => {
console.error('error saving repositories');
loading.hide();
});
}
function populateList(options) {
var html = '';
html += '<div class="paperList">';
for (var i = 0; i < options.repositories.length; i++) {
html += getRepositoryHtml(options.repositories[i]);
}
html += '</div>';
if (!options.repositories.length) {
options.noneElement.classList.remove('hide');
}
options.listElement.innerHTML = html;
loading.hide();
}
function getRepositoryHtml(repository) {
var html = '';
html += '<div class="listItem listItem-border">';
html += `<a is="emby-linkbutton" style="margin:0;padding:0" class="clearLink listItemIconContainer" href="${repository.Url}">`;
html += '<span class="material-icons listItemIcon open_in_new"></span>';
html += '</a>';
html += '<div class="listItemBody two-line">';
html += `<h3 class="listItemBodyText">${repository.Name}</h3>`;
html += `<div class="listItemBodyText secondary">${repository.Url}</div>`;
html += '</div>';
html += `<button type="button" is="paper-icon-button-light" id="${repository.Url}" class="btnDelete" title="${globalize.translate('ButtonDelete')}"><span class="material-icons delete"></span></button>`;
html += '</div>';
return html;
}
function getTabs() {
return [{
href: 'installedplugins.html',
name: globalize.translate('TabMyPlugins')
}, {
href: 'availableplugins.html',
name: globalize.translate('TabCatalog')
}, {
href: 'repositories.html',
name: globalize.translate('TabRepositories')
}];
}
export default function(view, params) {
view.addEventListener('viewshow', function () {
libraryMenu.setTabs('plugins', 2, getTabs);
reloadList(this);
var save = this;
$('#repositories', view).on('click', '.btnDelete', function() {
var button = this;
repositories = repositories.filter(function (r) {
return r.Url !== button.id;
});
saveList(save);
});
});
view.querySelector('.btnNewRepository').addEventListener('click', () => {
let dialog = dialogHelper.createDialog({
scrollY: false,
size: 'large',
modal: false,
removeOnClose: true
});
let html = '';
html += '<div class="formDialogHeader">';
html += '<button type="button" is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><span class="material-icons arrow_back"></span></button>';
html += `<h3 class="formDialogHeaderTitle">${globalize.translate('HeaderNewRepository')}</h3>`;
html += '</div>';
html += '<form class="newPluginForm" style="margin:4em">';
html += '<div class="inputContainer">';
html += `<input is="emby-input" id="txtRepositoryName" label="${globalize.translate('LabelRepositoryName')}" type="text" required />`;
html += `<div class="fieldDescription">${globalize.translate('LabelRepositoryNameHelp')}</div>`;
html += '</div>';
html += '<div class="inputContainer">';
html += `<input is="emby-input" id="txtRepositoryUrl" label="${globalize.translate('LabelRepositoryUrl')}" type="url" required />`;
html += `<div class="fieldDescription">${globalize.translate('LabelRepositoryUrlHelp')}</div>`;
html += '</div>';
html += `<button is="emby-button" type="submit" class="raised button-submit block"><span>${globalize.translate('ButtonSave')}</span></button>`;
html += '</div>';
html += '</form>';
dialog.innerHTML = html;
dialog.querySelector('.btnCancel').addEventListener('click', () => {
dialogHelper.close(dialog);
});
dialog.querySelector('.newPluginForm').addEventListener('submit', () => {
repositories.push({
Name: dialog.querySelector('#txtRepositoryName').value,
Url: dialog.querySelector('#txtRepositoryUrl').value,
Enabled: true
});
saveList(view);
dialogHelper.close(dialog);
return false;
});
dialogHelper.open(dialog);
});
}

View file

@ -1,8 +1,10 @@
define(['components/activitylog', 'globalize'], function (ActivityLog, globalize) {
'use strict';
import ActivityLog from 'components/activitylog';
import globalize from 'globalize';
return function (view, params) {
var activityLog;
/* eslint-disable indent */
export default function (view, params) {
let activityLog;
if (params.useractivity !== 'false') {
view.querySelector('.activityItems').setAttribute('data-useractivity', 'true');
@ -27,5 +29,6 @@ define(['components/activitylog', 'globalize'], function (ActivityLog, globalize
activityLog = null;
});
};
});
}
/* eslint-enable indent */

View file

@ -400,6 +400,7 @@ define(['loading', 'appRouter', 'layoutManager', 'connectionManager', 'userSetti
} else if (item.Album) {
parentNameHtml.push(item.Album);
}
// FIXME: This whole section needs some refactoring, so it becames easier to scale across all form factors. See GH #1022
var html = '';
var tvShowHtml = parentNameHtml[0];
@ -415,9 +416,9 @@ define(['loading', 'appRouter', 'layoutManager', 'connectionManager', 'userSetti
}
} else {
if (layoutManager.mobile) {
html = '<h1 class="parentName" style="margin: .1em 0 .25em;">' + parentNameHtml.join('</br>') + '</h1>';
html = '<h1 class="parentName" style="margin: 0.2em 0 0">' + parentNameHtml.join('</br>') + '</h1>';
} else {
html = '<h1 class="parentName" style="margin: .1em 0 .25em;">' + tvShowHtml + '</h1>';
html = '<h1 class="parentName" style="margin: 0.2em 0 0">' + tvShowHtml + '</h1>';
}
}
}
@ -425,20 +426,19 @@ define(['loading', 'appRouter', 'layoutManager', 'connectionManager', 'userSetti
var name = itemHelper.getDisplayName(item, {
includeParentInfo: false
});
var offset = parentNameLast ? '.25em' : '.5em';
if (html && !parentNameLast) {
if (!layoutManager.mobile && tvSeasonHtml) {
html += '<h3 class="itemName infoText" style="margin: .25em 0 .5em;">' + tvSeasonHtml + ' - ' + name + '</h3>';
html += '<h3 class="itemName infoText" style="margin: 0.2em 0 0">' + tvSeasonHtml + ' - ' + name + '</h3>';
} else {
html += '<h3 class="itemName infoText" style="margin: .25em 0 .5em;">' + name + '</h3>';
html += '<h3 class="itemName infoText" style="margin: 0.2em 0 0">' + name + '</h3>';
}
} else {
html = '<h1 class="itemName infoText" style="margin: .1em 0 ' + offset + ';">' + name + '</h1>' + html;
html = '<h1 class="itemName infoText" style="margin: 0.4em 0 0">' + name + '</h1>' + html;
}
if (item.OriginalTitle && item.OriginalTitle != item.Name) {
html += '<h4 class="itemName infoText" style="margin: -' + offset + ' 0 0;">' + item.OriginalTitle + '</h4>';
html += '<h4 class="itemName infoText" style="margin: 0 0 0;">' + item.OriginalTitle + '</h4>';
}
container.innerHTML = html;
@ -462,7 +462,7 @@ define(['loading', 'appRouter', 'layoutManager', 'connectionManager', 'userSetti
if (dom.getWindowSize().innerWidth >= 1000) {
backdrop.setBackdrops([item]);
} else {
backdrop.clear();
backdrop.clearBackdrop();
}
}
@ -1106,10 +1106,10 @@ define(['loading', 'appRouter', 'layoutManager', 'connectionManager', 'userSetti
var externalLinksElem = page.querySelector('.itemExternalLinks');
renderOverview([overview], item);
var i;
var itemMiscInfo;
itemMiscInfo = page.querySelectorAll('.itemMiscInfo-primary');
for (i = 0; i < itemMiscInfo.length; i++) {
mediaInfo.fillPrimaryMediaInfo(itemMiscInfo[i], item, {
interactive: true,
@ -1125,7 +1125,6 @@ define(['loading', 'appRouter', 'layoutManager', 'connectionManager', 'userSetti
}
itemMiscInfo = page.querySelectorAll('.itemMiscInfo-secondary');
for (i = 0; i < itemMiscInfo.length; i++) {
mediaInfo.fillSecondaryMediaInfo(itemMiscInfo[i], item, {
interactive: true

View file

@ -1,4 +1,4 @@
define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager', 'cardBuilder', 'loading', 'connectionManager', 'alphaNumericShortcuts', 'scroller', 'playbackManager', 'alphaPicker', 'emby-itemscontainer', 'emby-scroller'], function (globalize, listView, layoutManager, userSettings, focusManager, cardBuilder, loading, connectionManager, AlphaNumericShortcuts, scroller, playbackManager, alphaPicker) {
define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager', 'cardBuilder', 'loading', 'connectionManager', 'alphaNumericShortcuts', 'scroller', 'playbackManager', 'alphaPicker', 'emby-itemscontainer', 'emby-scroller'], function (globalize, listView, layoutManager, userSettings, focusManager, cardBuilder, loading, connectionManager, AlphaNumericShortcuts, scroller, playbackManager, AlphaPicker) {
'use strict';
function getInitialLiveTvQuery(instance, params) {
@ -386,7 +386,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
var instance = this;
require(['playlistEditor'], function (playlistEditor) {
new playlistEditor().show({
new playlistEditor.showEditor({
items: [],
serverId: instance.params.serverId
});
@ -544,7 +544,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
alphaPickerElement.classList.add('focuscontainer-right');
self.itemsContainer.parentNode.classList.add('padded-right-withalphapicker');
self.alphaPicker = new alphaPicker({
self.alphaPicker = new AlphaPicker.default({
element: alphaPickerElement,
itemsContainer: layoutManager.tv ? self.itemsContainer : null,
itemClass: 'card',

View file

@ -59,7 +59,7 @@ define(['jQuery', 'loading', 'globalize', 'emby-button'], function ($, loading,
$('.liveTvSettingsForm').off('submit', onSubmit).on('submit', onSubmit);
$('#btnSelectRecordingPath', page).on('click.selectDirectory', function () {
require(['directorybrowser'], function (directoryBrowser) {
var picker = new directoryBrowser();
var picker = new directoryBrowser.default();
picker.show({
callback: function (path) {
if (path) {
@ -74,7 +74,7 @@ define(['jQuery', 'loading', 'globalize', 'emby-button'], function ($, loading,
});
$('#btnSelectMovieRecordingPath', page).on('click.selectDirectory', function () {
require(['directorybrowser'], function (directoryBrowser) {
var picker = new directoryBrowser();
var picker = new directoryBrowser.default();
picker.show({
callback: function (path) {
if (path) {
@ -89,7 +89,7 @@ define(['jQuery', 'loading', 'globalize', 'emby-button'], function ($, loading,
});
$('#btnSelectSeriesRecordingPath', page).on('click.selectDirectory', function () {
require(['directorybrowser'], function (directoryBrowser) {
var picker = new directoryBrowser();
var picker = new directoryBrowser.default();
picker.show({
callback: function (path) {
if (path) {
@ -104,7 +104,7 @@ define(['jQuery', 'loading', 'globalize', 'emby-button'], function ($, loading,
});
$('#btnSelectPostProcessorPath', page).on('click.selectDirectory', function () {
require(['directorybrowser'], function (directoryBrowser) {
var picker = new directoryBrowser();
var picker = new directoryBrowser.default();
picker.show({
includeFiles: true,
callback: function (path) {

View file

@ -155,8 +155,8 @@ define(['jQuery', 'globalize', 'scripts/taskbutton', 'dom', 'libraryMenu', 'layo
}
function mapChannels(page, providerId) {
require(['components/channelMapper/channelMapper'], function (channelmapper) {
new channelmapper({
require(['components/channelMapper/channelMapper'], function (channelMapper) {
new channelMapper.default({
serverId: ApiClient.serverInfo().Id,
providerId: providerId
}).show();

View file

@ -215,7 +215,7 @@ define(['globalize', 'loading', 'libraryMenu', 'dom', 'emby-input', 'emby-button
});
view.querySelector('.btnSelectPath').addEventListener('click', function () {
require(['directorybrowser'], function (directoryBrowser) {
var picker = new directoryBrowser();
var picker = new directoryBrowser.default();
picker.show({
includeFiles: true,
callback: function (path) {

View file

@ -242,7 +242,7 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB
tabContent.querySelector('.btnNewCollection').addEventListener('click', function () {
require(['collectionEditor'], function (collectionEditor) {
var serverId = ApiClient.serverInfo().Id;
new collectionEditor().show({
new collectionEditor.showEditor({
items: [],
serverId: serverId
});

View file

@ -1,4 +1,4 @@
define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser', 'alphaPicker', 'listView', 'cardBuilder', 'globalize', 'emby-itemscontainer'], function (loading, layoutManager, userSettings, events, libraryBrowser, alphaPicker, listView, cardBuilder, globalize) {
define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser', 'alphaPicker', 'listView', 'cardBuilder', 'globalize', 'emby-itemscontainer'], function (loading, layoutManager, userSettings, events, libraryBrowser, AlphaPicker, listView, cardBuilder, globalize) {
'use strict';
return function (view, params, tabContent, options) {
@ -168,7 +168,7 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser',
query.StartIndex = 0;
itemsContainer.refreshItems();
});
self.alphaPicker = new alphaPicker({
self.alphaPicker = new AlphaPicker.default({
element: alphaPickerElement,
valueChangeEvent: 'click'
});

View file

@ -1,4 +1,4 @@
define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', 'alphaPicker', 'listView', 'cardBuilder', 'userSettings', 'globalize', 'emby-itemscontainer'], function (layoutManager, loading, events, libraryBrowser, imageLoader, alphaPicker, listView, cardBuilder, userSettings, globalize) {
define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', 'alphaPicker', 'listView', 'cardBuilder', 'userSettings', 'globalize', 'emby-itemscontainer'], function (layoutManager, loading, events, libraryBrowser, imageLoader, AlphaPicker, listView, cardBuilder, userSettings, globalize) {
'use strict';
return function (view, params, tabContent) {
@ -213,7 +213,7 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', '
query.StartIndex = 0;
reloadItems();
});
self.alphaPicker = new alphaPicker({
self.alphaPicker = new AlphaPicker.default({
element: alphaPickerElement,
valueChangeEvent: 'click'
});

View file

@ -1,4 +1,4 @@
define(['layoutManager', 'playbackManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', 'alphaPicker', 'listView', 'cardBuilder', 'userSettings', 'globalize', 'emby-itemscontainer'], function (layoutManager, playbackManager, loading, events, libraryBrowser, imageLoader, alphaPicker, listView, cardBuilder, userSettings, globalize) {
define(['layoutManager', 'playbackManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', 'alphaPicker', 'listView', 'cardBuilder', 'userSettings', 'globalize', 'emby-itemscontainer'], function (layoutManager, playbackManager, loading, events, libraryBrowser, imageLoader, AlphaPicker, listView, cardBuilder, userSettings, globalize) {
'use strict';
return function (view, params, tabContent) {
@ -215,7 +215,7 @@ define(['layoutManager', 'playbackManager', 'loading', 'events', 'libraryBrowser
query.StartIndex = 0;
reloadItems(tabContent);
});
self.alphaPicker = new alphaPicker({
self.alphaPicker = new AlphaPicker.default({
element: alphaPickerElement,
valueChangeEvent: 'click'
});

View file

@ -1,4 +1,4 @@
define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', 'alphaPicker', 'listView', 'cardBuilder', 'apphost', 'userSettings', 'emby-itemscontainer'], function (layoutManager, loading, events, libraryBrowser, imageLoader, alphaPicker, listView, cardBuilder, appHost, userSettings) {
define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', 'alphaPicker', 'listView', 'cardBuilder', 'apphost', 'userSettings', 'emby-itemscontainer'], function (layoutManager, loading, events, libraryBrowser, imageLoader, AlphaPicker, listView, cardBuilder, appHost, userSettings) {
'use strict';
return function (view, params, tabContent) {
@ -199,7 +199,7 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', '
query.StartIndex = 0;
reloadItems(tabContent);
});
self.alphaPicker = new alphaPicker({
self.alphaPicker = new AlphaPicker.default({
element: alphaPickerElement,
valueChangeEvent: 'click'
});

View file

@ -1,4 +1,4 @@
define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardBuilder', 'alphaPicker', 'userSettings', 'globalize', 'emby-itemscontainer'], function (layoutManager, loading, events, libraryBrowser, imageLoader, listView, cardBuilder, alphaPicker, userSettings, globalize) {
define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardBuilder', 'alphaPicker', 'userSettings', 'globalize', 'emby-itemscontainer'], function (layoutManager, loading, events, libraryBrowser, imageLoader, listView, cardBuilder, AlphaPicker, userSettings, globalize) {
'use strict';
return function (view, params, tabContent) {
@ -226,7 +226,7 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', '
query.StartIndex = 0;
reloadItems(tabContent);
});
self.alphaPicker = new alphaPicker({
self.alphaPicker = new AlphaPicker.default({
element: alphaPickerElement,
valueChangeEvent: 'click'
});

View file

@ -21,7 +21,7 @@ define(['displaySettings', 'userSettings', 'autoFocuser'], function (DisplaySett
if (settingsInstance) {
settingsInstance.loadData();
} else {
settingsInstance = new DisplaySettings({
settingsInstance = new DisplaySettings.default({
serverId: ApiClient.serverId(),
userId: userId,
element: view.querySelector('.settingsContainer'),

View file

@ -21,7 +21,7 @@ define(['homescreenSettings', 'dom', 'globalize', 'loading', 'userSettings', 'au
if (homescreenSettingsInstance) {
homescreenSettingsInstance.loadData();
} else {
homescreenSettingsInstance = new HomescreenSettings({
homescreenSettingsInstance = new HomescreenSettings.default({
serverId: ApiClient.serverId(),
userId: userId,
element: view.querySelector('.homeScreenSettingsContainer'),

View file

@ -21,7 +21,7 @@ define(['playbackSettings', 'dom', 'globalize', 'loading', 'userSettings', 'auto
if (settingsInstance) {
settingsInstance.loadData();
} else {
settingsInstance = new PlaybackSettings({
settingsInstance = new PlaybackSettings.default({
serverId: ApiClient.serverId(),
userId: userId,
element: view.querySelector('.settingsContainer'),

View file

@ -1,52 +1,63 @@
define(['subtitleSettings', 'userSettings', 'autoFocuser'], function (SubtitleSettings, userSettings, autoFocuser) {
'use strict';
import subtitleSettings from 'subtitleSettings';
import {UserSettings, currentSettings as userSettings} from 'userSettings';
import autoFocuser from 'autoFocuser';
// Shortcuts
const UserSettings = userSettings.UserSettings;
export class SubtitleController {
constructor(view, params) {
this.userId = params.userId || ApiClient.getCurrentUserId();
this.currentSettings = this.userId === ApiClient.getCurrentUserId() ? userSettings : new UserSettings();
this.hasChanges = false;
this.subtitleSettingsInstance = null;
this.view = view;
return function (view, params) {
function onBeforeUnload(e) {
if (hasChanges) {
e.returnValue = 'You currently have unsaved changes. Are you sure you wish to leave?';
}
view.addEventListener('viewshow', this.viewShow.bind(this));
view.addEventListener('change', this.change.bind(this));
view.addEventListener('viewbeforehide', this.viewBeforeHide.bind(this));
view.addEventListener('viewdestroy', this.viewDestroy.bind(this));
}
viewShow() {
window.addEventListener('beforeunload', this.beforeUnload.bind(this));
if (this.subtitleSettingsInstance) {
this.subtitleSettingsInstance.loadData();
} else {
this.subtitleSettingsInstance = new subtitleSettings({
serverId: ApiClient.serverId(),
userId: this.userId,
element: this.view.querySelector('.settingsContainer'),
userSettings: this.currentSettings,
enableSaveButton: false,
enableSaveConfirmation: false,
autoFocus: autoFocuser.isEnabled()
});
}
}
var subtitleSettingsInstance;
var hasChanges;
var userId = params.userId || ApiClient.getCurrentUserId();
var currentSettings = userId === ApiClient.getCurrentUserId() ? userSettings : new UserSettings();
view.addEventListener('viewshow', function () {
window.addEventListener('beforeunload', onBeforeUnload);
viewDestroy() {
if (this.subtitleSettingsInstance) {
this.subtitleSettingsInstance.destroy();
this.subtitleSettingsInstance = null;
}
}
if (subtitleSettingsInstance) {
subtitleSettingsInstance.loadData();
} else {
subtitleSettingsInstance = new SubtitleSettings({
serverId: ApiClient.serverId(),
userId: userId,
element: view.querySelector('.settingsContainer'),
userSettings: currentSettings,
enableSaveButton: false,
enableSaveConfirmation: false,
autoFocus: autoFocuser.isEnabled()
});
}
});
view.addEventListener('change', function () {
hasChanges = true;
});
view.addEventListener('viewbeforehide', function () {
hasChanges = false;
viewBeforeHide() {
this.hasChanges = false;
if (subtitleSettingsInstance) {
subtitleSettingsInstance.submit();
}
});
view.addEventListener('viewdestroy', function () {
if (subtitleSettingsInstance) {
subtitleSettingsInstance.destroy();
subtitleSettingsInstance = null;
}
});
};
});
if (this.subtitleSettingsInstance) {
this.subtitleSettingsInstance.submit();
}
}
change() {
this.hasChanges = true;
}
beforeUnload(e) {
if (this.hasChanges) {
e.returnValue = 'You currently have unsaved changes. Are you sure you wish to leave?';
}
}
}
export default SubtitleController;

View file

@ -1,7 +1,11 @@
define(['browser', 'dom', 'css!./emby-checkbox', 'registerElement'], function (browser, dom) {
'use strict';
import browser from 'browser';
import dom from 'dom';
import 'css!./emby-checkbox';
import 'registerElement';
var EmbyCheckboxPrototype = Object.create(HTMLInputElement.prototype);
/* eslint-disable indent */
let EmbyCheckboxPrototype = Object.create(HTMLInputElement.prototype);
function onKeyDown(e) {
// Don't submit form on enter
@ -19,10 +23,10 @@ define(['browser', 'dom', 'css!./emby-checkbox', 'registerElement'], function (b
}
}
var enableRefreshHack = browser.tizen || browser.orsay || browser.operaTv || browser.web0s ? true : false;
const enableRefreshHack = browser.tizen || browser.orsay || browser.operaTv || browser.web0s ? true : false;
function forceRefresh(loading) {
var elem = this.parentNode;
let elem = this.parentNode;
elem.style.webkitAnimationName = 'repaintChrome';
elem.style.webkitAnimationDelay = (loading === true ? '500ms' : '');
@ -43,22 +47,22 @@ define(['browser', 'dom', 'css!./emby-checkbox', 'registerElement'], function (b
this.classList.add('emby-checkbox');
var labelElement = this.parentNode;
const labelElement = this.parentNode;
labelElement.classList.add('emby-checkbox-label');
var labelTextElement = labelElement.querySelector('span');
const labelTextElement = labelElement.querySelector('span');
var outlineClass = 'checkboxOutline';
let outlineClass = 'checkboxOutline';
var customClass = this.getAttribute('data-outlineclass');
const customClass = this.getAttribute('data-outlineclass');
if (customClass) {
outlineClass += ' ' + customClass;
}
var checkedIcon = this.getAttribute('data-checkedicon') || 'check';
var uncheckedIcon = this.getAttribute('data-uncheckedicon') || '';
var checkHtml = '<span class="material-icons checkboxIcon checkboxIcon-checked ' + checkedIcon + '"></span>';
var uncheckedHtml = '<span class="material-icons checkboxIcon checkboxIcon-unchecked ' + uncheckedIcon + '"></span>';
const checkedIcon = this.getAttribute('data-checkedicon') || 'check';
const uncheckedIcon = this.getAttribute('data-uncheckedicon') || '';
const checkHtml = '<span class="material-icons checkboxIcon checkboxIcon-checked ' + checkedIcon + '"></span>';
const uncheckedHtml = '<span class="material-icons checkboxIcon checkboxIcon-unchecked ' + uncheckedIcon + '"></span>';
labelElement.insertAdjacentHTML('beforeend', '<span class="' + outlineClass + '">' + checkHtml + uncheckedHtml + '</span>');
labelTextElement.classList.add('checkboxLabel');
@ -103,4 +107,5 @@ define(['browser', 'dom', 'css!./emby-checkbox', 'registerElement'], function (b
prototype: EmbyCheckboxPrototype,
extends: 'input'
});
});
/* eslint-enable indent */

View file

@ -109,9 +109,7 @@ define(['layoutManager', 'browser', 'dom', 'css!./emby-input', 'registerElement'
}
EmbyInputPrototype.attachedCallback = function () {
this.labelElement.htmlFor = this.id;
onChange.call(this);
};

View file

@ -1,16 +1,21 @@
define(['emby-progressring', 'dom', 'serverNotifications', 'events', 'registerElement'], function (EmbyProgressRing, dom, serverNotifications, events) {
'use strict';
import EmbyProgressRing from 'emby-progressring';
import dom from 'dom';
import serverNotifications from 'serverNotifications';
import events from 'events';
import 'registerElement';
/* eslint-disable indent */
function addNotificationEvent(instance, name, handler) {
var localHandler = handler.bind(instance);
const localHandler = handler.bind(instance);
events.on(serverNotifications, name, localHandler);
instance[name] = localHandler;
}
function removeNotificationEvent(instance, name) {
var handler = instance[name];
const handler = instance[name];
if (handler) {
events.off(serverNotifications, name, handler);
instance[name] = null;
@ -19,7 +24,7 @@ define(['emby-progressring', 'dom', 'serverNotifications', 'events', 'registerEl
function onRefreshProgress(e, apiClient, info) {
var indicator = this;
const indicator = this;
if (!indicator.itemId) {
indicator.itemId = dom.parentWithAttribute(indicator, 'data-id').getAttribute('data-id');
@ -27,7 +32,7 @@ define(['emby-progressring', 'dom', 'serverNotifications', 'events', 'registerEl
if (info.ItemId === indicator.itemId) {
var progress = parseFloat(info.Progress);
const progress = parseFloat(info.Progress);
if (progress && progress < 100) {
this.classList.remove('hide');
@ -39,7 +44,7 @@ define(['emby-progressring', 'dom', 'serverNotifications', 'events', 'registerEl
}
}
var EmbyItemRefreshIndicatorPrototype = Object.create(EmbyProgressRing);
let EmbyItemRefreshIndicatorPrototype = Object.create(EmbyProgressRing);
EmbyItemRefreshIndicatorPrototype.createdCallback = function () {
@ -74,4 +79,5 @@ define(['emby-progressring', 'dom', 'serverNotifications', 'events', 'registerEl
prototype: EmbyItemRefreshIndicatorPrototype,
extends: 'div'
});
});
/* eslint-enable indent */

View file

@ -1,20 +1,19 @@
define([], function() {
'use strict';
/* eslint-disable indent */
var ProgressBarPrototype = Object.create(HTMLDivElement.prototype);
let ProgressBarPrototype = Object.create(HTMLDivElement.prototype);
function onAutoTimeProgress() {
var start = parseInt(this.getAttribute('data-starttime'));
var end = parseInt(this.getAttribute('data-endtime'));
const start = parseInt(this.getAttribute('data-starttime'));
const end = parseInt(this.getAttribute('data-endtime'));
var now = new Date().getTime();
var total = end - start;
var pct = 100 * ((now - start) / total);
const now = new Date().getTime();
const total = end - start;
let pct = 100 * ((now - start) / total);
pct = Math.min(100, pct);
pct = Math.max(0, pct);
var itemProgressBarForeground = this.querySelector('.itemProgressBarForeground');
const itemProgressBarForeground = this.querySelector('.itemProgressBarForeground');
itemProgressBarForeground.style.width = pct + '%';
}
@ -39,4 +38,5 @@ define([], function() {
prototype: ProgressBarPrototype,
extends: 'div'
});
});
/* eslint-enable indent */

View file

@ -1,12 +1,15 @@
define(['require', 'css!./emby-progressring', 'registerElement'], function (require) {
'use strict';
import require from 'require';
import 'css!./emby-progressring';
import 'webcomponents';
var EmbyProgressRing = Object.create(HTMLDivElement.prototype);
/* eslint-disable indent */
let EmbyProgressRing = Object.create(HTMLDivElement.prototype);
EmbyProgressRing.createdCallback = function () {
this.classList.add('progressring');
var instance = this;
const instance = this;
require(['text!./emby-progressring.template.html'], function (template) {
instance.innerHTML = template;
@ -37,7 +40,7 @@ define(['require', 'css!./emby-progressring', 'registerElement'], function (requ
progress = Math.floor(progress);
var angle;
let angle;
if (progress < 25) {
angle = -90 + (progress / 100) * 360;
@ -82,7 +85,7 @@ define(['require', 'css!./emby-progressring', 'registerElement'], function (requ
EmbyProgressRing.detachedCallback = function () {
var observer = this.observer;
let observer = this.observer;
if (observer) {
// later, you can stop observing
@ -97,5 +100,6 @@ define(['require', 'css!./emby-progressring', 'registerElement'], function (requ
extends: 'div'
});
return EmbyProgressRing;
});
export default EmbyProgressRing;
/* eslint-enable indent */

View file

@ -1,7 +1,10 @@
define(['layoutManager', 'css!./emby-radio', 'registerElement'], function (layoutManager) {
'use strict';
import layoutManager from 'layoutManager';
import 'css!./emby-radio';
import 'registerElement';
var EmbyRadioPrototype = Object.create(HTMLInputElement.prototype);
/* eslint-disable indent */
let EmbyRadioPrototype = Object.create(HTMLInputElement.prototype);
function onKeyDown(e) {
@ -23,7 +26,7 @@ define(['layoutManager', 'css!./emby-radio', 'registerElement'], function (layou
}
EmbyRadioPrototype.attachedCallback = function () {
var showFocus = !layoutManager.mobile;
const showFocus = !layoutManager.mobile;
if (this.getAttribute('data-radio') === 'true') {
return;
@ -33,7 +36,7 @@ define(['layoutManager', 'css!./emby-radio', 'registerElement'], function (layou
this.classList.add('mdl-radio__button');
var labelElement = this.parentNode;
let labelElement = this.parentNode;
//labelElement.classList.add('"mdl-radio mdl-js-radio mdl-js-ripple-effect');
labelElement.classList.add('mdl-radio');
labelElement.classList.add('mdl-js-radio');
@ -42,12 +45,12 @@ define(['layoutManager', 'css!./emby-radio', 'registerElement'], function (layou
labelElement.classList.add('show-focus');
}
var labelTextElement = labelElement.querySelector('span');
let labelTextElement = labelElement.querySelector('span');
labelTextElement.classList.add('radioButtonLabel');
labelTextElement.classList.add('mdl-radio__label');
var html = '';
let html = '';
html += '<div class="mdl-radio__circles">';
@ -76,4 +79,5 @@ define(['layoutManager', 'css!./emby-radio', 'registerElement'], function (layou
prototype: EmbyRadioPrototype,
extends: 'input'
});
});
/* eslint-enable indent */

View file

@ -1,16 +1,21 @@
define(['connectionManager', 'serverNotifications', 'events', 'globalize', 'emby-button'], function (connectionManager, serverNotifications, events, globalize, EmbyButtonPrototype) {
'use strict';
import connectionManager from 'connectionManager';
import serverNotifications from 'serverNotifications';
import events from 'events';
import globalize from 'globalize';
import EmbyButtonPrototype from 'emby-button';
/* eslint-disable indent */
function addNotificationEvent(instance, name, handler) {
var localHandler = handler.bind(instance);
const localHandler = handler.bind(instance);
events.on(serverNotifications, name, localHandler);
instance[name] = localHandler;
}
function removeNotificationEvent(instance, name) {
var handler = instance[name];
const handler = instance[name];
if (handler) {
events.off(serverNotifications, name, handler);
instance[name] = null;
@ -24,13 +29,13 @@ define(['connectionManager', 'serverNotifications', 'events', 'globalize', 'emby
function onClick(e) {
var button = this;
var id = button.getAttribute('data-id');
var serverId = button.getAttribute('data-serverid');
var apiClient = connectionManager.getApiClient(serverId);
const button = this;
const id = button.getAttribute('data-id');
const serverId = button.getAttribute('data-serverid');
const apiClient = connectionManager.getApiClient(serverId);
var likes = this.getAttribute('data-likes');
var isFavorite = this.getAttribute('data-isfavorite') === 'true';
let likes = this.getAttribute('data-likes');
const isFavorite = this.getAttribute('data-isfavorite') === 'true';
if (likes === 'true') {
likes = true;
} else if (likes === 'false') {
@ -47,7 +52,7 @@ define(['connectionManager', 'serverNotifications', 'events', 'globalize', 'emby
function onUserDataChanged(e, apiClient, userData) {
var button = this;
const button = this;
if (userData.ItemId === button.getAttribute('data-id')) {
@ -57,7 +62,7 @@ define(['connectionManager', 'serverNotifications', 'events', 'globalize', 'emby
function setState(button, likes, isFavorite, updateAttribute) {
var icon = button.querySelector('.material-icons');
const icon = button.querySelector('.material-icons');
if (isFavorite) {
@ -106,7 +111,7 @@ define(['connectionManager', 'serverNotifications', 'events', 'globalize', 'emby
function setTitle(button) {
button.title = globalize.translate('Favorite');
var text = button.querySelector('.button-text');
const text = button.querySelector('.button-text');
if (text) {
text.innerHTML = button.title;
}
@ -126,7 +131,7 @@ define(['connectionManager', 'serverNotifications', 'events', 'globalize', 'emby
addNotificationEvent(button, 'UserDataChanged', onUserDataChanged);
}
var EmbyRatingButtonPrototype = Object.create(EmbyButtonPrototype);
const EmbyRatingButtonPrototype = Object.create(EmbyButtonPrototype);
EmbyRatingButtonPrototype.createdCallback = function () {
@ -143,12 +148,12 @@ define(['connectionManager', 'serverNotifications', 'events', 'globalize', 'emby
EmbyButtonPrototype.attachedCallback.call(this);
}
var itemId = this.getAttribute('data-id');
var serverId = this.getAttribute('data-serverid');
const itemId = this.getAttribute('data-id');
const serverId = this.getAttribute('data-serverid');
if (itemId && serverId) {
var likes = this.getAttribute('data-likes');
var isFavorite = this.getAttribute('data-isfavorite') === 'true';
let likes = this.getAttribute('data-likes');
const isFavorite = this.getAttribute('data-isfavorite') === 'true';
if (likes === 'true') {
likes = true;
} else if (likes === 'false') {
@ -181,7 +186,7 @@ define(['connectionManager', 'serverNotifications', 'events', 'globalize', 'emby
this.setAttribute('data-id', item.Id);
this.setAttribute('data-serverid', item.ServerId);
var userData = item.UserData || {};
const userData = item.UserData || {};
setState(this, userData.Likes, userData.IsFavorite);
bindEvents(this);
@ -199,4 +204,5 @@ define(['connectionManager', 'serverNotifications', 'events', 'globalize', 'emby
prototype: EmbyRatingButtonPrototype,
extends: 'button'
});
});
/* eslint-enable indent */

View file

@ -1,13 +1,18 @@
define(['layoutManager', 'dom', 'css!./emby-scrollbuttons', 'registerElement', 'paper-icon-button-light'], function (layoutManager, dom) {
'use strict';
import layoutManager from 'layoutManager';
import dom from 'dom';
import 'css!./emby-scrollbuttons';
import 'registerElement';
import 'paper-icon-button-light';
var EmbyScrollButtonsPrototype = Object.create(HTMLDivElement.prototype);
/* eslint-disable indent */
const EmbyScrollButtonsPrototype = Object.create(HTMLDivElement.prototype);
EmbyScrollButtonsPrototype.createdCallback = function () {};
function getScrollButtonHtml(direction) {
var html = '';
var icon = direction === 'left' ? 'chevron_left' : 'chevron_right';
let html = '';
const icon = direction === 'left' ? 'chevron_left' : 'chevron_right';
html += '<button type="button" is="paper-icon-button-light" data-ripple="false" data-direction="' + direction + '" class="emby-scrollbuttons-button">';
html += '<span class="material-icons ' + icon + '"></span>';
@ -45,7 +50,7 @@ define(['layoutManager', 'dom', 'css!./emby-scrollbuttons', 'registerElement', '
scrollButtons.scrollButtonsLeft.disabled = true;
}
var scrollPosEnd = scrollPos + scrollSize;
const scrollPosEnd = scrollPos + scrollSize;
if (scrollWidth > 0 && scrollPosEnd >= scrollWidth) {
scrollButtons.scrollButtonsRight.disabled = true;
} else {
@ -54,18 +59,18 @@ define(['layoutManager', 'dom', 'css!./emby-scrollbuttons', 'registerElement', '
}
function onScroll(e) {
var scrollButtons = this;
var scroller = this.scroller;
const scrollButtons = this;
const scroller = this.scroller;
var scrollSize = getScrollSize(scroller);
var scrollPos = getScrollPosition(scroller);
var scrollWidth = getScrollWidth(scroller);
const scrollSize = getScrollSize(scroller);
const scrollPos = getScrollPosition(scroller);
const scrollWidth = getScrollWidth(scroller);
updateScrollButtons(scrollButtons, scrollSize, scrollPos, scrollWidth);
}
function getStyleValue(style, name) {
var value = style.getPropertyValue(name);
let value = style.getPropertyValue(name);
if (!value) {
return 0;
}
@ -84,20 +89,20 @@ define(['layoutManager', 'dom', 'css!./emby-scrollbuttons', 'registerElement', '
}
function getScrollSize(elem) {
var scrollSize = elem.offsetWidth;
var style = window.getComputedStyle(elem, null);
let scrollSize = elem.offsetWidth;
let style = window.getComputedStyle(elem, null);
var paddingLeft = getStyleValue(style, 'padding-left');
let paddingLeft = getStyleValue(style, 'padding-left');
if (paddingLeft) {
scrollSize -= paddingLeft;
}
var paddingRight = getStyleValue(style, 'padding-right');
let paddingRight = getStyleValue(style, 'padding-right');
if (paddingRight) {
scrollSize -= paddingRight;
}
var slider = elem.getScrollSlider();
const slider = elem.getScrollSlider();
style = window.getComputedStyle(slider, null);
paddingLeft = getStyleValue(style, 'padding-left');
@ -114,14 +119,14 @@ define(['layoutManager', 'dom', 'css!./emby-scrollbuttons', 'registerElement', '
}
function onScrollButtonClick(e) {
var scroller = this.parentNode.nextSibling;
let scroller = this.parentNode.nextSibling;
var direction = this.getAttribute('data-direction');
var scrollSize = getScrollSize(scroller);
var scrollPos = getScrollPosition(scroller);
var scrollWidth = getScrollWidth(scroller);
const direction = this.getAttribute('data-direction');
const scrollSize = getScrollSize(scroller);
const scrollPos = getScrollPosition(scroller);
const scrollWidth = getScrollWidth(scroller);
var newPos;
let newPos;
if (direction === 'left') {
newPos = Math.max(0, scrollPos - scrollSize);
} else {
@ -132,21 +137,21 @@ define(['layoutManager', 'dom', 'css!./emby-scrollbuttons', 'registerElement', '
}
EmbyScrollButtonsPrototype.attachedCallback = function () {
var scroller = this.nextSibling;
const scroller = this.nextSibling;
this.scroller = scroller;
var parent = this.parentNode;
const parent = this.parentNode;
parent.classList.add('emby-scroller-container');
this.innerHTML = getScrollButtonHtml('left') + getScrollButtonHtml('right');
var buttons = this.querySelectorAll('.emby-scrollbuttons-button');
const buttons = this.querySelectorAll('.emby-scrollbuttons-button');
buttons[0].addEventListener('click', onScrollButtonClick);
buttons[1].addEventListener('click', onScrollButtonClick);
this.scrollButtonsLeft = buttons[0];
this.scrollButtonsRight = buttons[1];
var scrollHandler = onScroll.bind(this);
const scrollHandler = onScroll.bind(this);
this.scrollHandler = scrollHandler;
scroller.addScrollEventListener(scrollHandler, {
capture: false,
@ -155,10 +160,10 @@ define(['layoutManager', 'dom', 'css!./emby-scrollbuttons', 'registerElement', '
};
EmbyScrollButtonsPrototype.detachedCallback = function () {
var parent = this.scroller;
const parent = this.scroller;
this.scroller = null;
var scrollHandler = this.scrollHandler;
let scrollHandler = this.scrollHandler;
if (parent && scrollHandler) {
parent.removeScrollEventListener(scrollHandler, {
capture: false,
@ -175,4 +180,5 @@ define(['layoutManager', 'dom', 'css!./emby-scrollbuttons', 'registerElement', '
prototype: EmbyScrollButtonsPrototype,
extends: 'div'
});
});
/* eslint-enable indent */

View file

@ -1,7 +1,15 @@
define(['scroller', 'dom', 'layoutManager', 'inputManager', 'focusManager', 'browser', 'registerElement', 'css!./emby-scroller'], function (scroller, dom, layoutManager, inputManager, focusManager, browser) {
'use strict';
import scroller from 'scroller';
import dom from 'dom';
import layoutManager from 'layoutManager';
import inputManager from 'inputManager';
import focusManager from 'focusManager';
import browser from 'browser';
import 'registerElement';
import 'css!./emby-scroller';
var ScrollerPrototype = Object.create(HTMLDivElement.prototype);
/* eslint-disable indent */
let ScrollerPrototype = Object.create(HTMLDivElement.prototype);
ScrollerPrototype.createdCallback = function () {
this.classList.add('emby-scroller');
@ -9,7 +17,7 @@ define(['scroller', 'dom', 'layoutManager', 'inputManager', 'focusManager', 'bro
function initCenterFocus(elem, scrollerInstance) {
dom.addEventListener(elem, 'focus', function (e) {
var focused = focusManager.focusableParent(e.target);
const focused = focusManager.focusableParent(e.target);
if (focused) {
scrollerInstance.toCenter(focused);
}
@ -80,7 +88,7 @@ define(['scroller', 'dom', 'layoutManager', 'inputManager', 'focusManager', 'bro
};
function onInputCommand(e) {
var cmd = e.detail.command;
const cmd = e.detail.command;
if (cmd === 'end') {
focusManager.focusLast(this, '.' + this.getAttribute('data-navcommands'));
e.preventDefault();
@ -101,18 +109,18 @@ define(['scroller', 'dom', 'layoutManager', 'inputManager', 'focusManager', 'bro
inputManager.on(this, onInputCommand);
}
var horizontal = this.getAttribute('data-horizontal') !== 'false';
const horizontal = this.getAttribute('data-horizontal') !== 'false';
var slider = this.querySelector('.scrollSlider');
const slider = this.querySelector('.scrollSlider');
if (horizontal) {
slider.style['white-space'] = 'nowrap';
}
var scrollFrame = this;
var enableScrollButtons = layoutManager.desktop && horizontal && this.getAttribute('data-scrollbuttons') !== 'false';
const scrollFrame = this;
const enableScrollButtons = layoutManager.desktop && horizontal && this.getAttribute('data-scrollbuttons') !== 'false';
var options = {
const options = {
horizontal: horizontal,
mouseDragging: 1,
mouseWheel: this.getAttribute('data-mousewheel') !== 'false',
@ -154,14 +162,14 @@ define(['scroller', 'dom', 'layoutManager', 'inputManager', 'focusManager', 'bro
}
ScrollerPrototype.pause = function () {
var headroom = this.headroom;
const headroom = this.headroom;
if (headroom) {
headroom.pause();
}
};
ScrollerPrototype.resume = function () {
var headroom = this.headroom;
const headroom = this.headroom;
if (headroom) {
headroom.resume();
}
@ -172,13 +180,13 @@ define(['scroller', 'dom', 'layoutManager', 'inputManager', 'focusManager', 'bro
inputManager.off(this, onInputCommand);
}
var headroom = this.headroom;
const headroom = this.headroom;
if (headroom) {
headroom.destroy();
this.headroom = null;
}
var scrollerInstance = this.scroller;
const scrollerInstance = this.scroller;
if (scrollerInstance) {
scrollerInstance.destroy();
this.scroller = null;
@ -189,4 +197,5 @@ define(['scroller', 'dom', 'layoutManager', 'inputManager', 'focusManager', 'bro
prototype: ScrollerPrototype,
extends: 'div'
});
});
/* eslint-enable indent */

View file

@ -1,7 +1,12 @@
define(['layoutManager', 'browser', 'actionsheet', 'css!./emby-select', 'registerElement'], function (layoutManager, browser, actionsheet) {
'use strict';
import layoutManager from 'layoutManager';
import browser from 'browser';
import actionsheet from 'actionsheet';
import 'css!./emby-select';
import 'registerElement';
var EmbySelectPrototype = Object.create(HTMLSelectElement.prototype);
/* eslint-disable indent */
const EmbySelectPrototype = Object.create(HTMLSelectElement.prototype);
function enableNativeMenu() {
@ -27,7 +32,7 @@ define(['layoutManager', 'browser', 'actionsheet', 'css!./emby-select', 'registe
}
function triggerChange(select) {
var evt = document.createEvent('HTMLEvents');
const evt = document.createEvent('HTMLEvents');
evt.initEvent('change', false, true);
select.dispatchEvent(evt);
}
@ -39,8 +44,8 @@ define(['layoutManager', 'browser', 'actionsheet', 'css!./emby-select', 'registe
function showActionSheet(select) {
var labelElem = getLabel(select);
var title = labelElem ? (labelElem.textContent || labelElem.innerText) : null;
const labelElem = getLabel(select);
const title = labelElem ? (labelElem.textContent || labelElem.innerText) : null;
actionsheet.show({
items: select.options,
@ -54,7 +59,7 @@ define(['layoutManager', 'browser', 'actionsheet', 'css!./emby-select', 'registe
}
function getLabel(select) {
var elem = select.previousSibling;
let elem = select.previousSibling;
while (elem && elem.tagName !== 'LABEL') {
elem = elem.previousSibling;
}
@ -62,21 +67,20 @@ define(['layoutManager', 'browser', 'actionsheet', 'css!./emby-select', 'registe
}
function onFocus(e) {
var label = getLabel(this);
const label = getLabel(this);
if (label) {
label.classList.add('selectLabelFocused');
}
}
function onBlur(e) {
var label = getLabel(this);
const label = getLabel(this);
if (label) {
label.classList.remove('selectLabelFocused');
}
}
function onMouseDown(e) {
// e.button=0 for primary (left) mouse button click
if (!e.button && !enableNativeMenu()) {
e.preventDefault();
@ -85,9 +89,7 @@ define(['layoutManager', 'browser', 'actionsheet', 'css!./emby-select', 'registe
}
function onKeyDown(e) {
switch (e.keyCode) {
case 13:
if (!enableNativeMenu()) {
e.preventDefault();
@ -107,7 +109,7 @@ define(['layoutManager', 'browser', 'actionsheet', 'css!./emby-select', 'registe
}
}
var inputId = 0;
let inputId = 0;
EmbySelectPrototype.createdCallback = function () {
@ -137,7 +139,7 @@ define(['layoutManager', 'browser', 'actionsheet', 'css!./emby-select', 'registe
this.classList.add('emby-select');
var label = this.ownerDocument.createElement('label');
const label = this.ownerDocument.createElement('label');
label.innerHTML = this.getAttribute('label') || '';
label.classList.add('selectLabel');
label.htmlFor = this.id;
@ -150,7 +152,7 @@ define(['layoutManager', 'browser', 'actionsheet', 'css!./emby-select', 'registe
EmbySelectPrototype.setLabel = function (text) {
var label = this.parentNode.querySelector('label');
const label = this.parentNode.querySelector('label');
label.innerHTML = text;
};
@ -159,4 +161,5 @@ define(['layoutManager', 'browser', 'actionsheet', 'css!./emby-select', 'registe
prototype: EmbySelectPrototype,
extends: 'select'
});
});
/* eslint-enable indent */

View file

@ -1,15 +1,22 @@
define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-slider', 'registerElement', 'emby-input'], function (browser, dom, layoutManager, keyboardnavigation) {
'use strict';
import browser from 'browser';
import dom from 'dom';
import layoutManager from 'layoutManager';
import keyboardnavigation from 'keyboardnavigation';
import 'css!./emby-slider';
import 'webcomponents';
import 'emby-input';
var EmbySliderPrototype = Object.create(HTMLInputElement.prototype);
/* eslint-disable indent */
var supportsValueSetOverride = false;
let EmbySliderPrototype = Object.create(HTMLInputElement.prototype);
var enableWidthWithTransform;
let supportsValueSetOverride = false;
let enableWidthWithTransform;
if (Object.getOwnPropertyDescriptor && Object.defineProperty) {
var descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
const descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
// descriptor returning null in webos
if (descriptor && descriptor.configurable) {
supportsValueSetOverride = true;
@ -24,14 +31,14 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
* @return {number} slider fraction
*/
function mapClientToFraction(range, clientX) {
var rect = range.sliderBubbleTrack.getBoundingClientRect();
const rect = range.sliderBubbleTrack.getBoundingClientRect();
var fraction = (clientX - rect.left) / rect.width;
let fraction = (clientX - rect.left) / rect.width;
// Snap to step
var valueRange = range.max - range.min;
const valueRange = range.max - range.min;
if (range.step !== 'any' && valueRange !== 0) {
var step = (range.step || 1) / valueRange;
const step = (range.step || 1) / valueRange;
fraction = Math.round(fraction / step) * step;
}
@ -46,7 +53,7 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
* @return {number} slider value
*/
function mapFractionToValue(range, fraction) {
var value = (range.max - range.min) * fraction;
let value = (range.max - range.min) * fraction;
// Snap to step
if (range.step !== 'any') {
@ -67,8 +74,8 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
* @return {number} slider fraction
*/
function mapValueToFraction(range, value) {
var valueRange = range.max - range.min;
var fraction = valueRange !== 0 ? (value - range.min) / valueRange : 0;
const valueRange = range.max - range.min;
const fraction = valueRange !== 0 ? (value - range.min) / valueRange : 0;
return Math.min(Math.max(fraction, 0), 1);
}
@ -84,18 +91,18 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
return;
}
var range = this;
var value = range.value;
const range = this;
const value = range.value;
// put this on a callback. Doing it within the event sometimes causes the slider to get hung up and not respond
// Keep only one per slider frame request
cancelAnimationFrame(range.updateValuesFrame);
range.updateValuesFrame = requestAnimationFrame(function () {
var backgroundLower = range.backgroundLower;
let backgroundLower = range.backgroundLower;
if (backgroundLower) {
var fraction = (value - range.min) / (range.max - range.min);
let fraction = (value - range.min) / (range.max - range.min);
if (enableWidthWithTransform) {
backgroundLower.style.transform = 'scaleX(' + (fraction) + ')';
@ -110,10 +117,10 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
function updateBubble(range, value, bubble, bubbleText) {
requestAnimationFrame(function () {
var bubbleTrackRect = range.sliderBubbleTrack.getBoundingClientRect();
var bubbleRect = bubble.getBoundingClientRect();
const bubbleTrackRect = range.sliderBubbleTrack.getBoundingClientRect();
const bubbleRect = bubble.getBoundingClientRect();
var bubblePos = bubbleTrackRect.width * value / 100;
let bubblePos = bubbleTrackRect.width * value / 100;
bubblePos = Math.min(Math.max(bubblePos, bubbleRect.width / 2), bubbleTrackRect.width - bubbleRect.width / 2);
bubble.style.left = bubblePos + 'px';
@ -158,10 +165,10 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
this.classList.add('show-focus');
}
var containerElement = this.parentNode;
const containerElement = this.parentNode;
containerElement.classList.add('mdl-slider-container');
var htmlToInsert = '';
let htmlToInsert = '';
htmlToInsert += '<div class="mdl-slider-background-flex-container">';
htmlToInsert += '<div class="mdl-slider-background-flex">';
@ -187,9 +194,9 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
this.sliderBubbleTrack = containerElement.querySelector('.sliderBubbleTrack');
this.backgroundLower = containerElement.querySelector('.mdl-slider-background-lower');
this.backgroundUpper = containerElement.querySelector('.mdl-slider-background-upper');
var sliderBubble = containerElement.querySelector('.sliderBubble');
const sliderBubble = containerElement.querySelector('.sliderBubble');
var hasHideClass = sliderBubble.classList.contains('hide');
let hasHideClass = sliderBubble.classList.contains('hide');
dom.addEventListener(this, 'input', function (e) {
this.dragging = true;
@ -198,7 +205,7 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
updateValues.call(this);
}
var bubbleValue = mapValueToFraction(this, this.value) * 100;
const bubbleValue = mapValueToFraction(this, this.value) * 100;
updateBubble(this, bubbleValue, sliderBubble);
if (hasHideClass) {
@ -223,10 +230,11 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
passive: true
});
/* eslint-disable-next-line compat/compat */
dom.addEventListener(this, (window.PointerEvent ? 'pointermove' : 'mousemove'), function (e) {
if (!this.dragging) {
var bubbleValue = mapClientToFraction(this, e.clientX) * 100;
const bubbleValue = mapClientToFraction(this, e.clientX) * 100;
updateBubble(this, bubbleValue, sliderBubble);
@ -240,6 +248,7 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
passive: true
});
/* eslint-disable-next-line compat/compat */
dom.addEventListener(this, (window.PointerEvent ? 'pointerleave' : 'mouseleave'), function () {
sliderBubble.classList.add('hide');
hasHideClass = true;
@ -256,7 +265,7 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
this.touched = true;
var fraction = mapClientToFraction(this, e.targetTouches[0].clientX);
const fraction = mapClientToFraction(this, e.targetTouches[0].clientX);
this.value = mapFractionToValue(this, fraction);
this.dispatchEvent(new Event('input', {
@ -276,7 +285,7 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
return;
}
var fraction = mapClientToFraction(this, e.targetTouches[0].clientX);
const fraction = mapClientToFraction(this, e.targetTouches[0].clientX);
this.value = mapFractionToValue(this, fraction);
this.dispatchEvent(new Event('input', {
@ -288,7 +297,7 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
});
dom.addEventListener(this, 'touchend', function (e) {
var range = this;
const range = this;
setTimeout(function () {
range.touched = false;
@ -314,12 +323,12 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
* Keyboard dragging timeout.
* After this delay "change" event will be fired.
*/
var KeyboardDraggingTimeout = 1000;
const KeyboardDraggingTimeout = 1000;
/**
* Keyboard dragging timer.
*/
var keyboardDraggingTimer;
let keyboardDraggingTimer;
/**
* Start keyboard dragging.
@ -346,7 +355,7 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
elem.keyboardDragging = false;
var event = new Event('change', {
const event = new Event('change', {
bubbles: true,
cancelable: false
});
@ -364,7 +373,7 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
elem.value = Math.max(elem.min, Math.min(elem.max, parseFloat(elem.value) + delta));
var event = new Event('input', {
const event = new Event('input', {
bubbles: true,
cancelable: false
});
@ -414,10 +423,10 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
function setRange(elem, startPercent, endPercent) {
var style = elem.style;
const style = elem.style;
style.left = Math.max(startPercent, 0) + '%';
var widthPercent = endPercent - startPercent;
const widthPercent = endPercent - startPercent;
style.width = Math.max(Math.min(widthPercent, 100), 0) + '%';
}
@ -438,7 +447,7 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
EmbySliderPrototype.setBufferedRanges = function (ranges, runtime, position) {
var elem = this.backgroundUpper;
const elem = this.backgroundUpper;
if (!elem) {
return;
}
@ -449,9 +458,7 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
position = (position / runtime) * 100;
}
for (var i = 0, length = ranges.length; i < length; i++) {
var range = ranges[i];
for (const range in ranges) {
if (position != null) {
if (position >= range.end) {
@ -468,7 +475,7 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
EmbySliderPrototype.setIsClear = function (isClear) {
var backgroundLower = this.backgroundLower;
const backgroundLower = this.backgroundLower;
if (backgroundLower) {
if (isClear) {
backgroundLower.classList.add('mdl-slider-background-lower-clear');
@ -479,7 +486,7 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
};
function startInterval(range) {
var interval = range.interval;
const interval = range.interval;
if (interval) {
clearInterval(interval);
}
@ -488,7 +495,7 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
EmbySliderPrototype.detachedCallback = function () {
var interval = this.interval;
const interval = this.interval;
if (interval) {
clearInterval(interval);
}
@ -501,4 +508,5 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
prototype: EmbySliderPrototype,
extends: 'input'
});
});
/* eslint-enable indent */

View file

@ -1,9 +1,17 @@
define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'registerElement', 'css!./emby-tabs', 'scrollStyles'], function (dom, scroller, browser, layoutManager, focusManager) {
'use strict';
import dom from 'dom';
import scroller from 'scroller';
import browser from 'browser';
import layoutManager from 'layoutManager';
import focusManager from 'focusManager';
import 'registerElement';
import 'css!./emby-tabs';
import 'scrollStyles';
var EmbyTabs = Object.create(HTMLDivElement.prototype);
var buttonClass = 'emby-tab-button';
var activeButtonClass = buttonClass + '-active';
/* eslint-disable indent */
let EmbyTabs = Object.create(HTMLDivElement.prototype);
const buttonClass = 'emby-tab-button';
const activeButtonClass = buttonClass + '-active';
function setActiveTabButton(tabs, newButton, oldButton, animate) {
@ -16,14 +24,14 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register
}
function removeActivePanelClass(tabs, index) {
var tabPanel = getTabPanel(tabs, index);
let tabPanel = getTabPanel(tabs, index);
if (tabPanel) {
tabPanel.classList.remove('is-active');
}
}
function addActivePanelClass(tabs, index) {
var tabPanel = getTabPanel(tabs, index);
let tabPanel = getTabPanel(tabs, index);
if (tabPanel) {
tabPanel.classList.add('is-active');
}
@ -31,9 +39,9 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register
function fadeInRight(elem) {
var pct = browser.mobile ? '4%' : '0.5%';
const pct = browser.mobile ? '4%' : '0.5%';
var keyframes = [
const keyframes = [
{ opacity: '0', transform: 'translate3d(' + pct + ', 0, 0)', offset: 0 },
{ opacity: '1', transform: 'none', offset: 1 }];
@ -56,7 +64,7 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register
removeActivePanelClass(tabs, previousIndex);
}
var newPanel = getTabPanel(tabs, index);
let newPanel = getTabPanel(tabs, index);
if (newPanel) {
// animate new panel ?
@ -70,10 +78,10 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register
function onClick(e) {
var tabs = this;
const tabs = this;
var current = tabs.querySelector('.' + activeButtonClass);
var tabButton = dom.parentWithClass(e.target, buttonClass);
const current = tabs.querySelector('.' + activeButtonClass);
const tabButton = dom.parentWithClass(e.target, buttonClass);
if (tabButton && tabButton !== current) {
@ -81,11 +89,11 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register
current.classList.remove(activeButtonClass);
}
var previousIndex = current ? parseInt(current.getAttribute('data-index')) : null;
const previousIndex = current ? parseInt(current.getAttribute('data-index')) : null;
setActiveTabButton(tabs, tabButton, current, true);
var index = parseInt(tabButton.getAttribute('data-index'));
const index = parseInt(tabButton.getAttribute('data-index'));
triggerBeforeTabChange(tabs, index, previousIndex);
@ -115,7 +123,7 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register
return;
}
var contentScrollSlider = tabs.querySelector('.emby-tabs-slider');
const contentScrollSlider = tabs.querySelector('.emby-tabs-slider');
if (contentScrollSlider) {
tabs.scroller = new scroller(tabs, {
horizontal: 1,
@ -160,7 +168,7 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register
EmbyTabs.focus = function () {
var selected = this.querySelector('.' + activeButtonClass);
const selected = this.querySelector('.' + activeButtonClass);
if (selected) {
focusManager.focus(selected);
@ -180,16 +188,16 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register
initScroller(this);
var current = this.querySelector('.' + activeButtonClass);
var currentIndex = current ? parseInt(current.getAttribute('data-index')) : parseInt(this.getAttribute('data-index') || '0');
const current = this.querySelector('.' + activeButtonClass);
const currentIndex = current ? parseInt(current.getAttribute('data-index')) : parseInt(this.getAttribute('data-index') || '0');
if (currentIndex !== -1) {
this.selectedTabIndex = currentIndex;
var tabButtons = this.querySelectorAll('.' + buttonClass);
const tabButtons = this.querySelectorAll('.' + buttonClass);
var newTabButton = tabButtons[currentIndex];
const newTabButton = tabButtons[currentIndex];
if (newTabButton) {
setActiveTabButton(this, newTabButton, current, false);
@ -221,18 +229,18 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register
EmbyTabs.selectedIndex = function (selected, triggerEvent) {
var tabs = this;
const tabs = this;
if (selected == null) {
return tabs.selectedTabIndex || 0;
}
var current = tabs.selectedIndex();
const current = tabs.selectedIndex();
tabs.selectedTabIndex = selected;
var tabButtons = tabs.querySelectorAll('.' + buttonClass);
const tabButtons = tabs.querySelectorAll('.' + buttonClass);
if (current === selected || triggerEvent === false) {
@ -244,7 +252,7 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register
}
}));
var currentTabButton = tabButtons[current];
let currentTabButton = tabButtons[current];
setActiveTabButton(tabs, tabButtons[selected], currentTabButton, false);
if (current !== selected && currentTabButton) {
@ -262,7 +270,7 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register
function getSibling(elem, method) {
var sibling = elem[method];
let sibling = elem[method];
while (sibling) {
if (sibling.classList.contains(buttonClass)) {
@ -280,9 +288,9 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register
EmbyTabs.selectNext = function () {
var current = getSelectedTabButton(this);
const current = getSelectedTabButton(this);
var sibling = getSibling(current, 'nextSibling');
const sibling = getSibling(current, 'nextSibling');
if (sibling) {
onClick.call(this, {
@ -293,9 +301,9 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register
EmbyTabs.selectPrevious = function () {
var current = getSelectedTabButton(this);
const current = getSelectedTabButton(this);
var sibling = getSibling(current, 'previousSibling');
const sibling = getSibling(current, 'previousSibling');
if (sibling) {
onClick.call(this, {
@ -306,14 +314,14 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register
EmbyTabs.triggerBeforeTabChange = function (selected) {
var tabs = this;
const tabs = this;
triggerBeforeTabChange(tabs, tabs.selectedIndex());
};
EmbyTabs.triggerTabChange = function (selected) {
var tabs = this;
const tabs = this;
tabs.dispatchEvent(new CustomEvent('tabchange', {
detail: {
@ -324,8 +332,8 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register
EmbyTabs.setTabEnabled = function (index, enabled) {
var tabs = this;
var btn = this.querySelector('.emby-tab-button[data-index="' + index + '"]');
const tabs = this;
const btn = this.querySelector('.emby-tab-button[data-index="' + index + '"]');
if (enabled) {
btn.classList.remove('hide');
@ -338,4 +346,5 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register
prototype: EmbyTabs,
extends: 'div'
});
});
/* eslint-enable indent */

View file

@ -1,8 +1,13 @@
define(['layoutManager', 'browser', 'css!./emby-textarea', 'registerElement', 'emby-input'], function (layoutManager, browser) {
'use strict';
import layoutManager from 'layoutManager';
import browser from 'browser';
import 'css!./emby-textarea';
import 'registerElement';
import 'emby-input';
/* eslint-disable indent */
function autoGrow(textarea, maxLines) {
var self = this;
const self = this;
if (maxLines === undefined) {
maxLines = 999;
@ -14,17 +19,17 @@ define(['layoutManager', 'browser', 'css!./emby-textarea', 'registerElement', 'e
* @returns {number}
*/
self.getOffset = function (textarea) {
var style = window.getComputedStyle(textarea, null);
var props = ['paddingTop', 'paddingBottom'];
var offset = 0;
const style = window.getComputedStyle(textarea, null);
const props = ['paddingTop', 'paddingBottom'];
let offset = 0;
for (var i = 0; i < props.length; i++) {
for (let i = 0; i < props.length; i++) {
offset += parseInt(style[props[i]]);
}
return offset;
};
var offset;
let offset;
function reset() {
textarea.rows = 1;
offset = self.getOffset(textarea);
@ -43,8 +48,8 @@ define(['layoutManager', 'browser', 'css!./emby-textarea', 'registerElement', 'e
textarea.rows = 3;
return;
}
var newHeight = 0;
var hasGrown = false;
let newHeight = 0;
let hasGrown = false;
if ((textarea.scrollHeight - offset) > self.maxAllowedHeight) {
textarea.style.overflowY = 'scroll';
@ -67,17 +72,17 @@ define(['layoutManager', 'browser', 'css!./emby-textarea', 'registerElement', 'e
autogrowFn();
}
var EmbyTextAreaPrototype = Object.create(HTMLTextAreaElement.prototype);
const EmbyTextAreaPrototype = Object.create(HTMLTextAreaElement.prototype);
var elementId = 0;
let elementId = 0;
if (Object.getOwnPropertyDescriptor && Object.defineProperty) {
var descriptor = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value');
const descriptor = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value');
// descriptor returning null in webos
if (descriptor && descriptor.configurable) {
var baseSetMethod = descriptor.set;
const baseSetMethod = descriptor.set;
descriptor.set = function (value) {
baseSetMethod.call(this, value);
@ -108,8 +113,8 @@ define(['layoutManager', 'browser', 'css!./emby-textarea', 'registerElement', 'e
this.rows = 1;
this.classList.add('emby-textarea');
var parentNode = this.parentNode;
var label = this.ownerDocument.createElement('label');
const parentNode = this.parentNode;
const label = this.ownerDocument.createElement('label');
label.innerHTML = this.getAttribute('label') || '';
label.classList.add('textareaLabel');
@ -136,4 +141,5 @@ define(['layoutManager', 'browser', 'css!./emby-textarea', 'registerElement', 'e
prototype: EmbyTextAreaPrototype,
extends: 'textarea'
});
});
/* eslint-enable indent */

View file

@ -1,7 +1,9 @@
define(['css!./emby-toggle', 'registerElement'], function () {
'use strict';
import 'css!./emby-toggle';
import 'registerElement';
var EmbyTogglePrototype = Object.create(HTMLInputElement.prototype);
/* eslint-disable indent */
const EmbyTogglePrototype = Object.create(HTMLInputElement.prototype);
function onKeyDown(e) {
@ -29,11 +31,11 @@ define(['css!./emby-toggle', 'registerElement'], function () {
this.classList.add('mdl-switch__input');
var labelElement = this.parentNode;
const labelElement = this.parentNode;
labelElement.classList.add('mdl-switch');
labelElement.classList.add('mdl-js-switch');
var labelTextElement = labelElement.querySelector('span');
const labelTextElement = labelElement.querySelector('span');
labelElement.insertAdjacentHTML('beforeend', '<div class="mdl-switch__trackContainer"><div class="mdl-switch__track"></div><div class="mdl-switch__thumb"><span class="mdl-switch__focus-helper"></span></div></div>');
@ -47,4 +49,5 @@ define(['css!./emby-toggle', 'registerElement'], function () {
prototype: EmbyTogglePrototype,
extends: 'input'
});
});
/* eslint-enable indent */

View file

@ -10,8 +10,8 @@
<div class="detailPagePrimaryContainer padded-left padded-right">
<div class="infoWrapper infoText">
<div class="nameContainer"></div>
<div class="itemMiscInfo itemMiscInfo-primary"></div>
<div class="itemMiscInfo itemMiscInfo-secondary"></div>
<div class="itemMiscInfo itemMiscInfo-primary" style="margin-bottom: 0.6em"></div>
<div class="itemMiscInfo itemMiscInfo-secondary" style="margin-bottom: 0.6em"></div>
</div>
<div class="mainDetailButtons">

19
src/repositories.html Normal file
View file

@ -0,0 +1,19 @@
<div id="repositories" data-role="page" class="page type-interior withTabs fullWidthContent">
<div>
<div class="content-primary">
<div class="sectionTitleContainer flex align-items-center">
<h2 class="sectionTitle">${TabRepositories}</h2>
<button is="emby-button" type="button" class="fab btnNewRepository submit" style="margin-left:1em;" title="${ButtonAdd}">
<span class="material-icons add" aria-hidden="true"></span>
</button>
</div>
<div id="repositories"></div>
<div id="none" class="noItemsMessage centerMessage hide">
<h1>${MessageNoRepositories}</h1>
<p>${MessageAddRepository}</p>
</div>
</div>
</div>
</div>

View file

@ -50,7 +50,7 @@ define(['backdrop', 'userSettings', 'libraryMenu'], function (backdrop, userSett
return i;
}));
} else {
backdrop.clear();
backdrop.clearBackdrop();
}
});
}
@ -67,10 +67,10 @@ define(['backdrop', 'userSettings', 'libraryMenu'], function (backdrop, userSett
showBackdrop(type, parentId);
} else {
page.classList.remove('backdropPage');
backdrop.clear();
backdrop.clearBackdrop();
}
} else {
backdrop.clear();
backdrop.clearBackdrop();
}
}
});

View file

@ -12,10 +12,10 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
html += '</div>';
html += '<div class="headerRight">';
html += '<span class="headerSelectedPlayer"></span>';
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="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"><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="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('ButtonSearch')}"><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>';
@ -64,7 +64,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
updateHeaderUserButton(url);
hasImage = true;
}
headerUserButton.title = user.name;
headerUserButton.classList.remove('hide');
} else {
headerUserButton.classList.add('hide');
@ -834,6 +834,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
var headerAudioPlayerButton;
var headerSyncButton;
var enableLibraryNavDrawer = layoutManager.desktop;
var enableLibraryNavDrawerHome = !layoutManager.tv;
var skinHeader = document.querySelector('.skinHeader');
var requiresUserRefresh = true;
var lastOpenTime = new Date().getTime();
@ -920,7 +921,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
refreshDashboardInfoInDrawer(apiClient);
} else {
if (mainDrawerButton) {
if (enableLibraryNavDrawer || isHomePage) {
if (enableLibraryNavDrawer || (isHomePage && enableLibraryNavDrawerHome)) {
mainDrawerButton.classList.remove('hide');
} else {
mainDrawerButton.classList.add('hide');

View file

@ -184,7 +184,7 @@ define(['loading', 'listView', 'cardBuilder', 'libraryMenu', 'libraryBrowser', '
view.querySelector('.btnNewPlaylist').addEventListener('click', function () {
require(['playlistEditor'], function (playlistEditor) {
var serverId = ApiClient.serverInfo().Id;
new playlistEditor().show({
new playlistEditor.showEditor({
items: [],
serverId: serverId
});

View file

@ -215,6 +215,12 @@ define([
roles: 'admin',
controller: 'dashboard/plugins/available'
});
defineRoute({
path: '/repositories.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/plugins/repositories'
});
defineRoute({
path: '/home.html',

View file

@ -193,25 +193,6 @@ var Dashboard = {
}).then(options.callback || function () {});
});
},
restartServer: function () {
var apiClient = window.ApiClient;
if (apiClient) {
require(['serverRestartDialog', 'events'], function (ServerRestartDialog, events) {
var dialog = new ServerRestartDialog({
apiClient: apiClient
});
events.on(dialog, 'restarted', function () {
if (AppInfo.isNativeApp) {
apiClient.ensureWebSocket();
} else {
window.location.reload(true);
}
});
dialog.show();
});
}
},
capabilities: function (appHost) {
var capabilities = {
PlayableMediaTypes: ['Audio', 'Video'],
@ -814,7 +795,6 @@ var AppInfo = {};
define('tabbedView', [componentsPath + '/tabbedview/tabbedview'], returnFirstDependency);
define('itemsTab', [componentsPath + '/tabbedview/itemstab'], returnFirstDependency);
define('collectionEditor', [componentsPath + '/collectionEditor/collectionEditor'], returnFirstDependency);
define('serverRestartDialog', [componentsPath + '/serverRestartDialog'], returnFirstDependency);
define('playlistEditor', [componentsPath + '/playlisteditor/playlisteditor'], returnFirstDependency);
define('recordingCreator', [componentsPath + '/recordingcreator/recordingcreator'], returnFirstDependency);
define('recordingEditor', [componentsPath + '/recordingcreator/recordingeditor'], returnFirstDependency);
@ -837,6 +817,7 @@ var AppInfo = {};
define('upNextDialog', [componentsPath + '/upnextdialog/upnextdialog'], returnFirstDependency);
define('subtitleAppearanceHelper', [componentsPath + '/subtitlesettings/subtitleappearancehelper'], returnFirstDependency);
define('subtitleSettings', [componentsPath + '/subtitlesettings/subtitlesettings'], returnFirstDependency);
define('settingsHelper', [componentsPath + '/settingshelper'], returnFirstDependency);
define('displaySettings', [componentsPath + '/displaySettings/displaySettings'], returnFirstDependency);
define('playbackSettings', [componentsPath + '/playbackSettings/playbackSettings'], returnFirstDependency);
define('homescreenSettings', [componentsPath + '/homeScreenSettings/homeScreenSettings'], returnFirstDependency);
@ -857,7 +838,6 @@ var AppInfo = {};
define('deleteHelper', [scriptsPath + '/deleteHelper'], returnFirstDependency);
define('tvguide', [componentsPath + '/guide/guide'], returnFirstDependency);
define('guide-settings-dialog', [componentsPath + '/guide/guide-settings'], returnFirstDependency);
define('loadingDialog', [componentsPath + '/loadingDialog/loadingDialog'], returnFirstDependency);
define('viewManager', [componentsPath + '/viewManager/viewManager'], function (viewManager) {
window.ViewManager = viewManager;
viewManager.dispatchPageEvents(true);

View file

@ -1,27 +1,29 @@
define(['userSettings', 'skinManager', 'connectionManager', 'events'], function (userSettings, skinManager, connectionManager, events) {
'use strict';
import * as userSettings from 'userSettings';
import skinManager from 'skinManager';
import connectionManager from 'connectionManager';
import events from 'events';
var currentViewType;
pageClassOn('viewbeforeshow', 'page', function () {
var classList = this.classList;
var viewType = classList.contains('type-interior') || classList.contains('wizardPage') ? 'a' : 'b';
var currentViewType;
pageClassOn('viewbeforeshow', 'page', function () {
var classList = this.classList;
var viewType = classList.contains('type-interior') || classList.contains('wizardPage') ? 'a' : 'b';
if (viewType !== currentViewType) {
currentViewType = viewType;
var theme;
var context;
if (viewType !== currentViewType) {
currentViewType = viewType;
var theme;
var context;
if ('a' === viewType) {
theme = userSettings.dashboardTheme();
context = 'serverdashboard';
} else {
theme = userSettings.theme();
}
skinManager.setTheme(theme, context);
if ('a' === viewType) {
theme = userSettings.dashboardTheme();
context = 'serverdashboard';
} else {
theme = userSettings.theme();
}
});
events.on(connectionManager, 'localusersignedin', function (e, user) {
currentViewType = null;
});
skinManager.setTheme(theme, context);
}
});
events.on(connectionManager, 'localusersignedin', function (e, user) {
currentViewType = null;
});

View file

@ -1018,7 +1018,6 @@
"HeaderSeriesStatus": "حالة المسلسل",
"HeaderSeriesOptions": "اعدادات المسلسل",
"HeaderSecondsValue": "{0} ثوانى",
"HeaderRestartingServer": "اعادة تشغيل الخادم",
"HeaderRecordingOptions": "اعدادات التسجيل",
"HeaderPlaybackError": "خطأ فى التشغيل",
"HeaderPlayOn": "شغل على",

View file

@ -277,7 +277,7 @@
"Identify": "Разпознаване",
"Images": "Изображения",
"ImportMissingEpisodesHelp": "Ако е активирано, информация за липсващи епизоди ще бъде добавена в базата данни на Jellyfin и ще бъде показвана заедно със сезони и серии. Това може да доведе до значително по-дълго сканиране на библиотеката.",
"InstallingPackage": "Инсталиране на {0}",
"InstallingPackage": "Инсталиране на {0} на версия {1})",
"InstantMix": "Пускане на подобни",
"Label3DFormat": "Триизмерен формат:",
"LabelAirDays": "Дни на излъчване:",
@ -302,7 +302,7 @@
"LabelCountry": "Държава:",
"LabelCriticRating": "Оценка на критиците:",
"LabelCurrentPassword": "Текуща парола:",
"LabelCustomCertificatePath": "Път към потребителския сертификат:",
"LabelCustomCertificatePath": "Ръчно задаване на пътя към SSL сертификата:",
"LabelCustomCertificatePathHelp": "Път до файл с шифровъчен стандарт №12 (PKCS #12), съдържащ сертификат и частен ключ за поддръжка на протокол TLS на собствен домейн.",
"LabelCustomCss": "CSS по избор:",
"LabelCustomCssHelp": "Добавете собствен стил към уеб-интерфейса.",
@ -333,7 +333,7 @@
"LabelEnableDlnaPlayTo": "Включване на функцията \"възпроизвеждане с ДЛНА\"",
"LabelEnableDlnaPlayToHelp": "Засичане на устройства в мрежата ви и предлагане на възможност за дистанционно управление.",
"LabelEnableDlnaServer": "Включване на ДЛНА-сървър",
"LabelEnableDlnaServerHelp": "Разрешава на UPnP устройства в мрежата да разглеждат и пускат Jellyfin съдържание.",
"LabelEnableDlnaServerHelp": "Разрешава на UPnP устройства в мрежата да разглеждат и пускат Джелифин съдържание.",
"LabelEnableRealtimeMonitor": "Активиране на наблюдение в реално време",
"LabelEnableRealtimeMonitorHelp": "Промените ще бъдат обработени веднага, на поддържани файлови системи.",
"LabelEpisodeNumber": "Номер на епизода:",
@ -348,20 +348,20 @@
"LabelHomeNetworkQuality": "Качество на домашната мрежа:",
"LabelHomeScreenSectionValue": "Раздел {0} на началния екран:",
"LabelHttpsPort": "Локален HTTPS порт:",
"LabelHttpsPortHelp": "TCP портът на който HTTPS сървърът на Jellyfin трябва да се закачи.",
"LabelHttpsPortHelp": "TCP портът на който HTTPS сървърът на Джелифин трябва да се закачи.",
"LabelImageType": "Вид изображение:",
"LabelInternetQuality": "Качество на интернетната връзка:",
"LabelKodiMetadataDateFormat": "Формат на датата на издаване:",
"LabelKodiMetadataUserHelp": "Разрешете това, за да запазите данните за гледанията във файлове Nfo за употреба от други програми.",
"LabelLanguage": "Език:",
"LabelLocalHttpServerPortNumber": "Локален HTTP порт:",
"LabelLocalHttpServerPortNumberHelp": "TCP портът на който HTTP сървърът на Jellyfin трябва да се закачи.",
"LabelLocalHttpServerPortNumberHelp": "TCP портът на който HTTP сървърът на Джелифин трябва да се закачи.",
"LabelLogs": "Журнали:",
"LabelManufacturer": "Производител",
"LabelManufacturer": "Производител:",
"LabelManufacturerUrl": "Адрес на производителя",
"LabelMaxBackdropsPerItem": "Максимален брой фонове на медия:",
"LabelMaxParentalRating": "Максимално допустима родителска оценка:",
"LabelMaxResumePercentage": "Макс процент за продължение:",
"LabelMaxResumePercentage": "Максимален процент за продължение:",
"LabelMaxScreenshotsPerItem": "Максимален брой снимки на екрана на медия:",
"LabelMaxStreamingBitrate": "Максимално качество на излъчване:",
"LabelMetadata": "Метаданни:",
@ -369,9 +369,9 @@
"LabelMetadataPath": "Път към метаданните:",
"LabelMetadataPathHelp": "Задайте място по избор за свалени картини и метаданни.",
"LabelMinBackdropDownloadWidth": "Минимална широчина на сваления фон:",
"LabelMinResumeDuration": "Мин време за продължение (секунди):",
"LabelMinResumePercentage": "Мин процент за продължение:",
"LabelMinResumePercentageHelp": "Заглавията се считат за непускани ако бъдат спрени преди това време.",
"LabelMinResumeDuration": "Минимално време за продължение:",
"LabelMinResumePercentage": "Минимален процент за продължение:",
"LabelMinResumePercentageHelp": "Заглавията се считат ,че не са възпроизведени ако бъдат спрени преди този момент.",
"LabelMinScreenshotDownloadWidth": "Минимална широчина на свалената снимка на екрана:",
"LabelModelDescription": "Описание на модела",
"LabelModelName": "Модел",
@ -443,7 +443,7 @@
"LabelTimeLimitHours": "Времево ограничение (часове):",
"LabelTitle": "Заглавие:",
"LabelTranscodingAudioCodec": "Звуков кодек:",
"LabelTranscodingTempPathHelp": "Тази папка съдържа работни файлове използвани от транскодера. Задайте място по избор или оставете празно за мястото по подразбиране.",
"LabelTranscodingTempPathHelp": "Посочете персонализиран път за файлове,които е необходимо да бъдат транскодирани и доставени на клиентите. Оставете празно ,за да се използва мястото по подразбиране.",
"LabelTranscodingVideoCodec": "Видеокодек:",
"LabelTriggerType": "Тип на спусъка:",
"LabelType": "Вид:",
@ -512,7 +512,7 @@
"News": "Новини",
"NoNextUpItemsMessage": "Нищо не е намерено. Започнете да гледате вашите предавания!",
"NoSubtitleSearchResultsFound": "Няма намерени резултати.",
"NoSubtitles": "Без субтитри",
"NoSubtitles": "Без",
"None": "Нищо",
"Normal": "Нормален",
"NumLocationsValue": "{0} папки",
@ -583,7 +583,7 @@
"OptionMissingEpisode": "Липсващи епизоди",
"OptionMonday": "Понеделник",
"OptionNameSort": "Име",
"OptionNew": "Нов...",
"OptionNew": "Нов",
"OptionNone": "Нищо",
"OptionOnAppStartup": "Като се стартира приложението",
"OptionOnInterval": "През интервал",
@ -640,7 +640,7 @@
"Refresh": "Опресняване",
"RefreshMetadata": "Опресняване на метаданните",
"ReleaseDate": "Дата на издаване",
"RememberMe": "Запомняне на данните",
"RememberMe": "Запомни ме",
"RemoveFromCollection": "Премахване от колекцията",
"RemoveFromPlaylist": "Премахване от списъка",
"Repeat": "Повтаряне",
@ -711,7 +711,7 @@
"TabMusicVideos": "Музикални клипове",
"TabMyPlugins": "Моите приставки",
"TabNetworks": "Мрежи",
"TabNfoSettings": "Формат за метаданни .nfo",
"TabNfoSettings": "Формат за метаданни NFO",
"TabNotifications": "Известия",
"TabOther": "Други",
"TabParentalControl": "Родителски контрол",
@ -724,7 +724,7 @@
"TabProfiles": "Профили",
"TabRecordings": "Записи",
"TabResponses": "Отговори",
"TabResumeSettings": "Настройки за продължаване",
"TabResumeSettings": "Възобнови",
"TabScheduledTasks": "Планирани задачи",
"TabSeries": "Сериали",
"TabServer": "Сървър",
@ -847,7 +847,7 @@
"BrowsePluginCatalogMessage": "За да видите наличните добавки, прегледайте каталога с добавките.",
"Box": "Кутия",
"AlwaysPlaySubtitlesHelp": "Поднадписите, съвпадащи с езика от настройките, ще се зареждат, независимо от езика на аудио то.",
"BookLibraryHelp": "Поддържат се звукови и текстови книги. Преглед на инструкция за наименоване {1} на книга {0}.",
"BookLibraryHelp": "Поддържат се аудио книги такива съдържащи текст. Проверете ръководството за наименуване {1} на книги {0}.",
"Blacklist": "Списък с блокирани",
"BirthLocation": "Месторождение",
"Banner": "Банер",
@ -856,7 +856,7 @@
"Ascending": "Възходящо",
"AsManyAsPossible": "Колкото е възможно повече",
"Artist": "Артист",
"AroundTime": "Към {0}",
"AroundTime": "Около {0}",
"Anytime": "По всяко време",
"AnyLanguage": "Който и да е език",
"AlwaysPlaySubtitles": "Постоянно изпълнение",
@ -879,9 +879,9 @@
"BoxSet": "Комплект",
"AuthProviderHelp": "Избор на доставчик на услуга за Автентификация, която ще се използва за автентификация на потребителската парола.",
"AllowedRemoteAddressesHelp": "Списък с IP адреси или IP/маска записи, разделени със запетая, които ще имат отдалечен достъп. Ако полето не е попълнено всички адреси ще имат отдалечен достъп.",
"BurnSubtitlesHelp": "Определя дали сървърът трябва да записва субтитри във видеоклиповете припрекодиране. Избягването на това значително ще подобри производителността. Изберете Auto, за да запишете формати, базирани на изображения (VOBSUB, PGS, SUB, IDX) и някои ASS или SSA субтитри.",
"BurnSubtitlesHelp": "Определя дали сървърът трябва да записва субтитри във видеофайлове при прекодиране. Избягването на това значително ще подобри производителността. Изберете Auto, за да запишете формати, базирани на изображения (VOBSUB, PGS, SUB, IDX) и някои ASS или SSA субтитри.",
"AllowFfmpegThrottlingHelp": "Когато прекодирането или запазването на видео стигне достатъчно далеч напред от текущата позиция за възпроизвеждане, поставете на пауза процеса, така ще се изразходват по-малко ресурси. Това е най-полезно, когато се гледа, без да се търси често из видеото. Изключете това, ако имате проблеми с възпроизвеждането.",
"AllowOnTheFlySubtitleExtractionHelp": "Вградените субтитри могат да бъдат извлечени от видеоклиповете и прехвърлени към клиентите като обикновен текст, за да се предотврати транскодирането на видеото. В някои системи това може да отнеме много време и да спре възпроизвеждането на видео по време на процеса на извличане. Деактивирайте това, за да има вградени субтитри, записани с видео кодиране, когато те не се поддържат от клиентското устройство.",
"AllowOnTheFlySubtitleExtractionHelp": "Вградените субтитри могат да бъдат извлечени от видеофайловете и прехвърлени към клиентите като обикновен текст, за да се предотврати транскодирането на видеото. В някои системи това може да отнеме много време и да спре възпроизвеждането на видео по време на процеса на извличане. Деактивирайте това, за да има вградени субтитри, записани с видео кодиране, когато те не се поддържат от клиентското устройство.",
"CinemaModeConfigurationHelp": "Режимът на кино носи театрално изживяване направо във вашата всекидневна с възможност за пускане на трейлъри и персонализирани интродукции преди основния филм.",
"ChangingMetadataImageSettingsNewContent": "Промените в настройките за изтегляне на метаданни или изображения ще се прилагат само за ново съдържание, добавено към вашата библиотека. За да приложите промените към съществуващите заглавия, ще трябва да обновите метаданните им ръчно.",
"DefaultMetadataLangaugeDescription": "Това са настройки по подразбиране и могат да се променят на база библиотека.",
@ -903,5 +903,653 @@
"DeleteImageConfirmation": "Сигурнили сте че искате да премахнете това Изображение?",
"DeleteImage": "Премахване на Исображение",
"ButtonTogglePlaylist": "Списък с изпълнения",
"ButtonToggleContextMenu": "Повече"
"ButtonToggleContextMenu": "Повече",
"ErrorSavingTvProvider": "Има проблем със запазването на ТВ доставчика.Убедете се ,че е достъпен и опитайте отново.",
"ErrorPleaseSelectLineup": "Моля изберете списък и опитайте отново.Ако няма налични списъци се убедете ,че името,паролата и пощенския код са точни.",
"ErrorMessageStartHourGreaterThanEnd": "Времето за край трябва да бъде по-голямо от началното време.",
"ErrorGettingTvLineups": "Има проблем при опита да бъдат свалени списъци с ТВ.Убедете се ,че информацията е правилна и опитайте отново.",
"ErrorDeletingItem": "Има проблем при опита да бъде изтрит файла от сървъра.Убедете се ,че сървъра има право да трие папки и опитайте отново.",
"ErrorAddingXmlTvFile": "Има проблем при достъпа на XMLTV файла.Уверете се ,че е наличен и пробвайте отново.",
"ErrorAddingTunerDevice": "Възникна проблем при добавянето на тунера. Уверете се ,че устройството е достъпно и пробвайте отново.",
"ErrorAddingMediaPathToVirtualFolder": "Има проблем при добавянето на пътя.Моля уверете се ,че е валиден и сървъра има достъп до папката.",
"Episode": "Епизод",
"EnableDetailsBannerHelp": "Покажи картинка с банер в горната част на страницата с детайли.",
"EnableDetailsBanner": "Банер с подробности",
"EnableThemeVideosHelp": "Пускай тематични видеа на заден план ,докато се разглежда библиотеката.",
"EnableThemeVideos": "Тематични видеа",
"EnableThemeSongsHelp": "Пускай тематична музика ,докато се разглежда библиотеката.",
"EnableStreamLoopingHelp": "Включи това ,ако поточното видео се предава на кратки интервали и е необходимо да се изпращат заявки постоянно.Включването на тази опция без нужда може да породи проблеми.",
"EnableStreamLooping": "Автоматично повторение на поточни видеа",
"EnablePhotosHelp": "Снимките ще бъдат разпознати и показани заедно с другите медийни файлове.",
"EnablePhotos": "Покажи снимки",
"EnableNextVideoInfoOverlayHelp": "В края на видеото покажи информация за следващия файл от текущия списък.",
"EnableNextVideoInfoOverlay": "Покажи информация за следващото видео по време на възпроизвеждане",
"EnableHardwareEncoding": "Включи хардуерно енкодиране",
"EnableExternalVideoPlayersHelp": "Меню от външен плеър ще бъде показано при стартиране на възпроизвеждането.",
"EnableDisplayMirroring": "Дублиране на дисплей",
"EnableColorCodedBackgrounds": "Цветни \"декори\"",
"EnableBackdropsHelp": "Показвай \"декори\" на заден план за някои страници ,докато се разглежда библиотеката.",
"DrmChannelsNotImported": "Канали със DRM няма да бъдат импортирани.",
"Down": "Долу",
"DoNotRecord": "Не записвай",
"DisplayModeHelp": "Изберете какво оформление желаете за интерфейса.",
"DisplayMissingEpisodesWithinSeasonsHelp": "Това трябва да бъде включено и за ТВ библиотеки в настройките на сървъра.",
"DisplayMissingEpisodesWithinSeasons": "Показвай липсващите епизоди от сезона",
"DisplayInOtherHomeScreenSections": "Покажи на главната страница (като \"последно добавени\" и \"продължи да гледаш\")",
"DisplayInMyMedia": "Покажи на главната страница",
"Disconnect": "Прекъсване",
"Disabled": "Изключено",
"DirectStreaming": "Директно възпроизвеждане",
"DirectStreamHelp2": "Директното възпроизвеждане на файла използва минимална процесорна мощност без загуба на качество.",
"DirectStreamHelp1": "Файлът е съвместим с устройството по отношение на резолюция и тип (H.264, AC3 и т.н.), но контейнера е несъвместим (mkv, avi, wmv, т.н.).Файлът ще бъде \"препакетиран\" преди да се възпроизведе от устройството.",
"DirectPlaying": "Директен метод на възпроизвеждане",
"DetectingDevices": "Откриване на устройства",
"Descending": "Намаляващ",
"Depressed": "Понижен",
"DeleteDeviceConfirmation": "Сигурни ли сте ,че искате да изтриете устройството? Ще се появи отново ,когато потребителят се впише с него.",
"DeinterlaceMethodHelp": "Избери типа деинтерлейсинг ,когато е необходимо транскодиране на подобно съдържание.",
"DefaultSubtitlesHelp": "Субтитрите са заредени според настройките зададени в метадатата на видеофайла.Когато има повече от едни субтитри се зарежда първо зададените в настройките.",
"HeaderFavoriteVideos": "Любими видеа",
"HeaderFavoritePeople": "Любими хора",
"HeaderFavoriteMovies": "Любими филми",
"HeaderFavoriteBooks": "Любими книги",
"HeaderExternalIds": "Външни идентификатори:",
"HeaderEpisodes": "Епизоди",
"HeaderEnabledFieldsHelp": "Махни отметката ,за да го заключиш и да предотвратиш неговата промяна.",
"HeaderDVR": "DVR (Цифрово записващо устройство)",
"HeaderDirectPlayProfileHelp": "Добави профили за директно възпроизвеждане ,за да се укаже кои формати може да възпроизвежда устройството.",
"HeaderDeleteTaskTrigger": "Изтрий \"пусковият механизъм\" за задачи",
"HeaderDeleteProvider": "Изтрий доставчик",
"HeaderDeleteItems": "Изтрий елемент",
"HeaderDeleteItem": "Изтрий елемент",
"HeaderDeleteDevice": "Изтрий устройство",
"HeaderDefaultRecordingSettings": "Стандартни настройки за запис",
"HeaderDateIssued": "Дата на издаване",
"HeaderContainerProfileHelp": "Профилите на различните контейнери показват ограниченията на устройството ,когато се възпроизвеждат определени формати.Ако ограничението е валидно - медията ще бъде транскодирана дори дадения формат да е конфигуриран за директно възпроизвеждане.",
"HeaderConnectionFailure": "Проблем при свързване",
"HeaderConnectToServer": "Свържи се със сървър",
"HeaderConfirmRevokeApiKey": "Отмяна на API ключа",
"HeaderConfirmProfileDeletion": "Потвърди изтриването на профила",
"HeaderConfirmPluginInstallation": "Потвърди инсталирането на плъгина",
"HeaderConfigureRemoteAccess": "Настройка на отдалечения достъп",
"HeaderCodecProfileHelp": "Профилите на различните кодеци показват ограниченията на устройството при тяхното възпроизвеждане.Ако ограничението е валидно - медията ще бъде транскодирана дори дадения кодек да е конфигуриран за директно възпроизвеждане.",
"HeaderChapterImages": "Картинки към раздела",
"HeaderChannelAccess": "Достъп до канали",
"HeaderCancelSeries": "Отмени сериал",
"HeaderCancelRecording": "Отмени запис",
"HeaderBranding": "Оформление",
"HeaderBlockItemsWithNoRating": "Забрани достъпа на елементи без информация за рейтинг:",
"HeaderAudioBooks": "Аудио книги",
"HeaderAppearsOn": "Фигурира в",
"ApiKeysCaption": "Списък с работещите в момента API ключове",
"HeaderApiKeysHelp": "Външните програми се налага да имат API ключ ,за да комуникират правилно със сървъра.Такива се издават при вписването в сървъра или чрез ръчно предоставяне.",
"HeaderAllowMediaDeletionFrom": "Позволи изтриването на медия от",
"HeaderAlert": "Предупреждение",
"HeaderAccessScheduleHelp": "Създай разписание за достъп ,за да го ограничиш до определени часове.",
"HeaderAccessSchedule": "Разписание за достъп",
"HardwareAccelerationWarning": "Включването на хардуерното ускорение може да предизвика нестабилност в някои случаи.Уверете се ,че операционната ви система и видео драйверите са актуални.Ако имате проблеми с възпроизвеждането при включена настройка е необходимо да я изключите.",
"EncoderPresetHelp": "Избери по-бърза стойност ,за да се подобри производителността или по-бавна стойност ,за да се подобри качеството.",
"H264CrfHelp": "Факторът \"постоянна скорост на предаване\" (CRF) е настройката по подразбиране на x264 енкодера.Може да определите стойност между 0 и 51, където най-ниската стойност ще даде по-добро качество (като се вземе предвид,че крайния размер на файла ще се увеличи).Препоръчват се стойности между 18 и 28.Стойността по подразбиране за x264 е 23 ,така че може да я имате за някакъв ориентир.",
"GuideProviderSelectListings": "Избери списъци",
"GroupBySeries": "Групирай по сериали",
"General": "Общи",
"FileReadError": "Има проблем при четенето на файла.",
"FileReadCancelled": "Четенето на файла е преустановено.",
"FileNotFound": "Файлът не е намерен.",
"FetchingData": "Извличане на допълнителни данни",
"Features": "Характеристики",
"FFmpegSavePathNotFound": "Не е намерен FFmpeg в зададения път.FFprobe е също задължителен и трябва да се намира в същата папка.Двете програми обикновено вървят заедно ,когато ги теглите.Уверете се ,че пътя е правилен и опитайте отново.",
"ExtractChapterImagesHelp": "Извличането на снимки от раздел ще позволи на устройствата да възпроизвеждат графични кадри при избора на меню.Този процес може бавен,ресурсно натоварващ и може да е необходимо да се заемат няколко гигабайта дисково пространство.Процесът се извършва при откриването на нови файлове или може да бъде зададен , като планирана задача при слаба активност на сървъра.Това може да бъде конфигурирано в раздела с планирани задачи.Не е препоръчително да се задава изпълнението на такава задача в натоварените часове на сървъра.",
"HttpsRequiresCert": "За да разрешите \"сигурни връзки\" е необходимо да притежавате доверен издател на сертификати ,като Let's Encrypt.Моля въведете ,ако притежавате такъв или изключете \"сигурни връзки\".",
"HeaderXmlSettings": "Настройки на XML",
"HeaderXmlDocumentAttributes": "Атрибути на XML документа",
"HeaderXmlDocumentAttribute": "Атрибут на XML документа",
"HeaderUpcomingOnTV": "Скоро по ТВ",
"HeaderTypeText": "Въведи текст",
"HeaderTypeImageFetchers": "{0} Извличане на картини",
"HeaderTuners": "Тунери",
"HeaderTranscodingProfileHelp": "Добави профили за транскодиране ,за да се види кои формати ще се използват ,когато е необходимо транскодиране.",
"HeaderThisUserIsCurrentlyDisabled": "Този потребител в момента е блокиран",
"HeaderSyncPlayEnabled": "Включване на \"синхронизирано възпроизвеждане\"",
"HeaderSyncPlaySelectGroup": "Присъединяване към група",
"HeaderSubtitleProfilesHelp": "Профилите за субтитри показват форматите поддържани от устройството.",
"HeaderSubtitleProfiles": "Профили за субтитри",
"HeaderSubtitleProfile": "Профил за субтитри",
"HeaderSubtitleDownloads": "Сваляне на субтитри",
"HeaderStopRecording": "Спри запис",
"HeaderSpecialEpisodeInfo": "Информация за специалните епизоди",
"HeaderShutdown": "Изключване",
"HeaderServerAddressSettings": "Настройки за адреса на сървъра",
"HeaderSeriesStatus": "Състояние на сериала",
"HeaderSeriesOptions": "Настройки на сериала",
"HeaderSelectTranscodingPathHelp": "Търси или въведи ръчно пътя ,където искаш да се съхраняват временните файлове при транскодиране.Папката трябва да има права за запис.",
"HeaderSelectTranscodingPath": "Избери папка за временното транскодиране на файлове",
"HeaderSelectServerCachePathHelp": "Търси или въведи ръчно пътя до временните файлове на сървъра.Папката трябва да има права за запис.",
"HeaderSelectServerCachePath": "Избери папка за временните файлове на сървъра",
"HeaderSelectServer": "Избери сървър",
"HeaderSelectMetadataPathHelp": "Търси или въведи ръчно пътя ,където искаш да се съхраняват метаданните.Папката трябва да има права за запис.",
"HeaderSelectMetadataPath": "Избери папка със метаданни",
"HeaderSelectCertificatePath": "Избери папка със сертификат",
"HeaderRestartingServer": "Рестартиране на сървъра",
"HeaderResponseProfileHelp": "Профилите за комуникация дават възможност да се настрои типът информация ,която се изпраща към устройството при възпроизвеждането на определени типове медия.",
"HeaderRemoveMediaLocation": "Премахни папката с медия",
"HeaderRemoteAccessSettings": "Настройки за отдалечен достъп",
"HeaderRecordingPostProcessing": "Последваща обработка на записи",
"HeaderRecordingOptions": "Настройки за запис",
"HeaderPluginInstallation": "Инсталиране на добавка",
"HeaderPlaybackError": "Грешка при възпроизвеждане",
"HeaderPlayback": "Възпроизвеждане на медия",
"HeaderPinCodeReset": "Зануляване на пин код",
"HeaderPhotoAlbums": "Фото албум",
"HeaderPasswordReset": "Зануляване на парола",
"HeaderOtherItems": "Други елементи",
"HeaderNextVideoPlayingInValue": "Следващото видео ще се възпроизведе след {0}",
"HeaderNavigation": "Навигация",
"HeaderLiveTvTunerSetup": "Настройка на тв тунера",
"HeaderKodiMetadataHelp": "За да включиш или изключиш използването на NFO метаданните влез в настройките за библиотеки на сървъра и намери секцията за съхранение.",
"HeaderKeepSeries": "Запази сериалите",
"HeaderKeepRecording": "Запази записите",
"HeaderItems": "Елементи",
"HeaderImageOptions": "Настройки на картини",
"HeaderIdentifyItemHelp": "Въведете един или повече критерии за търсене.Премахнете ,ако искате да увеличите резултатите при търсенето.",
"HeaderIdentificationHeader": "Идентификационен хедър",
"HeaderIdentificationCriteriaHelp": "Въведете пони един критерии.",
"HeaderHttpsSettings": "HTTPS настройки",
"HeaderHttpHeaders": "HTTP Хедъри",
"HeaderHome": "Главна",
"HeaderFetcherSettings": "Настройки на програмата за изтегляне",
"HeaderFavoritePlaylists": "Любими списъци",
"LabelDeathDate": "Дата на смърт:",
"LabelDateAddedBehaviorHelp": "Ако е взета стойност от метаданните, тя винаги ще бъде използвана преди някоя от тези опции.",
"LabelDateAddedBehavior": "за ново съдържание се приема дата на добавяне:",
"LabelCustomDeviceDisplayNameHelp": "Добави ръчно име ,с което ще се показва устройството или остави това ,което е по подразбиране.",
"LabelCorruptedFrames": "Повредени кадри:",
"LabelCancelled": "Отменено",
"LabelCache": "Временни файлове:",
"LabelBurnSubtitles": "Вграждане на субтитри:",
"LabelBlockContentWithTags": "Блокирай елементи с етикети:",
"LabelBlastMessageIntervalHelp": "Определя продължителността в секунди при \"бомбардирането\" с активни съобщения.",
"LabelBlastMessageInterval": "Интервал на активните съобщения (в секунди)",
"LabelBitrate": "Битрейт:",
"LabelBirthYear": "Година на раждане:",
"LabelBirthDate": "Дата на раждане:",
"LabelBindToLocalNetworkAddressHelp": "Не задължително.Замени локалния IP адрес за \"закачане\" към http сървъра.Ако полето е празно сървъра ще \"закачи\" всички налични адреси.Промяната на тази стойност изисква рестарт на сървъра.",
"LabelBindToLocalNetworkAddress": "\"Закачи\" към локален мрежов адрес:",
"LabelAutomaticallyRefreshInternetMetadataEvery": "Автоматично обновявай метаданните от Интернет:",
"LabelAuthProvider": "Доставчик за идентификация:",
"LabelAudioSampleRate": "Честотна дискретизация на аудиото:",
"LabelAudioCodec": "Аудио кодек:",
"LabelAudioChannels": "Аудио канали:",
"LabelAudioBitrate": "Скорост на предаване на аудиото:",
"LabelAudioBitDepth": "Битова дълбочина на аудиото:",
"LabelAudio": "Аудио",
"LabelAppNameExample": "Примерно: Sickbeard, Sonarr",
"LabelAllowedRemoteAddressesMode": "Режим на филтъра за външни ИП адреси:",
"LabelAllowedRemoteAddresses": "Филтър за външни ИП адреси:",
"LabelAllowHWTranscoding": "Разреши хардуерно транскодиране",
"LabelAll": "Всички",
"LabelAlbumArtMaxWidthHelp": "Максимална резолюция на обложките за албуми показани чрез upnp:albumArtURI.",
"LabelAlbumArtMaxHeightHelp": "Максимална резолюция на обложките за албуми показани чрез upnp:albumArtURI.",
"LabelAlbumArtMaxHeight": "Максимална височина на обложките за албуми:",
"LabelAlbumArtHelp": "PN се използва за обложки на албуми dlna:profileID attribute on upnp:albumArtURI.Някои устройства се нуждаят от определена стойност ,независимо от размера на картината.",
"LabelAirsBeforeSeason": "Ще бъде излъчен преди сезон:",
"LabelAirsBeforeEpisode": "Ще бъде излъчен преди епизод:",
"LabelAirsAfterSeason": "Ще бъде излъчен след сезон:",
"LabelAccessStart": "Начално време:",
"LabelAccessEnd": "Време на приключване:",
"LabelAccessDay": "Ден от седмицата:",
"LabelAbortedByServerShutdown": "(Прекъснато поради изключването на сървъра)",
"Kids": "Деца",
"Items": "Елементи",
"ItemCount": "{0} Елементи",
"ImportFavoriteChannelsHelp": "Ако е включено само канали ,които са отбелязани ,като любими на устройството ще бъдат добавени.",
"LabelFriendlyName": "Лесно име:",
"LabelFormat": "Формат:",
"LabelFolder": "Папка:",
"LabelFileOrUrl": "Файл или URL:",
"LabelExtractChaptersDuringLibraryScanHelp": "Генерирай изображения от различните раздели при импортиране на видеофайлове (по време на сканиране на библиотеката).В противен случай те ще бъдат извлечени при планиране на такава задача,което ще позволи сканирането на библиотеката да се извърши по-бързо.",
"LabelExtractChaptersDuringLibraryScan": "Извлечи изображения от разделите при сканиране на библиотеката",
"LabelBaseUrlHelp": "Добавя потребителска папка към адреса на съръвра.Например: <code>http://example.com/<b>&lt;baseurl&gt;</b></code>",
"LabelBaseUrl": "Основен URL:",
"LabelEndDate": "Крайна дата:",
"LabelEnableSingleImageInDidlLimitHelp": "Някои устройства няма да го покажат правилно ,ако множество изображения са вградени в Didl.",
"LabelEnableSingleImageInDidlLimit": "Ограничи до едно вградено изображение",
"LabelEnableHttpsHelp": "Позволява на сървъра да \"слуша\" на предварително зададен HTTPS порт.Необходимо е да има настроен валиден сертификат ,за да работи правилно настройката.",
"LabelEnableHttps": "Включи HTTPS",
"LabelEnableHardwareDecodingFor": "Включи хардуерно декодиране за:",
"LabelEnableDlnaDebugLoggingHelp": "Създава големи журнали файлове и е редно да се използва само с цел отстраняване на проблеми.",
"LabelEnableBlastAliveMessagesHelp": "Включи ,ако сървъра не се открива правилно от други UPnP устройства в мрежата.",
"LabelEnableBlastAliveMessages": "\"Бомбардира\" активните съобщения",
"LabelEnableAutomaticPortMapHelp": "Автоматично пренасочва външните портове на рутера към локалните портове на сървъра чрез UPnP.Тази опция може да на работи при някои модели рутери или мрежови конфигурации.Промените влизат в сила само след рестарт на сървъра.",
"LabelEmbedAlbumArtDidlHelp": "Някои устройства предпочитат този метод за извличане на обложки за албуми.При други може да има проблем при възпроизвеждането ,ако опцията е включена.",
"LabelEasyPinCode": "Лесен пин код:",
"LabelDynamicExternalId": "{0} Ид:",
"LabelDroppedFrames": "Пропуснати кадри:",
"LabelDiscNumber": "Диск номер:",
"LabelDidlMode": "DIDL режим:",
"LabelDeinterlaceMethod": "Метод на деинтерлейсинг:",
"LabelDefaultUserHelp": "Определя коя потребителска библиотека ще се показва на свързаните устройства.Това може да бъде отменено за всяко отделно устройство чрез използване на профилите.",
"LabelDefaultUser": "Потребител по подразбиране:",
"LabelDefaultScreen": "Дисплей по подразбиране:",
"LabelPostProcessor": "Приложение за последваща обработка:",
"LabelPleaseRestart": "Промените ще влязат в сила след ръчно презареждане на уеб клиента.",
"LabelPlayMethod": "Метод на възпроизвеждане:",
"LabelPlayerDimensions": "Размери на плеъра:",
"LabelPlayer": "Плеър:",
"LabelPersonRoleHelp": "Пример: Шофьор на камион за сладолед",
"LabelPasswordRecoveryPinCode": "Пин код:",
"LabelPasswordResetProvider": "Услуга за нулиране на парола:",
"LabelParentNumber": "Родителски номер:",
"LabelOptionalNetworkPathHelp": "Ако папката е споделена във вашата мрежа,предоставянето на споделения път ще позволи приложения инсталирани на други устройства да имат достъп до медията директно.Например, {0} or {1}.",
"LabelNumber": "Номер:",
"LabelNotificationEnabled": "Включване на известие",
"LabelNewsCategories": "Категории новини:",
"LabelNightly": "Тестов",
"LabelStable": "Стабилна",
"LabelChromecastVersion": "Версия на Chromecast",
"LabelMusicStreamingTranscodingBitrateHelp": "Посочете максимален битрейт при поточно предаване на музика.",
"LabelMusicStreamingTranscodingBitrate": "Битрейт при транскодиране на музика:",
"LabelMoviePrefixHelp": "Ако има добавен префикс към филмовите заглавия го въведете тук ,за да може сървъра да го обработи правилно.",
"LabelMoviePrefix": "Префикс на филма:",
"LabelMovieCategories": "Филмови категории:",
"LabelMonitorUsers": "Следете активността от:",
"LabelMinResumeDurationHelp": "Най-краткото време в секунди позволяващо ви да запазите текущата позиция на възпроизвеждане и впоследствие да я възобновите.",
"LabelMethod": "Метод:",
"LabelMetadataSaversHelp": "Изберете форматите на файловете, в които да запишете метаданните си.",
"LabelMetadataSavers": "Хранилища на метаданни:",
"LabelMetadataReadersHelp": "Подреди по важност любимите си източници на локални метаданни.Първият намерен файл ще бъде прочетен.",
"LabelMetadataReaders": "Четци на метаданни:",
"LabelMetadataDownloadersHelp": "Включи и подреди по важност любимите си програми за изтегляне на метаданни.Тези с по-нисък ранг ще се използват само за попълване на липсваща информация.",
"LabelMessageTitle": "Заглавие на съобщението:",
"LabelMessageText": "Текст на съобщението:",
"LabelMaxStreamingBitrateHelp": "Определете максималния битрейт при излъчване.",
"LabelMaxResumePercentageHelp": "Приема се ,че файловете се възпроизведени до края ,ако се спре след този момент.",
"LabelMaxChromecastBitrate": "Качество на излъчване чрез Chromecast:",
"LabelMatchType": "Тип съвпадение:",
"LabelLoginDisclaimerHelp": "Съобщението ще се показва в долната част на страницата за вход.",
"LabelLoginDisclaimer": "Предупреждение при вход:",
"LabelLockItemToPreventChanges": "Заключи дадения елемент ,за да забраниш бъдещи промени",
"LabelLineup": "Редица:",
"LabelLibraryPageSizeHelp": "Настройва броя елементи ,които ще се показват в страницата на библиотеката.Въведете 0 ,за да забраните номерирането.",
"LabelLibraryPageSize": "Размер на страницата на библиотеката:",
"LabelKodiMetadataUser": "Запази данните за активността на потребителя в файл тип NFO за:",
"LabelKodiMetadataEnablePathSubstitutionHelp": "Активирай подмяната на пътя за изображения ,като се използват настройките зададени от сървъра.",
"LabelKodiMetadataEnablePathSubstitution": "Активирай подмяната на пътя",
"LabelKodiMetadataEnableExtraThumbsHelp": "Когато се свалят изображения те могат да бъдат запазени и в двете полета extrathumbs и extrafanart за по-добра съвместимост с облиците на Коди.",
"LabelKodiMetadataEnableExtraThumbs": "Копирай extrafanart в полето extrathumbs",
"LabelKodiMetadataDateFormatHelp": "Всички дати в NFO файловете ще бъдат анализирани в този формат.",
"LabelKidsCategories": "Детски категории:",
"LabelKeepUpTo": "Пази до:",
"LabelInNetworkSignInWithEasyPasswordHelp": "Използвай лесен пин код за вписване от потребителите в домашната мрежа.Основната ви парола ще се използва ,само когато сте вписвате отдалечено.Ако полето за пин код е празно няма да имате нужда да се вписвате с парола в домашната мрежа.",
"LabelInNetworkSignInWithEasyPassword": "Включи вписване в мрежата с лесен пин код",
"LabelImportOnlyFavoriteChannels": "Ограничи до канали обозначени ,като любими",
"LabelImageFetchersHelp": "Включете и подредете любимите ви услуги за извличане на изображения по важност.",
"LabelIconMaxWidthHelp": "Максимална резолюция на иконите показани чрез upnp:icon.",
"LabelIconMaxWidth": "Максимална ширина на икона:",
"LabelIconMaxHeightHelp": "Максимална резолюция на иконите показани чрез upnp:icon.",
"LabelIconMaxHeight": "Максимална височина на икона:",
"LabelHardwareAccelerationTypeHelp": "Хардуерното ускорение се нуждае от допълнителни настройки.",
"LabelEncoderPreset": "Шаблон за енкодиране чрез H264 и H265 :",
"LabelH264Crf": "Значение на CRF H264-кодиране:",
"LabelGroupMoviesIntoCollectionsHelp": "Когато се показват списъци с филми, филмите, принадлежащи към колекция, ще бъдат показани като един общ елемент.",
"LabelServerNameHelp": "Това е името ,което ще се ползва ,за да се разпознава сървъра и по подразбиране съвпада с името на компютъра.",
"LabelRefreshMode": "Режим на опресняване:",
"LabelRecordingPathHelp": "Посочете местоположението по подразбиране, за запазване на записи.Ако полето е празно ще се използва папката на сървъра.",
"LabelRecord": "Запис:",
"LabelReasonForTranscoding": "Причина за транскодиране:",
"LabelProtocolInfoHelp": "Стойността, която ще бъде използвана при отговор към GetProtocolInfo заявките от устройството.",
"LabelProtocolInfo": "Информация за протокола:",
"LabelPostProcessorArgumentsHelp": "Използвай {path},като път за записване на файла.",
"LabelPostProcessorArguments": "Аргументи на командния ред след обработка:",
"EnableBlurhashHelp": "Изображенията, които все още се зареждат, ще се показват чрез функцията\"размито запълване\"",
"EnableBlurhash": "Активиране на функцията \"размито запълване\" за изображения",
"UnsupportedPlayback": "Джелифин не може да дешифрира съдържание, защитено с DRM, но въпреки това цялото съдържание ще бъде обработено, включително защитените заглавия. Някои файлове могат да изглеждат напълно черни поради криптиране или други неподдържани функции, например интерактивни заглавия.",
"OnApplicationStartup": "При стартиране на приложението",
"EveryXHours": "На всеки {0} часа",
"EveryHour": "Всеки час",
"EveryXMinutes": "Всеки {0} минути",
"OnWakeFromSleep": "При \"събуждане от сън\"",
"WeeklyAt": "{0}сек в {1}",
"DailyAt": "Ежедневно в {0}",
"LastSeen": "Последно видян {0}",
"PersonRole": "като {0}",
"ListPaging": "{0}-{1} от {2}",
"WriteAccessRequired": "Джелифин сървъра изисква достъп с права за запис до тази папка. Моля, осигурете достъп с права за запис и опитайте отново.",
"PathNotFound": "Пътят не можа да бъде намерен. Моля, уверете се, че пътят е валиден и опитайте отново.",
"Yesterday": "Вчера",
"YadifBob": "YADIF х2",
"Yadif": "YADIF (Yet Another DeInterlacing Filter)",
"XmlTvSportsCategoriesHelp": "Програмите от тези категории ще се показват като спортни програми.За разделяне на множество използвайте '|'.",
"XmlTvPathHelp": "Път до XMLTV файла.Джелифин ще чете от този файл и периодично ще го проверява за актуализации.Ваша грижа е да го създадете и редовно да го обновявате.",
"XmlTvKidsCategoriesHelp": "Програмите с тези категории ще се показват като програми за деца.За разделяне на множество използвайте '|'.",
"XmlTvNewsCategoriesHelp": "Програмите от тези категории ще се показват като новинарски програми.За разделяне на множество използвайте '|'.",
"XmlTvMovieCategoriesHelp": "Програмите от тези категории ще се показват като филми.За разделяне на множество използвайте '|'.",
"XmlDocumentAttributeListHelp": "Тези атрибути се прилагат към коренния елемент на всеки XML отговор.",
"Whitelist": "Бял списък",
"ViewPlaybackInfo": "Вижте информация за възпроизвеждането",
"VideoRange": "Диапазон на видео",
"ValueTimeLimitSingleHour": "Времеви лимит: 1 час",
"ValueTimeLimitMultiHour": "Времеви лимит {0} часове",
"ValueContainer": "Контейнер: {0}",
"UserAgentHelp": "Предоставяне на персонализиран ползавтел-агент HTTP хедър.",
"Up": "Нагоре",
"Unrated": "Без категории",
"UninstallPluginConfirmation": "Сигурни ли сте ,че искате да премахнете {0}?",
"Uniform": "Еднороден",
"TvLibraryHelp": "Прегледайте {0}ръководството за именуване на ТВ{1}.",
"Transcoding": "Транскодиране",
"Track": "Пътека",
"TitleHostingSettings": "Настройки за хостинг",
"TitleHardwareAcceleration": "Хардуерно ускорение",
"TabNetworking": "Работа в мрежа",
"TabDVR": "ДВР",
"SystemDlnaProfilesHelp": "Системните профили са с достъп \"само за четене\".При промяна в системния профил ще бъде създаден нов персонализиран профил.",
"SyncPlayAccessHelp": "Избери нивото на достъп ,който този потребител ще има за услугата \"Синхронизирано възпроизвеждане\".С нейна помощ може да синхронизирате възпроизвеждането с други устройства.",
"SubtitleOffset": "Изместване на субтитрите",
"SubtitleDownloadersHelp": "Включи и подреди по собствени предпочитания услугите за сваляне на субтитри.",
"SubtitleAppearanceSettingsDisclaimer": "Настройките не могат да се приложат за графични субтитри (PGS, DVD, т.н.) или ASS/SSA ,защото те зареждат свои стилове.",
"SubtitleAppearanceSettingsAlsoPassedToCastDevices": "Настройките важат за всеки Chromecast поток стартиран от това устройство.",
"StopRecording": "Спри запис",
"SortName": "Подреди по име",
"SortChannelsBy": "Подреди канали по:",
"SmartSubtitlesHelp": "Когато аудиото е чуждоезично ще бъдат заредени субтитри според зададените настройки за език.",
"ErrorAddingListingsToSchedulesDirect": "Възникна грешка при добавянето на списъка към вашия \"Schedules Direct\" акаунт.Услугата позволява определен брой списъци към един акаунт.Ще е необходимо да влезете в акаунта си (през сайта на услугата) и да отстраните другите списъци преди да продължите с добавянето на нови.",
"LabelIdentificationFieldHelp": "Подниз или регулярен израз с различаване на главни букви.",
"EnableFastImageFadeInHelp": "Използвай бързи анимации преходи",
"EnableFastImageFadeIn": "Бързи анимации",
"LabelScreensaver": "Скрийнсейвър:",
"LabelScheduledTaskLastRan": "Последно пускан {0}, заел {1}.",
"LabelRuntimeMinutes": "Продължителност (в минути):",
"LabelRequireHttpsHelp": "Ако е отметнато сървъра ще пренасочва автоматично всички заявка от HTTP към HTTPS.Няма никакъв ефект ,ако сървъра не \"слуша\" по HTTPS.",
"LabelRequireHttps": "Изисква HTTPS",
"LabelRemoteClientBitrateLimitHelp": "Допълнително ограничаване битрейта на поточното предаване за всички мрежови устройства.Това е необходимо ,за да не допуснете устройствата да изискват по-висок битрейт, отколкото вашата интернет връзка позволява.Това може да доведе до повишено натоварване на процесора на вашия сървър, за щото видеата ще се прекодират \"в движение\" до по-нисък битрейт.",
"LabelTranscodingThreadCount": "Количество транскодирани потоци :",
"LabelTranscodingProgress": "Прогрес на транскодирането:",
"LabelTranscodingFramerate": "Честота на кадрите при транскодиране:",
"LabelTranscodes": "Транскодирания:",
"LabelTranscodePath": "Път за транскодиране:",
"LabelTranscodingContainer": "Контейнер:",
"LabelTrackNumber": "Номер на песен:",
"LabelTextBackgroundColor": "Цвят на фона на текста:",
"LabelTagline": "Ключова фраза:",
"LabelTVHomeScreen": "Главна страница в режим ТВ:",
"LabelSyncPlayAccess": "Достъп до \"синхронизирано възпроизвеждане\"",
"LabelSyncPlayAccessNone": "Изключено за този потребител",
"LabelSyncPlayAccessJoinGroups": "Разрешаване на потребителя да се присъединява към групи",
"LabelSyncPlayAccessCreateAndJoinGroups": "Разрешаване на потребителя да създава и да се присъединява към групи",
"LabelSyncPlayLeaveGroupDescription": "Деактивирай \"синхронизирано възпроизвеждане\"",
"LabelSyncPlayLeaveGroup": "Напусни групата",
"LabelSyncPlayNewGroupDescription": "Създай нова група",
"LabelSyncPlayNewGroup": "Нова група",
"LabelSyncPlaySyncMethod": "Метод за синхронизиране:",
"LabelSyncPlayPlaybackDiff": "Разлика във времето на възпроизвеждане:",
"MillisecondsUnit": "мс",
"LabelSyncPlayTimeOffset": "Компенсиране на разликата във времето със сървъра:",
"LabelSubtitleFormatHelp": "Пример: srt",
"LabelSubtitleDownloaders": "Услуги за сваляне на субтитри:",
"LabelStreamType": "Вид на потока:",
"LabelStopping": "Спиране",
"LabelSportsCategories": "Спортни категории:",
"LabelSoundEffects": "Звукови ефекти:",
"LabelSortTitle": "Подреди по заглавие:",
"LabelSonyAggregationFlags": "\"Флагове\" за статистическа обработка на Сони:",
"LabelSkipIfGraphicalSubsPresentHelp": "Наличието на текстови версии на субтитрите ще доведе до по-ефективна доставка и намаляване на вероятността от транскодиране на видеото.",
"LabelSkipIfAudioTrackPresentHelp": "Махнете отметката ,за да се гарантира ,че всички видеофайлове имат субтитри,независимо от езика на аудиото им.",
"LabelSkin": "Облик:",
"LabelSize": "Размер:",
"LabelSimultaneousConnectionLimit": "Ограничение на броя едновременни потоци:",
"LabelServerName": "Име на сървъра:",
"LabelServerHostHelp": "192.168.1.100:8096 или https://myserver.com",
"LabelServerHost": "Хост:",
"LabelSendNotificationToUsers": "Изпрати уведомление до:",
"LabelSelectFolderGroupsHelp": "Папките ,които не са отметнати ще се показват самостоятелно с техен изглед.",
"LabelSelectFolderGroups": "Автоматично групира съдържанието от следните папки в режим \"изглед\" като Филми, Музика и ТВ:",
"MessageConfirmRemoveMediaLocation": "Сигурни ли сте ,че искате да премахнете мястото?",
"MessageConfirmRecordingCancellation": "Отмяна на записа?",
"MessageConfirmProfileDeletion": "Сигурни ли сте ,че искате да изтриете този профил?",
"MessageConfirmDeleteTunerDevice": "Сигурни ли сте ,че искате да изтриете това устройство?",
"MessageConfirmDeleteGuideProvider": "Сигурни ли се ,че искате да изтриете доставчика на справочника?",
"MessageConfirmAppExit": "Искате ли да излезете?",
"MessageAreYouSureDeleteSubtitles": "Сигурни ли се ,че искате да изтриете файла със субтитри?",
"MediaIsBeingConverted": "Медията е конвертирана във формат ,който е съвместим с устройството ,което ще я възпроизведе.",
"MediaInfoSoftware": "Софтуер",
"MediaInfoTimestamp": "Времеви отпечатък",
"MediaInfoSampleRate": "Кадрова честота",
"MediaInfoRefFrames": "Ref кадри",
"MediaInfoPixelFormat": "Пикселен формат",
"MediaInfoCodecTag": "Етикет на кодека",
"MediaInfoBitDepth": "Дълбочина на цвета",
"MediaInfoAnamorphic": "Анаморфно",
"MapChannels": "Разпредели канали",
"ManageRecording": "Управление на записа",
"ManageLibrary": "Управление на библиотеката",
"LiveBroadcasts": "Предавания на живо",
"LeaveBlankToNotSetAPassword": "Можете да оставите това поле празно и да не задавате парола.",
"LearnHowYouCanContribute": "Научете как можете да допринесете.",
"LaunchWebAppOnStartupHelp": "Отвори уеб клиента във браузъра по подразбиране при първото стартиране на сървъра.Това няма да се случи при използване на функцията на сървъра за рестартиране.",
"LaunchWebAppOnStartup": "Стартирай уеб интерфейса ,когато се стартира сървъра",
"LanNetworksHelp": "Списък разделен със запетая съдържащ ИП адреси или записи за ИП/мрежова маски отнасящи се за мрежи ,които ще се считат за локални ,когато се налагат ограничения в честотната лента.Ако е зададено всички други ИП адреси ще се считат за принадлежащи към външни мрежи и за тях ще важат правилата за ограничения на външни ИП -та.Ако полето е празно ще се счита ,че само подмрежата на сървъра е част от локалната мрежа.",
"LabelffmpegPathHelp": "Пътят към файла на приложението ffmpeg или папката, съдържаща ffmpeg.",
"LabelffmpegPath": "Път към FFmpeg:",
"LabelZipCode": "Пощенски код:",
"LabelXDlnaDoc": "Схема X-DLNA:",
"LabelXDlnaCapHelp": "Определя съдържанието на елемента X_DLNACAP във urn:schemas-dlna-org:device-1-0 в пространството от имена.",
"LabelXDlnaCap": "Свойства на X-Dlna:",
"LabelWeb": "Уеб:",
"LabelVideoResolution": "Видео резолюция:",
"LabelVideoCodec": "Видео кодек:",
"LabelVideoBitrate": "Видео битрейт:",
"DashboardArchitecture": "Архитектура: {0}",
"DashboardOperatingSystem": "Операционна система: {0}",
"DashboardServerName": "Сървър: {0}",
"DashboardVersionNumber": "Версия: {0}",
"LabelVersionInstalled": "{0} Инсталирано",
"LabelValue": "Стойност:",
"LabelVaapiDeviceHelp": "Това е възелът на визуализация, който се използва при хардуерно ускорение.",
"LabelVaapiDevice": "Устройство VA-API:",
"LabelUserRemoteClientBitrateLimitHelp": "Замени глобалната стойност по подразбиране, зададена в настройките за възпроизвеждане на сървъра.",
"LabelUserLoginAttemptsBeforeLockout": "Неуспешни опити за влизане, преди потребителският акаунт да бъде заключен:",
"LabelUserLibraryHelp": "Изберете коя библиотека да се показва на устройството.Оставете полето празно ,за да работят настройките по подразбиране.",
"LabelUserAgent": "Потребителски агент:",
"LabelUseNotificationServices": "Използвайте следните услуги:",
"LabelTypeMetadataDownloaders": "{0} услуги за сваляне на метаданни:",
"LabelTunerType": "Тип на тунера:",
"LabelTunerIpAddress": "ИП адрес на тунера:",
"LabelTranscodingThreadCountHelp": "Изберете максималния брой процесорни нишки ,които ще се използват при транскодиране.Намаляването на броя на нишките ще намали използването на процесора, но може да не преобразува достатъчно бързо за да осигури гладко възпроизвеждане.",
"MessageForgotPasswordFileCreated": "Следния файл беше създаден на вашия сървър и съдържа инструкции как да постъпите:",
"MessageFileReadError": "Възникна грешка при четенето на файла.Моля опитайте отново.",
"MessageEnablingOptionLongerScans": "Включването на тази опция значително ще удължи сканирането на библиотеките.",
"MessageDownloadQueued": "Свалянето е на опашката.",
"MessageDirectoryPickerLinuxInstruction": "За потребителите на следните Линукс дистрибуции: Arch Linux, CentOS, Debian, Fedora, openSUSE, или Ubuntu е необходимо да се предостави на сервизния потребител поне достъп с права за четене на вашите хранилища.",
"MessageDirectoryPickerBSDInstruction": "За BSD ще е нужно да настроите хранилище във вашия FreeNAS Jail ,за да може сървъра да има достъп до него.",
"MessageDeleteTaskTrigger": "Сигурни ли сте ,че искате да изтриете \"пусковият механизъм\" за задачи?",
"MessageCreateAccountAt": "Създай акаунт в {0}",
"MessageContactAdminToResetPassword": "Моля, свържете се със системния си администратор, за да нулирате паролата си.",
"MessageConfirmRevokeApiKey": "Сигурни ли сте ,че искате да оттеглите api ключа?Връзката на приложението със сървъра ще бъде прекратена незабавно.",
"MessageSyncPlayGroupDoesNotExist": "Неуспешно присъединяване към групата, защото не съществува.",
"MessageSyncPlayPlaybackPermissionRequired": "Необходимо е разрешение за възпроизвеждане.",
"MessageSyncPlayNoGroupsAvailable": "Няма налични групи.Пуснете нещо да се възпроизвежда.",
"MessageSyncPlayGroupWait": "<b>{0}</b> буферира се...",
"MessageSyncPlayUserLeft": "<b>{0}</b> е напуснал групата.",
"MessageSyncPlayUserJoined": "<b>{0}</b> се присъедини към групата.",
"MessageSyncPlayDisabled": "Услугата \"синхронизирано възпроизвеждане\" е изключена.",
"MessageSyncPlayEnabled": "Услугата \"синхронизирано възпроизвеждане\" е включена.",
"MessageUnsetContentHelp": "Съдържанието ще се показва като обикновени папки. За най-добри резултати използвайте мениджъра на метаданни, за да зададете типовете съдържание на подпапките.",
"MessageUnableToConnectToServer": "В момента не можем да се свържем с избрания сървър. Моля, уверете се, че работи и опитайте отново.",
"MessageReenableUser": "Вижте по-долу, за да активирате отново",
"MessagePluginInstallDisclaimer": "Приставките, създадени от членове на общността, са чудесен начин да подобрите изживяването с Джелифин чрез допълнителните функции и предимства.Преди да инсталирате, имайте предвид ефектите, които те могат да имат върху вашия Джелифин сървър, като по-дълго време за сканиране на библиотеки, допълнителна обработка на заден фон и намалена стабилност на системата.",
"MessagePluginConfigurationRequiresLocalAccess": "За да конфигурирате тази приставка, моля, впишете се директно в локалния си сървър.",
"MessagePleaseWait": "Моля,изчакайте. Това може да отнеме минута.",
"MessagePlayAccessRestricted": "Възпроизвеждането на това съдържание в момента е ограничено.Моля, свържете се с администратора на вашия сървър за повече информация.",
"MessagePasswordResetForUsers": "Следните потребители са занулили паролите си.Те вече могат да влязат с пин кодовете, използвани за извършване на нулирането.",
"MessageNoTrailersFound": "Не са намерени трейлъри.За да подобрите филмовото изживяване инсталирайте канал за трейлъри,може да подредите няколко канала в библиотека.",
"MessageNoServersAvailable": "Не са намерени сървъри, използващи функцията за автоматично откриване на сървър.",
"MessageNoMovieSuggestionsAvailable": "Понастоящем няма предложени филми. Започнете да гледате и оценявате филмите си, а след това се върнете, за да видите препоръките си.",
"MessageNoCollectionsAvailable": "Колекциите ви позволяват да се наслаждавате на персонализирани групи от филми, сериали и албуми. Кликнете върху бутона +, за да започнете да създавате колекции.",
"MessageLeaveEmptyToInherit": "Оставете празни, за да наследите настройки от родителски елемент или глобалната стойност по подразбиране.",
"MessageItemsAdded": "Добавени са елементи.",
"MessageItemSaved": "Елементът е запазен.",
"MessageUnauthorizedUser": "Понастоящем нямате право да получите достъп до сървъра. Моля, свържете се с администратора на вашия сървър за повече информация.",
"MessageInvalidUser": "Невалидно потребителско име или парола. Моля, опитайте отново.",
"MessageInvalidForgotPasswordPin": "Въведен е невалиден или изтекъл пин код. Моля, опитайте отново.",
"MessageInstallPluginFromApp": "Този плъгин трябва да бъде инсталиран от приложението, в което възнамерявате да го използвате.",
"MessageImageTypeNotSelected": "Изберете типът изображение от падащото меню.",
"MessageImageFileTypeAllowed": "Поддържат се само файлове с разширение JPEG и PNG.",
"MessageForgotPasswordInNetworkRequired": "Опитайте пак в домашната мрежа да повторите процеса по нулиране на паролата.",
"Smaller": "По-малък",
"SmallCaps": "Малки букви",
"SkipEpisodesAlreadyInMyLibraryHelp": "Епизодите ще се сравняват, като се използват номера на сезона и епизода, когато са налични.",
"SkipEpisodesAlreadyInMyLibrary": "Не записвай епизоди, които вече са в моята библиотека",
"SimultaneousConnectionLimitHelp": "Максималният брой разрешени едновременни потоци. Въвеждането на 0 означава ,че няма ограничение.",
"Filter": "Филтър",
"New": "Нов",
"ShowMore": "Покажи повече",
"ShowLess": "Покажи по-малко",
"ShowIndicatorsFor": "Показване на индикатори за:",
"SettingsWarning": "Промяната на тези стойности може да доведе до нестабилност или проблеми в свързаността. Ако срещнете някакви проблеми, препоръчваме ви да ги върнете по подразбиране.",
"SeriesSettings": "Настройки на сериала",
"SeriesRecordingScheduled": "Назначено е запис на сериал.",
"SeriesDisplayOrderHelp": "Подредете епизодите по дата на излъчване, излизане на DVD или абсолютна номерация.",
"SeriesCancelled": "Сериала е отменен.",
"Series": "Сериал",
"SelectAdminUsername": "Моля, изберете потребителско име за администраторския акаунт.",
"Season": "Сезон",
"SearchResults": "Резултати от търсенето",
"ScanForNewAndUpdatedFiles": "Сканирай за нови и актуализирани файлове",
"SaveSubtitlesIntoMediaFoldersHelp": "Съхраняването на субтитрите при видео файлове ще позволи по-лесното им управление.",
"SaveSubtitlesIntoMediaFolders": "Запазване на субтитрите в папките с медията",
"SaveChanges": "Запазете промените",
"RunAtStartup": "Пускай при стартиране",
"RestartPleaseWaitMessage": "Моля, изчакайте, докато сървъра се изключи и рестартира. Това може да отнеме минута или две.",
"RepeatOne": "Повтори един път",
"RepeatMode": "Режим на повторение",
"RepeatEpisodes": "Повтори епизодите",
"RepeatAll": "Повтори всички",
"ReleaseGroup": "Издаден от група",
"RefreshQueued": "Назначено е обновяване.",
"RefreshDialogHelp": "Метаданните се обновяват въз основа на настройките и интернет услугите, които са активирани от таблото за управление на сървъра.",
"Recordings": "Записи",
"RecordingScheduled": "Записът е насрочен.",
"RecordingPathChangeMessage": "Промяната на вашата папка за запис няма да мигрира съществуващите записи от старото местоположение към новото.Необходимо е да направите това ръчно.",
"RecordSeries": "Запиши сериал",
"RecommendationStarring": "В главните роли {0}",
"RecommendationDirectedBy": "Режисьор {0}",
"Rate": "Оценка",
"QueueAllFromHere": "Поред всичко от тук",
"ProductionLocations": "Места на заснемане",
"Previous": "Предишен",
"PreferEmbeddedEpisodeInfosOverFileNames": "Предпочитай \"вградената\" информация за епизода вместо името на файла",
"PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Това използва информацията за епизода от вградените метаданни, ако е налична.",
"PreferEmbeddedTitlesOverFileNamesHelp": "Това определя заглавието по подразбиране, когато няма интернет метаданни или локални метаданни.",
"PluginInstalledMessage": "Добавката е успешно инсталирана. Джелифин ще трябва да бъде рестартиран, за да влязат в сила промените.",
"PleaseSelectTwoItems": "Моля, изберете поне два елемента.",
"PleaseEnterNameOrId": "Моля, въведете име или външен идентификатор.",
"PleaseConfirmPluginInstallation": "Моля, щракнете върху OK, за да потвърдите, че сте прочели горното и искате да продължите с инсталирането на добавката.",
"PleaseAddAtLeastOneFolder": "Моля, добавете поне една папка към тази библиотека, като щракнете върху бутона \"Добавяне\".",
"PlaybackErrorNoCompatibleStream": "Този клиент не е съвместим с медията и сървърът не изпраща съвместим формат за медията.",
"PlayNext": "Възпроизведи следващ",
"PlayFromBeginning": "Възпроизведи от началото",
"PlayCount": "Брой възпроизвеждания",
"PlaybackData": "Данни за възпроизвеждане",
"PlaceFavoriteChannelsAtBeginning": "Постави любимите канали в началото",
"PinCodeResetConfirmation": "Сигурни ли сте, че искате да нулирате пин кода?",
"PinCodeResetComplete": "Пин кодът е нулиран.",
"Person": "Личност",
"PerfectMatch": "Перфектно съвпадение",
"PasswordSaved": "Паролата е запазена.",
"PasswordResetProviderHelp": "Изберете доставчик за нулиране на пароли, който да се използва, когато този потребител поиска нулиране на паролата",
"PasswordResetConfirmation": "Сигурни ли сте, че искате да нулирате паролата?",
"PasswordResetComplete": "Паролата е нулирана.",
"PasswordMatchError": "Паролата и потвърждението на паролата трябва да съвпадат.",
"PackageInstallFailed": "Инсталирането на {0} версия {1}) е неуспешно.",
"PackageInstallCompleted": "Инсталирането на {0} версия {1}) е завършено.",
"PackageInstallCancelled": "Инсталирането на {0} версия {1}) е отменено.",
"OtherArtist": "Друг изпълнител",
"OptionWeekends": "Почивни дни",
"OptionWeekdays": "Делници",
"OptionTvdbRating": "Рейтинг според ТВДБ",
"OptionThumbCard": "Икона карта",
"OptionThumb": "Икона",
"OptionSubstring": "Подниз",
"OptionSaveMetadataAsHiddenHelp": "Промяната на това ще се прилага за нови метаданни, запазени занапред. Съществуващите файлове с метаданни ще бъдат актуализирани следващия път, когато бъдат запазени на сървъра.",
"OptionSaveMetadataAsHidden": "Запишете метаданните и изображенията като скрити файлове",
"OptionRequirePerfectSubtitleMatchHelp": "Изискването за перфектно съвпадение ще филтрира субтитрите, за да включва само тези, които са тествани и проверени с точния видео файл.Ако премахнете отметката, това ще увеличи броя изтеглени субтитри, но ще увеличи шансовете за изтегляне на субтитри с неправилна синхронизация или несъвпадащи с видеофайла.",
"OptionReportByteRangeSeekingWhenTranscodingHelp": "Това е необходимо за някои устройства, които не търсят \"по време\" правилно.",
"OptionReportByteRangeSeekingWhenTranscoding": "Подава информация, че сървърът поддържа търсене на байтове при прекодиране",
"OptionRegex": "Регуларен",
"OptionRandom": "Случаен",
"OptionProtocolHttp": "HTTP",
"OptionProtocolHls": "Директно предаване по HTTP",
"OptionPosterCard": "Плакат карта",
"OptionPoster": "Плакат",
"OptionPlainVideoItemsHelp": "Ако е активирано, всички видеофайлове са представени в DIDL като \"object.item.videoItem\" вместо по-конкретен тип, като например \"object.item.videoItem.movie\".",
"OptionPlainStorageFoldersHelp": "Ако е активирано, всички папки са представени в DIDL като \"object.container.storageFolder\" вместо по-конкретен тип, като например \"object.container.person.musicArtist\".",
"OptionMax": "Максимално",
"OptionLoginAttemptsBeforeLockoutHelp": "Стойност нула означава наследяване по подразбиране на три опита за нормални потребители и пет за администратори. Задаването на това на -1 ще деактивира функцията.",
"OptionLoginAttemptsBeforeLockout": "Определя колко неправилни опита за влизане могат да бъдат направени, преди да бъде блокиран.",
"OptionList": "Списък",
"OptionIgnoreTranscodeByteRangeRequestsHelp": "Ако са активирани, тези заявки ще бъдат удовлетворени, но ще се пренебрегне заглавната част от обхвата на байтовете.",
"OptionIgnoreTranscodeByteRangeRequests": "Игнорирайте заявките за обхват на байтове при прекодиране",
"OptionHlsSegmentedSubtitles": "HLS сегментирани субтитри",
"OptionExtractChapterImage": "Включи извличането на изображения от разделите",
"OptionEstimateContentLength": "Приблизителна дължина на съдържанието при транскодиране",
"OptionEquals": "Се равнява",
"OptionEnableM2tsModeHelp": "Включи режим m2ts ,когато се декодира до mpegts.",
"OptionEnableM2tsMode": "Включи режим M2ts",
"OptionEnableForAllTuners": "Активиране за всички тунер устройства",
"OptionEnableExternalContentInSuggestionsHelp": "Разрешете интернет трейлърите и телевизионните програми на живо да бъдат включени в предложеното съдържание.",
"OptionEnableExternalContentInSuggestions": "Активиране на външно съдържание в предложенията",
"OptionEmbedSubtitles": "Внедрено вътре в контейнера",
"OptionDownloadImagesInAdvanceHelp": "По подразбиране повечето изображения се изтеглят само когато са поискани от приложенията свързани с Джелифин.Активирайте тази опция, за да изтеглите всички изображения предварително, при добавянето на нова медия. Това може да доведе до значително по-дълго сканиране на библиотеката.",
"OptionDisplayFolderViewHelp": "Показвай папките заедно с тези от медийната библиотека. Това може да бъде полезно, ако искате да имате по разбираем изглед за папките.",
"OptionDateAddedImportTime": "Използвай датата на сканиране в библиотеката",
"OptionDateAddedFileTime": "Използвай датата на създаване на файла",
"OptionCaptionInfoExSamsung": "CaptionInfoEx (Samsung)",
"OptionBluray": "Блу-рей",
"OptionBlockTrailers": "Трейлъри",
"OptionBlockMusic": "Музика",
"OptionBlockLiveTvChannels": "Телевизионни канали на живо",
"OptionBlockChannelContent": "Съдържание на интернет канала",
"OptionBanner": "Банер",
"OptionAutomaticallyGroupSeriesHelp": "Ако е активирано, сезоните, които се намират в различни папки, ще бъдат автоматично обединени в един сериал.",
"OptionAutomaticallyGroupSeries": "Автоматично обединява сезони, които са разпределени в множество папки",
"OptionAllowVideoPlaybackTranscoding": "Разреши възпроизвеждане на видео, което изисква транскодиране",
"OptionAllowVideoPlaybackRemuxing": "Разреши възпроизвеждане на видео, което изисква преобразуване без повторно кодиране",
"OptionAllowSyncTranscoding": "Разреши изтегляне и синхронизиране на медия, която изисква транскодиране",
"OptionAllowMediaPlaybackTranscodingHelp": "Ограничаването на достъпа до транскодирането може да доведе до проблеми при възпроизвеждането в приложенията на Джелифин поради неподдържани медийни формати.",
"OptionAllowLinkSharingHelp": "Споделят се само уеб страници, съдържащи медийна информация.Медийните файлове никога не се споделят публично. Споделянията са ограничени във времето и изтичат след {0} дни.",
"OptionAllowContentDownloading": "Разрешаване на изтегляне и синхронизиране на медия",
"OptionForceRemoteSourceTranscoding": "Принудително транскодиране на отдалечени медийни източници (като поточна ТВ)",
"OptionAllowAudioPlaybackTranscoding": "Разреши възпроизвеждане на аудио, което изисква транскодиране",
"OnlyImageFormats": "Само графични формати (VOBSUB, PGS, SUB)",
"OnlyForcedSubtitlesHelp": "Ще бъдат показани само субтитри, маркирани за \"принудително\" зареждане.",
"OnlyForcedSubtitles": "Само принудително",
"OneChannel": "Един канал",
"NoSubtitlesHelp": "Субтитрите няма да бъдат заредени по подразбиране. Те все още могат да бъдат включени ръчно по време на възпроизвеждане.",
"NoPluginConfigurationMessage": "Тази добавка няма настройки за конфигуриране.",
"NoNewDevicesFound": "Не са намерени нови устройства. За да добавите нов тунер, затворете този диалогов прозорец и въведете ръчно информацията за устройството.",
"NoCreatedLibraries": "Изглежда, че все още не сте създали библиотеки. {0} Искате ли да ги създадете сега? {1}",
"NextUp": "Следващ по ред",
"Next": "Следващ",
"NewCollectionHelp": "Колекциите ви позволяват да създавате персонализирани групи от филми и друго библиотечно съдържание.",
"MusicLibraryHelp": "Прегледайте {0} ръководството за именуване на музика {1}.",
"MusicArtist": "Музикален изпълнител",
"MusicAlbum": "Музикален албум",
"Movie": "Филм",
"MovieLibraryHelp": "Прегледайте {0} ръководството за именуване на филми {1}.",
"MoveRight": "Премести надясно",
"MoveLeft": "Премести на ляво",
"MoreMediaInfo": "Информация за медията",
"MetadataSettingChangeHelp": "Промяната на настройките на метаданните ще повлияе на новото съдържание, което ще се добавя занапред.За да обновите съществуващото съдържание, отворете панела с подробности и щракнете върху бутона за опресняване или извършете групово обновяване с помощта на мениджъра на метаданни.",
"MessageSyncPlayErrorMedia": "Неуспешно активиране на услугата \"синхронизирано възпроизвеждане\"!Грешка в медията.",
"MessageSyncPlayErrorMissingSession": "Неуспешно активиране на услугата \"синхронизирано възпроизвеждане\"! Липсва сесия.",
"MessageSyncPlayErrorNoActivePlayer": "Не е намерен активен плеър.Функцията \"синхронизирано възпроизвеждане\" е изключена.",
"MessageSyncPlayErrorAccessingGroups": "Възникна грешка при достъп до списъка с групи.",
"MessageSyncPlayLibraryAccessDenied": "Достъпът до това съдържание е ограничен.",
"MessageSyncPlayJoinGroupDenied": "Необходимо е разрешени за използване на функцията \"синхронизирано възпроизвеждане\".",
"MessageSyncPlayCreateGroupDenied": "Необходимо е разрешени за създаване на група.",
"LabelSonyAggregationFlagsHelp": "Определя съдържанието на aggregationFlags елемента във urn:schemas-sonycom:av пространство от имена.",
"LabelXDlnaDocHelp": "Определя съдържанието на X_DLNADOC елемента в urn:schemas-dlna-org:device-1-0 пространство от имена.",
"LabelSkipForwardLength": "Време за придвижване напред:",
"LabelSkipBackLength": "Време за придвижване назад:"
}

View file

@ -812,7 +812,7 @@
"MessageNoAvailablePlugins": "Nejsou dostupné žádné zásuvné moduly.",
"MessageNoMovieSuggestionsAvailable": "Žádné návrhy nejsou v současnosti k dispozici. Začněte sledovat a hodnotit filmy, a pak se vám doporučení zobrazí.",
"MessageNoPluginsInstalled": "Nemáte instalovány žádné zásuvné moduly.",
"MessageNoTrailersFound": "Nebyly nalezeny žádné upoutávky k filmu. Chcete-li si zlepšit zážitek ze sledování, nainstalujte si kanál s upoutávkami.",
"MessageNoTrailersFound": "Chcete-li si zlepšit zážitek ze sledování, nainstalujte si kanál s upoutávkami.",
"MessageNothingHere": "Tady nic není.",
"MessagePasswordResetForUsers": "Obnovení hesla bylo provedeno následujícími uživateli. Nyní se mohou přihlásit pomocí kódů PIN, které byly použity k provedení resetu.",
"MessagePlayAccessRestricted": "Přehrávání tohoto obsahu je aktuálně omezeno. Další informace získáte od správce serveru.",
@ -1059,7 +1059,6 @@
"RepeatOne": "Opakovat jeden",
"ReplaceAllMetadata": "Přepsat všechna metadata",
"ReplaceExistingImages": "Nahradit existující obrázky",
"RestartPleaseWaitMessage": "Počkejte prosím, než se server Jellyfin vypne a restartuje. Může to trvat několik minut.",
"ResumeAt": "Obnovit přehrávání od {0}",
"Rewind": "Přetočit zpět",
"RunAtStartup": "Spustit po startu",
@ -1395,7 +1394,6 @@
"HeaderFavoriteAlbums": "Oblíbená alba",
"HeaderFavoriteArtists": "Oblíbení interpreti",
"HeaderFavoriteSongs": "Oblíbená hudba",
"HeaderRestartingServer": "Restartování serveru",
"LabelAuthProvider": "Poskytovatel ověření:",
"LabelServerNameHelp": "Tento název bude použit k identifikaci serveru a bude výchozí pro název počítače serveru.",
"LabelPasswordResetProvider": "Poskytovatel obnovy hesla:",
@ -1633,5 +1631,22 @@
"EnableDetailsBannerHelp": "Zobrazí obrázek ve vrchní části detailu položky.",
"EnableDetailsBanner": "Obrázek detailu",
"ShowMore": "Zobrazit více",
"ShowLess": "Zobrazit méně"
"ShowLess": "Zobrazit méně",
"EnableBlurhashHelp": "Nenačtené obrázky budou zobrazeny pomocí neurčitých zástupných obrázků",
"EnableBlurhash": "Povolit zástupné obrázky",
"ButtonCast": "Přehrát v zařízení",
"ButtonSyncPlay": "Synchronizace přehrávání",
"MessageNoGenresAvailable": "Povolit některým poskytovatelům metadat stahovat informace o žánrech z Internetu.",
"EnableFasterAnimationsHelp": "Použít rychlejší animace a přechody",
"EnableFasterAnimations": "Rychlejší animace",
"EnableDecodingColorDepth10Vp9": "Povolit 10bitové hardwarové dekódování formátu VP9",
"EnableDecodingColorDepth10Hevc": "Povolit 10bitové hardwarové dekódování formátu HEVC",
"TabRepositories": "Repozitáře",
"MessageAddRepository": "Pokud chcete přidat repozitář, klikněte na tlačítko vedle záhlaví a vyplňte požadované informace.",
"LabelRepositoryNameHelp": "Vlastní pojmenování, které slouží k odlišení tohoto repozitáře od ostatních repozitářů přidaných na vašem serveru.",
"LabelRepositoryName": "Název repozitáře",
"LabelRepositoryUrlHelp": "Umístění manifestu repozitáře, který chcete zahrnout.",
"LabelRepositoryUrl": "URL adresa repozitáře",
"HeaderNewRepository": "Nový repozitář",
"MessageNoRepositories": "Neexistují žádné repozitáře."
}

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