Merge remote-tracking branch 'upstream/master' into syncplay

This commit is contained in:
gion 2020-05-27 10:42:29 +02:00
commit 385f0a0beb
213 changed files with 3867 additions and 2697 deletions

View file

@ -42,7 +42,8 @@ module.exports = {
'one-var': ["error", "never"],
'quotes': ["error", "single", { "avoidEscape": true, "allowTemplateLiterals": false }],
'semi': ["error"],
'space-before-blocks': ["error"]
'space-before-blocks': ["error"],
"space-infix-ops": "error"
},
overrides: [
{

3
.gitignore vendored
View file

@ -9,3 +9,6 @@ node_modules
# ide
.idea
.vscode
#log
yarn-error.log

View file

@ -44,7 +44,7 @@ Jellyfin Web is the frontend used for most of the clients available for end user
### Dependencies
- Yarn
- [Yarn 1.22.4](https://classic.yarnpkg.com/en/docs/install)
- Gulp-cli
### Getting Started

View file

@ -9,7 +9,7 @@
"@babel/plugin-transform-modules-amd": "^7.9.6",
"@babel/polyfill": "^7.8.7",
"@babel/preset-env": "^7.8.6",
"autoprefixer": "^9.7.6",
"autoprefixer": "^9.8.0",
"babel-loader": "^8.0.6",
"browser-sync": "^2.26.7",
"clean-webpack-plugin": "^3.0.0",
@ -25,7 +25,7 @@
"file-loader": "^6.0.0",
"gulp": "^4.0.2",
"gulp-babel": "^8.0.0",
"gulp-cli": "^2.2.0",
"gulp-cli": "^2.2.1",
"gulp-concat": "^2.6.1",
"gulp-htmlmin": "^5.0.1",
"gulp-if": "^3.0.0",
@ -42,14 +42,14 @@
"postcss-loader": "^3.0.0",
"postcss-preset-env": "^6.7.0",
"style-loader": "^1.1.3",
"stylelint": "^13.3.3",
"stylelint": "^13.5.0",
"stylelint-config-rational-order": "^0.1.2",
"stylelint-no-browser-hacks": "^1.2.1",
"stylelint-order": "^4.0.0",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10",
"webpack-concat-plugin": "^3.0.0",
"webpack-dev-server": "^3.10.3",
"webpack-dev-server": "^3.11.0",
"webpack-merge": "^4.2.2",
"webpack-stream": "^5.2.1"
},
@ -57,17 +57,17 @@
"alameda": "^1.4.0",
"classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz",
"core-js": "^3.6.5",
"date-fns": "^2.12.0",
"date-fns": "^2.14.0",
"document-register-element": "^1.14.3",
"fast-text-encoding": "^1.0.1",
"flv.js": "^1.5.0",
"headroom.js": "^0.11.0",
"hls.js": "^0.13.1",
"howler": "^2.1.3",
"howler": "^2.2.0",
"intersection-observer": "^0.10.0",
"jellyfin-apiclient": "^1.1.1",
"jellyfin-apiclient": "^1.1.2",
"jellyfin-noto": "https://github.com/jellyfin/jellyfin-noto",
"jquery": "^3.5.0",
"jquery": "^3.5.1",
"jstree": "^3.3.7",
"libass-wasm": "https://github.com/jellyfin/JavascriptSubtitlesOctopus#4.0.0-jf-smarttv",
"material-design-icons-iconfont": "^5.0.1",
@ -76,9 +76,9 @@
"query-string": "^6.11.1",
"resize-observer-polyfill": "^1.5.1",
"screenfull": "^5.0.2",
"shaka-player": "^2.5.10",
"shaka-player": "^2.5.11",
"sortablejs": "^1.10.2",
"swiper": "^5.3.7",
"swiper": "^5.4.1",
"webcomponents.js": "^0.7.24",
"whatwg-fetch": "^3.0.0"
},
@ -91,9 +91,9 @@
"test": [
"src/components/autoFocuser.js",
"src/components/cardbuilder/cardBuilder.js",
"src/components/filedownloader.js",
"src/scripts/fileDownloader.js",
"src/components/images/imageLoader.js",
"src/components/lazyloader/lazyloader-intersectionobserver.js",
"src/components/lazyLoader/lazyLoaderIntersectionObserver.js",
"src/components/playback/mediasession.js",
"src/components/sanatizefilename.js",
"src/components/scrollManager.js",
@ -106,7 +106,11 @@
"src/scripts/filesystem.js",
"src/scripts/imagehelper.js",
"src/scripts/inputManager.js",
"src/scripts/keyboardnavigation.js",
"src/scripts/deleteHelper.js",
"src/components/actionSheet/actionSheet.js",
"src/components/playmenu.js",
"src/components/indicators/indicators.js",
"src/scripts/keyboardNavigation.js",
"src/scripts/settings/appSettings.js",
"src/scripts/settings/userSettings.js",
"src/scripts/settings/webSettings.js"

View file

@ -5,8 +5,10 @@ const cssnano = require('cssnano');
const config = () => ({
plugins: [
// Explicitly specify browserslist to override ones from node_modules
// For example, Swiper has it in its package.json
postcssPresetEnv({browsers: packageConfig.browserslist}),
autoprefixer(),
autoprefixer({overrideBrowserslist: packageConfig.browserslist}),
cssnano()
]
});

View file

@ -15,6 +15,8 @@ print(langlst)
input('press enter to continue')
keysus = []
missing = []
with open(langdir + '/' + 'en-us.json') as en:
langus = json.load(en)
for key in langus:
@ -32,10 +34,19 @@ for lang in langlst:
for key in langjson:
if key in keysus:
langjnew[key] = langjson[key]
elif key not in missing:
missing.append(key)
f.seek(0)
f.write(json.dumps(langjnew, indent=inde, sort_keys=False, ensure_ascii=False))
f.write('\n')
f.truncate()
f.close()
print(missing)
print('LENGTH: ' + str(len(missing)))
with open('missing.txt', 'w') as out:
for item in missing:
out.write(item + '\n')
out.close()
print('DONE')

View file

@ -34,7 +34,7 @@ for lang in langlst:
print(dep)
print('LENGTH: ' + str(len(dep)))
with open('scout.txt', 'w') as out:
with open('unused.txt', 'w') as out:
for item in dep:
out.write(item + '\n')
out.close()

View file

@ -5,7 +5,7 @@
<div class="verticalSection">
<div class="sectionTitleContainer flex align-items-center">
<h1 class="sectionTitle pluginName"></h1>
<a is="emby-linkbutton" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/plugins/index.html">${Help}</a>
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/plugins/index.html">${Help}</a>
</div>
<p id="overview" style="font-style: italic;"></p>

View file

@ -3,7 +3,7 @@
<form class="addServerForm" style="margin: 0 auto;">
<h1>${HeaderConnectToServer}</h1>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtServerHost" required="required" label="${LabelServerHost}" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off" />
<input is="emby-input" type="url" id="txtServerHost" required="required" label="${LabelServerHost}"/>
<div class="fieldDescription">${LabelServerHostHelp}</div>
</div>
<br />

View file

@ -4,18 +4,19 @@
<div class="detailSectionHeader">
<h2 style="margin:.6em 0;vertical-align:middle;display:inline-block;">${HeaderApiKeys}</h2>
<button is="emby-button" type="button" class="fab btnNewKey submit" style="margin-left:1em;" title="${ButtonAdd}">
<span class="material-icons add"></span>
<span class="material-icons add" aria-hidden="true"></span>
</button>
</div>
<p>${HeaderApiKeysHelp}</p>
<br />
<table class="tblApiKeys detailTable">
<caption class="clipForScreenReader">${ApiKeysCaption}</caption>
<thead>
<tr>
<th class="detailTableHeaderCell"></th>
<th class="detailTableHeaderCell">${HeaderApiKey}</th>
<th class="detailTableHeaderCell">${HeaderApp}</th>
<th class="detailTableHeaderCell">${HeaderDateIssued}</th>
<th scope="col" class="detailTableHeaderCell"></th>
<th scope="col" class="detailTableHeaderCell">${HeaderApiKey}</th>
<th scope="col" class="detailTableHeaderCell">${HeaderApp}</th>
<th scope="col" class="detailTableHeaderCell">${HeaderDateIssued}</th>
</tr>
</thead>
<tbody class="resultBody"></tbody>

View file

@ -5,6 +5,17 @@ html {
height: 100%;
}
.clipForScreenReader {
clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%);
height: 1px;
width: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
}
.material-icons {
/* Fix font ligatures on older WebOS versions */
-webkit-font-feature-settings: "liga";
@ -109,3 +120,11 @@ div[data-role=page] {
.headroom--unpinned {
transform: translateY(-100%);
}
.force-scroll {
overflow-y: scroll;
}
.hide-scroll {
overflow-y: hidden;
}

View file

@ -50,7 +50,7 @@ define(['dialogHelper', 'datetime', 'globalize', 'emby-select', 'paper-icon-butt
show: function (options) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'components/accessschedule/accessschedule.template.html', true);
xhr.open('GET', 'components/accessSchedule/accessSchedule.template.html', true);
xhr.onload = function (e) {
var template = this.response;

View file

@ -1,6 +1,6 @@
<div class="formDialogHeader">
<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1">
<span class="material-icons arrow_back"></span>
<button is="paper-icon-button-light" class="btnCancel autoSize" title="${LabelPrevious}" tabindex="-1">
<span class="material-icons arrow_back" aria-hidden="true"></span>
</button>
<h3 class="formDialogHeaderTitle">
${HeaderAccessSchedule}

View file

@ -0,0 +1,345 @@
import dialogHelper from 'dialogHelper';
import layoutManager from 'layoutManager';
import globalize from 'globalize';
import dom from 'dom';
import 'emby-button';
import 'css!./actionSheet';
import 'material-icons';
import 'scrollStyles';
import 'listViewStyle';
function getOffsets(elems) {
let results = [];
if (!document) {
return results;
}
let box;
for (let elem of elems) {
// Support: BlackBerry 5, iOS 3 (original iPhone)
// If we don't have gBCR, just use 0,0 rather than error
if (elem.getBoundingClientRect) {
box = elem.getBoundingClientRect();
} else {
box = { top: 0, left: 0 };
}
results.push({
top: box.top,
left: box.left,
width: box.width,
height: box.height
});
}
return results;
}
function getPosition(options, dlg) {
const windowSize = dom.getWindowSize();
const windowHeight = windowSize.innerHeight;
const windowWidth = windowSize.innerWidth;
let pos = getOffsets([options.positionTo])[0];
if (options.positionY !== 'top') {
pos.top += (pos.height || 0) / 2;
}
pos.left += (pos.width || 0) / 2;
const height = dlg.offsetHeight || 300;
const width = dlg.offsetWidth || 160;
// Account for popup size
pos.top -= height / 2;
pos.left -= width / 2;
// Avoid showing too close to the bottom
const overflowX = pos.left + width - windowWidth;
const overflowY = pos.top + height - windowHeight;
if (overflowX > 0) {
pos.left -= (overflowX + 20);
}
if (overflowY > 0) {
pos.top -= (overflowY + 20);
}
pos.top += (options.offsetTop || 0);
pos.left += (options.offsetLeft || 0);
// Do some boundary checking
pos.top = Math.max(pos.top, 10);
pos.left = Math.max(pos.left, 10);
return pos;
}
function centerFocus(elem, horiz, on) {
require(['scrollHelper'], function (scrollHelper) {
const fn = on ? 'on' : 'off';
scrollHelper.centerFocus[fn](elem, horiz);
});
}
export function show(options) {
// items
// positionTo
// showCancel
// title
let dialogOptions = {
removeOnClose: true,
enableHistory: options.enableHistory,
scrollY: false
};
let isFullscreen;
if (layoutManager.tv) {
dialogOptions.size = 'fullscreen';
isFullscreen = true;
dialogOptions.autoFocus = true;
} else {
dialogOptions.modal = false;
dialogOptions.entryAnimation = options.entryAnimation;
dialogOptions.exitAnimation = options.exitAnimation;
dialogOptions.entryAnimationDuration = options.entryAnimationDuration || 140;
dialogOptions.exitAnimationDuration = options.exitAnimationDuration || 100;
dialogOptions.autoFocus = false;
}
let dlg = dialogHelper.createDialog(dialogOptions);
if (isFullscreen) {
dlg.classList.add('actionsheet-fullscreen');
} else {
dlg.classList.add('actionsheet-not-fullscreen');
}
dlg.classList.add('actionSheet');
if (options.dialogClass) {
dlg.classList.add(options.dialogClass);
}
let html = '';
const scrollClassName = layoutManager.tv ? 'scrollY smoothScrollY hiddenScrollY' : 'scrollY';
let style = '';
// Admittedly a hack but right now the scrollbar is being factored into the width which is causing truncation
if (options.items.length > 20) {
const minWidth = dom.getWindowSize().innerWidth >= 300 ? 240 : 200;
style += 'min-width:' + minWidth + 'px;';
}
let renderIcon = false;
let icons = [];
let itemIcon;
for (let item of options.items) {
itemIcon = item.icon || (item.selected ? 'check' : null);
if (itemIcon) {
renderIcon = true;
}
icons.push(itemIcon || '');
}
if (layoutManager.tv) {
html += '<button is="paper-icon-button-light" class="btnCloseActionSheet hide-mouse-idle-tv" tabindex="-1"><span class="material-icons arrow_back"></span></button>';
}
// If any items have an icon, give them all an icon just to make sure they're all lined up evenly
const center = options.title && (!renderIcon /*|| itemsWithIcons.length != options.items.length*/);
if (center || layoutManager.tv) {
html += '<div class="actionSheetContent actionSheetContent-centered">';
} else {
html += '<div class="actionSheetContent">';
}
if (options.title) {
html += '<h1 class="actionSheetTitle">' + options.title + '</h1>';
}
if (options.text) {
html += '<p class="actionSheetText">' + options.text + '</p>';
}
let scrollerClassName = 'actionSheetScroller';
if (layoutManager.tv) {
scrollerClassName += ' actionSheetScroller-tv focuscontainer-x focuscontainer-y';
}
html += '<div class="' + scrollerClassName + ' ' + scrollClassName + '" style="' + style + '">';
let menuItemClass = 'listItem listItem-button actionSheetMenuItem';
if (options.border || options.shaded) {
menuItemClass += ' listItem-border';
}
if (options.menuItemClass) {
menuItemClass += ' ' + options.menuItemClass;
}
if (layoutManager.tv) {
menuItemClass += ' listItem-focusscale';
}
if (layoutManager.mobile) {
menuItemClass += ' actionsheet-xlargeFont';
}
// 'options.items' is HTMLOptionsCollection, so no fancy loops
for (let i = 0; i < options.items.length; i++) {
const item = options.items[i];
if (item.divider) {
html += '<div class="actionsheetDivider"></div>';
continue;
}
const autoFocus = item.selected && layoutManager.tv ? ' autoFocus' : '';
// Check for null in case int 0 was passed in
const optionId = item.id == null || item.id === '' ? item.value : item.id;
html += '<button' + autoFocus + ' is="emby-button" type="button" class="' + menuItemClass + '" data-id="' + optionId + '">';
itemIcon = icons[i];
if (itemIcon) {
html += '<span class="actionsheetMenuItemIcon listItemIcon listItemIcon-transparent material-icons ' + itemIcon + '"></span>';
} else if (renderIcon && !center) {
html += '<span class="actionsheetMenuItemIcon listItemIcon listItemIcon-transparent material-icons check" style="visibility:hidden;"></span>';
}
html += '<div class="listItemBody actionsheetListItemBody">';
html += '<div class="listItemBodyText actionSheetItemText">';
html += (item.name || item.textContent || item.innerText);
html += '</div>';
if (item.secondaryText) {
html += '<div class="listItemBodyText secondary">' + item.secondaryText + '</div>';
}
html += '</div>';
if (item.asideText) {
html += '<div class="listItemAside actionSheetItemAsideText">' + item.asideText + '</div>';
}
html += '</button>';
}
if (options.showCancel) {
html += '<div class="buttons">';
html += '<button is="emby-button" type="button" class="btnCloseActionSheet">' + globalize.translate('ButtonCancel') + '</button>';
html += '</div>';
}
html += '</div>';
dlg.innerHTML = html;
if (layoutManager.tv) {
centerFocus(dlg.querySelector('.actionSheetScroller'), false, true);
}
let btnCloseActionSheet = dlg.querySelector('.btnCloseActionSheet');
if (btnCloseActionSheet) {
btnCloseActionSheet.addEventListener('click', function () {
dialogHelper.close(dlg);
});
}
// Seeing an issue in some non-chrome browsers where this is requiring a double click
//var eventName = browser.firefox ? 'mousedown' : 'click';
let selectedId;
let timeout;
if (options.timeout) {
timeout = setTimeout(function () {
dialogHelper.close(dlg);
}, options.timeout);
}
return new Promise(function (resolve, reject) {
let isResolved;
dlg.addEventListener('click', function (e) {
const actionSheetMenuItem = dom.parentWithClass(e.target, 'actionSheetMenuItem');
if (actionSheetMenuItem) {
selectedId = actionSheetMenuItem.getAttribute('data-id');
if (options.resolveOnClick) {
if (options.resolveOnClick.indexOf) {
if (options.resolveOnClick.indexOf(selectedId) !== -1) {
resolve(selectedId);
isResolved = true;
}
} else {
resolve(selectedId);
isResolved = true;
}
}
dialogHelper.close(dlg);
}
});
dlg.addEventListener('close', function () {
if (layoutManager.tv) {
centerFocus(dlg.querySelector('.actionSheetScroller'), false, false);
}
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
if (!isResolved) {
if (selectedId != null) {
if (options.callback) {
options.callback(selectedId);
}
resolve(selectedId);
} else {
reject();
}
}
});
dialogHelper.open(dlg);
const pos = options.positionTo && dialogOptions.size !== 'fullscreen' ? getPosition(options, dlg) : null;
if (pos) {
dlg.style.position = 'fixed';
dlg.style.margin = 0;
dlg.style.left = pos.left + 'px';
dlg.style.top = pos.top + 'px';
}
});
}
export default {
show: show
};

View file

@ -1,360 +0,0 @@
define(['dialogHelper', 'layoutManager', 'globalize', 'browser', 'dom', 'emby-button', 'css!./actionsheet', 'material-icons', 'scrollStyles', 'listViewStyle'], function (dialogHelper, layoutManager, globalize, browser, dom) {
'use strict';
function getOffsets(elems) {
var doc = document;
var results = [];
if (!doc) {
return results;
}
var box;
var elem;
for (var i = 0, length = elems.length; i < length; i++) {
elem = elems[i];
// Support: BlackBerry 5, iOS 3 (original iPhone)
// If we don't have gBCR, just use 0,0 rather than error
if (elem.getBoundingClientRect) {
box = elem.getBoundingClientRect();
} else {
box = { top: 0, left: 0 };
}
results[i] = {
top: box.top,
left: box.left,
width: box.width,
height: box.height
};
}
return results;
}
function getPosition(options, dlg) {
var windowSize = dom.getWindowSize();
var windowHeight = windowSize.innerHeight;
var windowWidth = windowSize.innerWidth;
var pos = getOffsets([options.positionTo])[0];
if (options.positionY !== 'top') {
pos.top += (pos.height || 0) / 2;
}
pos.left += (pos.width || 0) / 2;
var height = dlg.offsetHeight || 300;
var width = dlg.offsetWidth || 160;
// Account for popup size
pos.top -= height / 2;
pos.left -= width / 2;
// Avoid showing too close to the bottom
var overflowX = pos.left + width - windowWidth;
var overflowY = pos.top + height - windowHeight;
if (overflowX > 0) {
pos.left -= (overflowX + 20);
}
if (overflowY > 0) {
pos.top -= (overflowY + 20);
}
pos.top += (options.offsetTop || 0);
pos.left += (options.offsetLeft || 0);
// Do some boundary checking
pos.top = Math.max(pos.top, 10);
pos.left = Math.max(pos.left, 10);
return pos;
}
function centerFocus(elem, horiz, on) {
require(['scrollHelper'], function (scrollHelper) {
var fn = on ? 'on' : 'off';
scrollHelper.centerFocus[fn](elem, horiz);
});
}
function show(options) {
// items
// positionTo
// showCancel
// title
var dialogOptions = {
removeOnClose: true,
enableHistory: options.enableHistory,
scrollY: false
};
var backButton = false;
var isFullscreen;
if (layoutManager.tv) {
dialogOptions.size = 'fullscreen';
isFullscreen = true;
backButton = true;
dialogOptions.autoFocus = true;
} else {
dialogOptions.modal = false;
dialogOptions.entryAnimation = options.entryAnimation;
dialogOptions.exitAnimation = options.exitAnimation;
dialogOptions.entryAnimationDuration = options.entryAnimationDuration || 140;
dialogOptions.exitAnimationDuration = options.exitAnimationDuration || 100;
dialogOptions.autoFocus = false;
}
var dlg = dialogHelper.createDialog(dialogOptions);
if (isFullscreen) {
dlg.classList.add('actionsheet-fullscreen');
} else {
dlg.classList.add('actionsheet-not-fullscreen');
}
dlg.classList.add('actionSheet');
if (options.dialogClass) {
dlg.classList.add(options.dialogClass);
}
var html = '';
var scrollClassName = layoutManager.tv ? 'scrollY smoothScrollY hiddenScrollY' : 'scrollY';
var style = '';
// Admittedly a hack but right now the scrollbar is being factored into the width which is causing truncation
if (options.items.length > 20) {
var minWidth = dom.getWindowSize().innerWidth >= 300 ? 240 : 200;
style += 'min-width:' + minWidth + 'px;';
}
var i;
var length;
var option;
var renderIcon = false;
var icons = [];
var itemIcon;
for (i = 0, length = options.items.length; i < length; i++) {
option = options.items[i];
itemIcon = option.icon || (option.selected ? 'check' : null);
if (itemIcon) {
renderIcon = true;
}
icons.push(itemIcon || '');
}
if (layoutManager.tv) {
html += '<button is="paper-icon-button-light" class="btnCloseActionSheet hide-mouse-idle-tv" tabindex="-1"><span class="material-icons arrow_back"></span></button>';
}
// If any items have an icon, give them all an icon just to make sure they're all lined up evenly
var center = options.title && (!renderIcon /*|| itemsWithIcons.length != options.items.length*/);
if (center || layoutManager.tv) {
html += '<div class="actionSheetContent actionSheetContent-centered">';
} else {
html += '<div class="actionSheetContent">';
}
if (options.title) {
html += '<h1 class="actionSheetTitle">';
html += options.title;
html += '</h1>';
}
if (options.text) {
html += '<p class="actionSheetText">';
html += options.text;
html += '</p>';
}
var scrollerClassName = 'actionSheetScroller';
if (layoutManager.tv) {
scrollerClassName += ' actionSheetScroller-tv focuscontainer-x focuscontainer-y';
}
html += '<div class="' + scrollerClassName + ' ' + scrollClassName + '" style="' + style + '">';
var menuItemClass = 'listItem listItem-button actionSheetMenuItem';
if (options.border || options.shaded) {
menuItemClass += ' listItem-border';
}
if (options.menuItemClass) {
menuItemClass += ' ' + options.menuItemClass;
}
if (layoutManager.tv) {
menuItemClass += ' listItem-focusscale';
}
if (layoutManager.mobile) {
menuItemClass += ' actionsheet-xlargeFont';
}
for (i = 0, length = options.items.length; i < length; i++) {
option = options.items[i];
if (option.divider) {
html += '<div class="actionsheetDivider"></div>';
continue;
}
var autoFocus = option.selected && layoutManager.tv ? ' autoFocus' : '';
// Check for null in case int 0 was passed in
var optionId = option.id == null || option.id === '' ? option.value : option.id;
html += '<button' + autoFocus + ' is="emby-button" type="button" class="' + menuItemClass + '" data-id="' + optionId + '">';
itemIcon = icons[i];
if (itemIcon) {
html += '<span class="actionsheetMenuItemIcon listItemIcon listItemIcon-transparent material-icons ' + itemIcon + '"></span>';
} else if (renderIcon && !center) {
html += '<span class="actionsheetMenuItemIcon listItemIcon listItemIcon-transparent material-icons check" style="visibility:hidden;"></span>';
}
html += '<div class="listItemBody actionsheetListItemBody">';
html += '<div class="listItemBodyText actionSheetItemText">';
html += (option.name || option.textContent || option.innerText);
html += '</div>';
if (option.secondaryText) {
html += '<div class="listItemBodyText secondary">';
html += option.secondaryText;
html += '</div>';
}
html += '</div>';
if (option.asideText) {
html += '<div class="listItemAside actionSheetItemAsideText">';
html += option.asideText;
html += '</div>';
}
html += '</button>';
}
if (options.showCancel) {
html += '<div class="buttons">';
html += '<button is="emby-button" type="button" class="btnCloseActionSheet">' + globalize.translate('ButtonCancel') + '</button>';
html += '</div>';
}
html += '</div>';
dlg.innerHTML = html;
if (layoutManager.tv) {
centerFocus(dlg.querySelector('.actionSheetScroller'), false, true);
}
var btnCloseActionSheet = dlg.querySelector('.btnCloseActionSheet');
if (btnCloseActionSheet) {
dlg.querySelector('.btnCloseActionSheet').addEventListener('click', function () {
dialogHelper.close(dlg);
});
}
// Seeing an issue in some non-chrome browsers where this is requiring a double click
//var eventName = browser.firefox ? 'mousedown' : 'click';
var selectedId;
var timeout;
if (options.timeout) {
timeout = setTimeout(function () {
dialogHelper.close(dlg);
}, options.timeout);
}
return new Promise(function (resolve, reject) {
var isResolved;
dlg.addEventListener('click', function (e) {
var actionSheetMenuItem = dom.parentWithClass(e.target, 'actionSheetMenuItem');
if (actionSheetMenuItem) {
selectedId = actionSheetMenuItem.getAttribute('data-id');
if (options.resolveOnClick) {
if (options.resolveOnClick.indexOf) {
if (options.resolveOnClick.indexOf(selectedId) !== -1) {
resolve(selectedId);
isResolved = true;
}
} else {
resolve(selectedId);
isResolved = true;
}
}
dialogHelper.close(dlg);
}
});
dlg.addEventListener('close', function () {
if (layoutManager.tv) {
centerFocus(dlg.querySelector('.actionSheetScroller'), false, false);
}
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
if (!isResolved) {
if (selectedId != null) {
if (options.callback) {
options.callback(selectedId);
}
resolve(selectedId);
} else {
reject();
}
}
});
dialogHelper.open(dlg);
var pos = options.positionTo && dialogOptions.size !== 'fullscreen' ? getPosition(options, dlg) : null;
if (pos) {
dlg.style.position = 'fixed';
dlg.style.margin = 0;
dlg.style.left = pos.left + 'px';
dlg.style.top = pos.top + 'px';
}
});
}
return {
show: show
};
});

View file

@ -1,4 +1,4 @@
define(['browser', 'css!./appfooter'], function (browser) {
define(['browser', 'css!./appFooter'], function (browser) {
'use strict';
function render(options) {

View file

@ -1,4 +1,4 @@
define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinManager', 'pluginManager', 'backdrop', 'browser', 'page', 'appSettings', 'apphost', 'connectionManager'], function (loading, globalize, events, viewManager, layoutManager, skinManager, pluginManager, backdrop, browser, page, appSettings, appHost, connectionManager) {
define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdrop', 'browser', 'page', 'appSettings', 'apphost', 'connectionManager'], function (loading, globalize, events, viewManager, skinManager, backdrop, browser, page, appSettings, appHost, connectionManager) {
'use strict';
var appRouter = {

View file

@ -359,7 +359,6 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
return -1 !== supportedFeatures.indexOf(command.toLowerCase());
},
preferVisualCards: browser.android || browser.chrome,
moreIcon: browser.android ? 'more_vert' : 'more_horiz',
getSyncProfile: getSyncProfile,
getDefaultLayout: function () {
if (window.NativeShell) {

View file

@ -306,6 +306,10 @@ button::-moz-focus-inner {
text-align: left;
}
.dialog .cardText {
text-overflow: initial;
}
.cardText-secondary {
font-size: 86%;
}

View file

@ -869,7 +869,7 @@ import 'programStyles';
if (isOuterFooter && options.cardLayout && layoutManager.mobile) {
if (options.cardFooterAside !== 'none') {
html += '<button is="paper-icon-button-light" class="itemAction btnCardOptions cardText-secondary" data-action="menu"><span class="material-icons more_horiz"></span></button>';
html += '<button is="paper-icon-button-light" class="itemAction btnCardOptions cardText-secondary" data-action="menu"><span class="material-icons more_vert"></span></button>';
}
}
@ -1426,7 +1426,7 @@ import 'programStyles';
}
if (options.overlayMoreButton) {
overlayButtons += '<button is="paper-icon-button-light" class="' + btnCssClass + '" data-action="menu"><span class="material-icons cardOverlayButtonIcon more_horiz"></span></button>';
overlayButtons += '<button is="paper-icon-button-light" class="' + btnCssClass + '" data-action="menu"><span class="material-icons cardOverlayButtonIcon more_vert"></span></button>';
}
}
@ -1580,7 +1580,7 @@ import 'programStyles';
html += '<button is="emby-ratingbutton" type="button" data-action="none" class="' + btnCssClass + '" data-id="' + item.Id + '" data-serverid="' + item.ServerId + '" data-itemtype="' + item.Type + '" data-likes="' + likes + '" data-isfavorite="' + (userData.IsFavorite) + '"><span class="material-icons cardOverlayButtonIcon cardOverlayButtonIcon-hover favorite"></span></button>';
}
html += '<button is="paper-icon-button-light" class="' + btnCssClass + '" data-action="menu"><span class="material-icons cardOverlayButtonIcon cardOverlayButtonIcon-hover more_horiz"></span></button>';
html += '<button is="paper-icon-button-light" class="' + btnCssClass + '" data-action="menu"><span class="material-icons cardOverlayButtonIcon cardOverlayButtonIcon-hover more_vert"></span></button>';
html += '</div>';
html += '</div>';

View file

@ -79,7 +79,7 @@ define(['dom', 'dialogHelper', 'loading', 'connectionManager', 'globalize', 'act
function getEditorHtml() {
var html = '';
html += '<div class="formDialogContent">';
html += '<div class="formDialogContent smoothScrollY">';
html += '<div class="dialogContentInner dialog-content-centered">';
html += '<form style="margin:auto;">';
html += '<h1>' + globalize.translate('HeaderChannels') + '</h1>';

View file

@ -54,7 +54,13 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
// production version registered with google
// replace this value if you want to test changes on another instance
var applicationID = 'F007D354';
var applicationStable = 'F007D354';
var applicationNightly = '6F511C87';
var applicationID = applicationStable;
if (userSettings.chromecastVersion === 'nightly') {
applicationID = applicationNightly;
}
var messageNamespace = 'urn:x-cast:com.connectsdk';

View file

@ -1,57 +0,0 @@
define(['connectionManager', 'confirm', 'appRouter', 'globalize'], function (connectionManager, confirm, appRouter, globalize) {
'use strict';
function alertText(options) {
return new Promise(function (resolve, reject) {
require(['alert'], function (alert) {
alert(options).then(resolve, resolve);
});
});
}
function deleteItem(options) {
var item = options.item;
var itemId = item.Id;
var parentId = item.SeasonId || item.SeriesId || item.ParentId;
var serverId = item.ServerId;
var msg = globalize.translate('ConfirmDeleteItem');
var title = globalize.translate('HeaderDeleteItem');
var apiClient = connectionManager.getApiClient(item.ServerId);
return confirm({
title: title,
text: msg,
confirmText: globalize.translate('Delete'),
primary: 'delete'
}).then(function () {
return apiClient.deleteItem(itemId).then(function () {
if (options.navigate) {
if (parentId) {
appRouter.showItem(parentId, serverId);
} else {
appRouter.goHome();
}
}
}, function (err) {
var result = function () {
return Promise.reject(err);
};
return alertText(globalize.translate('ErrorDeletingItem')).then(result, result);
});
});
}
return {
deleteItem: deleteItem
};
});

View file

@ -126,25 +126,10 @@
}
@media all and (min-width: 80em) and (min-height: 45em) {
.dialog-medium {
width: 80%;
height: 80%;
}
.dialog-medium-tall {
width: 80%;
height: 90%;
}
.dialog-small {
width: 60%;
height: 80%;
}
.dialog-fullscreen-border {
width: 90%;
height: 90%;
}
}
.noScroll {

View file

@ -89,7 +89,6 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
var instruction = options.instruction ? options.instruction + '<br/><br/>' : '';
html += '<div class="infoBanner" style="margin-bottom:1.5em;">';
html += instruction;
html += globalize.translate('MessageDirectoryPickerInstruction', '<b>\\\\server</b>', '<b>\\\\192.168.1.101</b>');
if ('bsd' === systemInfo.OperatingSystem.toLowerCase()) {
html += '<br/>';
html += '<br/>';
@ -126,7 +125,7 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
html += '<div class="inputContainer" style="margin-top:2em;">';
html += '<input is="emby-input" id="txtNetworkPath" type="text" label="' + globalize.translate('LabelOptionalNetworkPath') + '"/>';
html += '<div class="fieldDescription">';
html += globalize.translate('LabelOptionalNetworkPathHelp');
html += globalize.translate('LabelOptionalNetworkPathHelp', '<b>\\\\server</b>', '<b>\\\\192.168.1.101</b>');
html += '</div>';
html += '</div>';
}
@ -253,7 +252,7 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
var systemInfo = responses[0];
var initialPath = responses[1];
var dlg = dialogHelper.createDialog({
size: 'medium-tall',
size: 'small',
removeOnClose: true,
scrollY: false
});

View file

@ -269,7 +269,7 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
}
function embed(options, self) {
require(['text!./displaysettings.template.html'], function (template) {
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) {

View file

@ -56,7 +56,7 @@
<div class="fieldDescription">
<div>${LabelDisplayLanguageHelp}</div>
<div class="learnHowToContributeContainer hide" style="margin-top: .25em;">
<a is="emby-linkbutton" class="button-link" href="https://github.com/jellyfin/jellyfin" target="_blank">${LearnHowYouCanContribute}</a>
<a is="emby-linkbutton" rel="noopener noreferrer" class="button-link" href="https://github.com/jellyfin/jellyfin" target="_blank">${LearnHowYouCanContribute}</a>
</div>
</div>
</div>

View file

@ -19,6 +19,10 @@
margin-bottom: 0;
}
.formDialogHeaderTitle:first-child {
margin-left: 1em;
}
.formDialogContent:not(.no-grow) {
flex-grow: 1;
}
@ -46,10 +50,16 @@
right: 0;
display: flex;
position: absolute;
padding: 1.25em 1em;
padding: 1em 1em;
/* Without this emby-checkbox is able to appear on top */
z-index: 1;
align-items: flex-end;
justify-content: flex-end;
flex-wrap: wrap;
}
.layout-tv .formDialogFooter {
align-items: center;
justify-content: center;
flex-wrap: wrap;
@ -69,8 +79,12 @@
.formDialogFooterItem {
margin: 0.5em !important;
flex-grow: 1;
text-align: center;
flex-basis: 12em;
}
.layout-tv .formDialogFooterItem {
flex-grow: 1;
flex-basis: 0;
}

View file

@ -1,5 +1,7 @@
<div class="formDialogHeader">
<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><span class="material-icons arrow_back"></span></button>
<button is="paper-icon-button-light" class="btnCancel autoSize" title="${LabelPrevious}" tabindex="-1">
<span class="material-icons arrow_back" aria-hidden="true"></span>
</button>
<h3 class="formDialogHeaderTitle">
${Settings}
</h3>

View file

@ -9,8 +9,8 @@
<div class="guide-headerTimeslots">
<div class="guide-channelTimeslotHeader">
<button is="paper-icon-button-light" type="button" class="btnGuideViewSettings">
<span class="material-icons btnGuideViewSettingsIcon more_horiz"></span>
<button is="paper-icon-button-light" type="button" class="btnGuideViewSettings" title="${ButtonMore}">
<span class="material-icons btnGuideViewSettingsIcon more_vert" aria-hidden="true"></span>
</button>
</div>
<div class="timeslotHeaders scrollX guideScroller"></div>
@ -29,10 +29,10 @@
</div>
<div class="guideOptions hide">
<button is="paper-icon-button-light" type="button" class="btnPreviousPage">
<span class="material-icons arrow_back"></span>
<button is="paper-icon-button-light" type="button" class="btnPreviousPage" title="${LabelPrevious}">
<span class="material-icons arrow_back" aria-hidden="true"></span>
</button>
<button is="paper-icon-button-light" type="button" class="btnNextPage">
<span class="material-icons arrow_forward"></span>
<button is="paper-icon-button-light" type="button" class="btnNextPage" title="${LabelNext}">
<span class="material-icons arrow_forward" aria-hidden="true"></span>
</button>
</div>

View file

@ -470,7 +470,7 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
function embed(options, self) {
require(['text!./homescreensettings.template.html'], function (template) {
require(['text!./homeScreenSettings.template.html'], function (template) {
for (var i = 1; i <= numConfigurableSections; i++) {
template = template.replace('{section' + i + 'label}', globalize.translate('LabelHomeScreenSectionValue', i));

View file

@ -136,7 +136,10 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp
requireHlsPlayer(function () {
var hls = new Hls({
manifestLoadingTimeOut: 20000
manifestLoadingTimeOut: 20000,
xhrSetup: function(xhr, url) {
xhr.withCredentials = true;
}
//appendErrorMaxRetry: 6,
//debug: true
});
@ -155,6 +158,9 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp
elem.autoplay = true;
// Safari will not send cookies without this
elem.crossOrigin = 'use-credentials';
return htmlMediaHelper.applySrc(elem, val, options).then(function () {
self._currentSrc = val;

View file

@ -194,7 +194,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve
}
};
events.map(function (name) {
element.addEventListener(name, onMediaChange);
return element.addEventListener(name, onMediaChange);
});
}
}

View file

@ -106,10 +106,18 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
});
}
function hidePrePlaybackPage() {
let animatedPage = document.querySelector('.page:not(.hide)');
animatedPage.classList.add('hide');
// At this point, we must hide the scrollbar placeholder, so it's not being displayed while the item is being loaded
document.body.classList.remove('force-scroll');
}
function zoomIn(elem) {
return new Promise(function (resolve, reject) {
var duration = 240;
elem.style.animation = 'htmlvideoplayer-zoomin ' + duration + 'ms ease-in normal';
hidePrePlaybackPage();
dom.addEventListener(elem, dom.whichAnimationEvent(), resolve, {
once: true
});
@ -290,7 +298,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
return createMediaElement(options).then(function (elem) {
return updateVideoUrl(options, options.mediaSource).then(function () {
return updateVideoUrl(options).then(function () {
return setCurrentSrc(elem, options);
});
});
@ -330,7 +338,10 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
requireHlsPlayer(function () {
var hls = new Hls({
manifestLoadingTimeOut: 20000
manifestLoadingTimeOut: 20000,
xhrSetup: function(xhr, xhr_url) {
xhr.withCredentials = true;
}
//appendErrorMaxRetry: 6,
//debug: true
});
@ -551,6 +562,9 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
elem.autoplay = true;
// Safari will not send cookies without this
elem.crossOrigin = 'use-credentials';
return htmlMediaHelper.applySrc(elem, val, options).then(function () {
self._currentSrc = val;
@ -837,7 +851,6 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
function onNavigatedToOsd() {
var dlg = videoDialog;
if (dlg) {
dlg.classList.remove('videoPlayerContainer-withBackdrop');
dlg.classList.remove('videoPlayerContainer-onTop');
onStartedAndNavigatedToOsd();
@ -874,7 +887,6 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
} else {
appRouter.setTransparency('backdrop');
videoDialog.classList.remove('videoPlayerContainer-withBackdrop');
videoDialog.classList.remove('videoPlayerContainer-onTop');
onStartedAndNavigatedToOsd();
@ -1295,12 +1307,6 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
function createMediaElement(options) {
if (browser.tv || browser.iOS || browser.mobile) {
// too slow
// also on iOS, the backdrop image doesn't look right
// on android mobile, it works, but can be slow to have the video surface fully cover the backdrop
options.backdropUrl = null;
}
return new Promise(function (resolve, reject) {
var dlg = document.querySelector('.videoPlayerContainer');
@ -1315,11 +1321,6 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
dlg.classList.add('videoPlayerContainer');
if (options.backdropUrl) {
dlg.classList.add('videoPlayerContainer-withBackdrop');
dlg.style.backgroundImage = "url('" + options.backdropUrl + "')";
}
if (options.fullscreen) {
dlg.classList.add('videoPlayerContainer-onTop');
}
@ -1354,6 +1355,9 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
videoElement.addEventListener('click', onClick);
videoElement.addEventListener('dblclick', onDblClick);
videoElement.addEventListener('waiting', onWaiting);
if (options.backdropUrl) {
videoElement.poster = options.backdropUrl;
}
document.body.insertBefore(dlg, document.body.firstChild);
videoDialog = dlg;
@ -1373,15 +1377,11 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
resolve(videoElement);
});
} else {
hidePrePlaybackPage();
resolve(videoElement);
}
});
} else {
if (options.backdropUrl) {
dlg.classList.add('videoPlayerContainer-withBackdrop');
dlg.style.backgroundImage = "url('" + options.backdropUrl + "')";
}
resolve(dlg.querySelector('video'));
}
});

View file

@ -6,20 +6,9 @@
right: 0;
display: flex;
align-items: center;
}
.videoPlayerContainer:not(.videoPlayerContainer-withBackdrop) {
background: #000 !important;
}
.videoPlayerContainer-withBackdrop {
background-repeat: no-repeat;
background-position: center center;
background-size: cover;
background-attachment: fixed;
background-color: #000;
}
.videoPlayerContainer-onTop {
z-index: 1000;
}

View file

@ -320,7 +320,7 @@ define(['dom', 'loading', 'apphost', 'dialogHelper', 'connectionManager', 'image
function showEditor(itemId, serverId, itemType) {
loading.show();
require(['text!./imagedownloader.template.html'], function (template) {
require(['text!./imageDownloader.template.html'], function (template) {
var apiClient = connectionManager.getApiClient(serverId);
@ -334,7 +334,7 @@ define(['dom', 'loading', 'apphost', 'dialogHelper', 'connectionManager', 'image
if (layoutManager.tv) {
dialogOptions.size = 'fullscreen';
} else {
dialogOptions.size = 'fullscreen-border';
dialogOptions.size = 'small';
}
var dlg = dialogHelper.createDialog(dialogOptions);

View file

@ -5,7 +5,7 @@
</h3>
</div>
<div class="formDialogContent">
<div class="formDialogContent smoothScrollY">
<div class="dialogContentInner">
<div class="flex align-items-center justify-content-center flex-wrap-wrap" style="margin: 2em 0;">

View file

@ -82,12 +82,12 @@ define(['globalize', 'dom', 'dialogHelper', 'emby-checkbox', 'emby-select', 'emb
this.show = function (itemType, options, availableOptions) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'components/imageoptionseditor/imageoptionseditor.template.html', true);
xhr.open('GET', 'components/imageOptionsEditor/imageOptionsEditor.template.html', true);
xhr.onload = function (e) {
var template = this.response;
var dlg = dialogHelper.createDialog({
size: 'medium-tall',
size: 'small',
removeOnClose: true,
scrollY: false
});

View file

@ -125,7 +125,7 @@ define(['dialogHelper', 'connectionManager', 'dom', 'loading', 'scrollHelper', '
options = options || {};
require(['text!./imageuploader.template.html'], function (template) {
require(['text!./imageUploader.template.html'], function (template) {
currentItemId = options.itemId;
currentServerId = options.serverId;
@ -137,7 +137,7 @@ define(['dialogHelper', 'connectionManager', 'dom', 'loading', 'scrollHelper', '
if (layoutManager.tv) {
dialogOptions.size = 'fullscreen';
} else {
dialogOptions.size = 'fullscreen-border';
dialogOptions.size = 'small';
}
var dlg = dialogHelper.createDialog(dialogOptions);

View file

@ -5,7 +5,7 @@
</h3>
</div>
<div class="formDialogContent">
<div class="formDialogContent smoothScrollY">
<div class="dialogContentInner">
<form class="uploadItemImageForm" style="max-width: 100%;">

View file

@ -457,7 +457,7 @@ define(['dialogHelper', 'connectionManager', 'loading', 'dom', 'layoutManager',
if (layoutManager.tv) {
dialogOptions.size = 'fullscreen';
} else {
dialogOptions.size = 'fullscreen-border';
dialogOptions.size = 'small';
}
var dlg = dialogHelper.createDialog(dialogOptions);

View file

@ -5,7 +5,7 @@
</h3>
</div>
<div class="formDialogContent">
<div class="formDialogContent smoothScrollY">
<div class="dialogContentInner">
<div id="imagesContainer">

View file

@ -1,38 +0,0 @@
define(['dom'], function (dom) {
'use strict';
function loadImage(elem, url) {
if (!elem) {
return Promise.reject('elem cannot be null');
}
if (elem.tagName !== 'IMG') {
elem.style.backgroundImage = "url('" + url + "')";
return Promise.resolve();
//return loadImageIntoImg(document.createElement('img'), url).then(function () {
// elem.style.backgroundImage = "url('" + url + "')";
// return Promise.resolve();
//});
}
return loadImageIntoImg(elem, url);
}
function loadImageIntoImg(elem, url) {
return new Promise(function (resolve, reject) {
dom.addEventListener(elem, 'load', resolve, {
once: true
});
elem.setAttribute('src', url);
});
}
return {
loadImage: loadImage
};
});

View file

@ -1,12 +1,13 @@
define(['datetime', 'itemHelper', 'emby-progressbar', 'css!./indicators.css', 'material-icons'], function (datetime, itemHelper) {
'use strict';
import datetime from 'datetime';
import itemHelper from 'itemHelper';
import 'emby-progressbar';
import 'css!./indicators.css';
import 'material-icons';
function enableProgressIndicator(item) {
if (item.MediaType === 'Video') {
if (item.Type !== 'TvChannel') {
export function enableProgressIndicator(item) {
if (item.MediaType === 'Video' && item.Type !== 'TvChannel') {
return true;
}
}
if (item.Type === 'AudioBook' || item.Type === 'AudioPodcast') {
return true;
@ -15,26 +16,22 @@ define(['datetime', 'itemHelper', 'emby-progressbar', 'css!./indicators.css', 'm
return false;
}
function getProgressHtml(pct, options) {
var containerClass = 'itemProgressBar';
if (options) {
if (options.containerClass) {
export function getProgressHtml(pct, options) {
let containerClass = 'itemProgressBar';
if (options && options.containerClass) {
containerClass += ' ' + options.containerClass;
}
}
return '<div class="' + containerClass + '"><div class="itemProgressBarForeground" style="width:' + pct + '%;"></div></div>';
}
function getAutoTimeProgressHtml(pct, options, isRecording, start, end) {
var containerClass = 'itemProgressBar';
if (options) {
if (options.containerClass) {
let containerClass = 'itemProgressBar';
if (options && options.containerClass) {
containerClass += ' ' + options.containerClass;
}
}
var foregroundClass = 'itemProgressBarForeground';
let foregroundClass = 'itemProgressBarForeground';
if (isRecording) {
foregroundClass += ' itemProgressBarForeground-recording';
}
@ -42,10 +39,11 @@ define(['datetime', 'itemHelper', 'emby-progressbar', 'css!./indicators.css', 'm
return '<div is="emby-progressbar" data-automode="time" data-starttime="' + start + '" data-endtime="' + end + '" class="' + containerClass + '"><div class="' + foregroundClass + '" style="width:' + pct + '%;"></div></div>';
}
function getProgressBarHtml(item, options) {
var pct;
export function getProgressBarHtml(item, options) {
let pct;
if (enableProgressIndicator(item) && item.Type !== 'Recording') {
var userData = options ? (options.userData || item.UserData) : item.UserData;
const userData = options && options.userData ? options.userData : item.UserData;
if (userData) {
pct = userData.PlayedPercentage;
if (pct && pct < 100) {
@ -55,8 +53,8 @@ define(['datetime', 'itemHelper', 'emby-progressbar', 'css!./indicators.css', 'm
}
if ((item.Type === 'Program' || item.Type === 'Timer' || item.Type === 'Recording') && item.StartDate && item.EndDate) {
var startDate = 0;
var endDate = 1;
let startDate = 0;
let endDate = 1;
try {
startDate = datetime.parseISO8601Date(item.StartDate).getTime();
@ -65,12 +63,12 @@ define(['datetime', 'itemHelper', 'emby-progressbar', 'css!./indicators.css', 'm
console.error(err);
}
var now = new Date().getTime();
var total = endDate - startDate;
const now = new Date().getTime();
const total = endDate - startDate;
pct = 100 * ((now - startDate) / total);
if (pct > 0 && pct < 100) {
var isRecording = item.Type === 'Timer' || item.Type === 'Recording' || item.TimerId;
const isRecording = item.Type === 'Timer' || item.Type === 'Recording' || item.TimerId;
return getAutoTimeProgressHtml(pct, options, isRecording, startDate, endDate);
}
}
@ -78,13 +76,13 @@ define(['datetime', 'itemHelper', 'emby-progressbar', 'css!./indicators.css', 'm
return '';
}
function enablePlayedIndicator(item) {
export function enablePlayedIndicator(item) {
return itemHelper.canMarkPlayed(item);
}
function getPlayedIndicator(item) {
export function getPlayedIndicatorHtml(item) {
if (enablePlayedIndicator(item)) {
var userData = item.UserData || {};
let userData = item.UserData || {};
if (userData.UnplayedItemCount) {
return '<div class="countIndicator indicator">' + userData.UnplayedItemCount + '</div>';
}
@ -97,25 +95,18 @@ define(['datetime', 'itemHelper', 'emby-progressbar', 'css!./indicators.css', 'm
return '';
}
function getCountIndicatorHtml(count) {
return '<div class="countIndicator indicator">' + count + '</div>';
}
function getChildCountIndicatorHtml(item, options) {
var minCount = 0;
if (options) {
minCount = options.minCount || minCount;
}
export function getChildCountIndicatorHtml(item, options) {
const minCount = options && options.minCount ? options.minCount : 0;
if (item.ChildCount && item.ChildCount > minCount) {
return getCountIndicatorHtml(item.ChildCount);
return '<div class="countIndicator indicator">' + item.ChildCount + '</div>';
}
return '';
}
function getTimerIndicator(item) {
var status;
export function getTimerIndicator(item) {
let status;
if (item.Type === 'SeriesTimer') {
return '<span class="material-icons timerIndicator indicatorIcon fiber_smart_record"></span>';
@ -138,7 +129,7 @@ define(['datetime', 'itemHelper', 'emby-progressbar', 'css!./indicators.css', 'm
return '<span class="material-icons timerIndicator indicatorIcon fiber_manual_record"></span>';
}
function getSyncIndicator(item) {
export function getSyncIndicator(item) {
if (item.SyncPercent === 100) {
return '<div class="syncIndicator indicator fullSyncIndicator"><span class="material-icons indicatorIcon file_download"></span></div>';
} else if (item.SyncPercent != null) {
@ -148,28 +139,23 @@ define(['datetime', 'itemHelper', 'emby-progressbar', 'css!./indicators.css', 'm
return '';
}
function getTypeIndicator(item) {
if (item.Type === 'Video') {
return '<div class="indicator videoIndicator"><span class="material-icons indicatorIcon videocam"></span></div>';
}
if (item.Type === 'Folder') {
return '<div class="indicator videoIndicator"><span class="material-icons indicatorIcon folder"></span></div>';
}
if (item.Type === 'PhotoAlbum') {
return '<div class="indicator videoIndicator"><span class="material-icons indicatorIcon photo_album"></span></div>';
}
if (item.Type === 'Photo') {
return '<div class="indicator videoIndicator"><span class="material-icons indicatorIcon photo"></span></div>';
export function getTypeIndicator(item) {
const iconT = {
'Video' : 'videocam',
'Folder' : 'folder',
'PhotoAlbum' : 'photo_album',
'Photo' : 'photo'
};
const icon = iconT[item.Type];
return icon ? '<div class="indicator videoIndicator"><span class="material-icons indicatorIcon ' + icon + '"></span></div>' : '';
}
return '';
}
function getMissingIndicator(item) {
export function getMissingIndicator(item) {
if (item.Type === 'Episode' && item.LocationType === 'Virtual') {
if (item.PremiereDate) {
try {
var premiereDate = datetime.parseISO8601Date(item.PremiereDate).getTime();
const premiereDate = datetime.parseISO8601Date(item.PremiereDate).getTime();
if (premiereDate > new Date().getTime()) {
return '<div class="unairedIndicator">Unaired</div>';
}
@ -183,10 +169,10 @@ define(['datetime', 'itemHelper', 'emby-progressbar', 'css!./indicators.css', 'm
return '';
}
return {
export default {
getProgressHtml: getProgressHtml,
getProgressBarHtml: getProgressBarHtml,
getPlayedIndicatorHtml: getPlayedIndicator,
getPlayedIndicatorHtml: getPlayedIndicatorHtml,
getChildCountIndicatorHtml: getChildCountIndicatorHtml,
enableProgressIndicator: enableProgressIndicator,
getTimerIndicator: getTimerIndicator,
@ -195,4 +181,3 @@ define(['datetime', 'itemHelper', 'emby-progressbar', 'css!./indicators.css', 'm
getTypeIndicator: getTypeIndicator,
getMissingIndicator: getMissingIndicator
};
});

View file

@ -450,7 +450,7 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
navigator.share({
title: item.Name,
text: item.Overview,
url: 'https://github.com/jellyfin/jellyfin'
url: `${apiClient.serverAddress()}/web/index.html#!/${appRouter.getRouteUrl(item)}`
});
break;
case 'album':

View file

@ -348,7 +348,7 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
currentItemType = currentItem.Type;
var dialogOptions = {
size: 'fullscreen-border',
size: 'small',
removeOnClose: true,
scrollY: false
};
@ -429,7 +429,7 @@ define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize',
require(['text!./itemidentifier.template.html'], function (template) {
var dialogOptions = {
size: 'fullscreen-border',
size: 'small',
removeOnClose: true,
scrollY: false
};

View file

@ -109,8 +109,8 @@ define(['globalize', 'dom', 'emby-checkbox', 'emby-select', 'emby-input'], funct
html += '<div class="metadataFetcher" data-type="' + availableTypeOptions.Type + '">';
html += '<h3 class="checkboxListLabel">' + globalize.translate('LabelTypeMetadataDownloaders', globalize.translate(availableTypeOptions.Type)) + '</h3>';
html += '<div class="checkboxList paperList checkboxList-paperList">';
for (var i = 0; i < plugins.length; i++) {
var plugin = plugins[i];
plugins.forEach((plugin, index) => {
html += '<div class="listItem metadataFetcherItem sortableOption" data-pluginname="' + plugin.Name + '">';
var isChecked = libraryOptionsForType.MetadataFetchers ? -1 !== libraryOptionsForType.MetadataFetchers.indexOf(plugin.Name) : plugin.DefaultEnabled;
var checkedHtml = isChecked ? ' checked="checked"' : '';
@ -120,8 +120,9 @@ define(['globalize', 'dom', 'emby-checkbox', 'emby-select', 'emby-input'], funct
html += plugin.Name;
html += '</h3>';
html += '</div>';
i > 0 ? html += '<button type="button" is="paper-icon-button-light" title="' + globalize.translate('ButtonUp') + '" class="btnSortableMoveUp btnSortable" data-pluginindex="' + i + '"><span class="material-icons keyboard_arrow_up"></span></button>' : plugins.length > 1 && (html += '<button type="button" is="paper-icon-button-light" title="' + globalize.translate('ButtonDown') + '" class="btnSortableMoveDown btnSortable" data-pluginindex="' + i + '"><span class="material-icons keyboard_arrow_down"></span></button>'), html += '</div>';
}
index > 0 ? html += '<button type="button" is="paper-icon-button-light" title="' + globalize.translate('ButtonUp') + '" class="btnSortableMoveUp btnSortable" data-pluginindex="' + index + '"><span class="material-icons keyboard_arrow_up"></span></button>' : plugins.length > 1 && (html += '<button type="button" is="paper-icon-button-light" title="' + globalize.translate('ButtonDown') + '" class="btnSortableMoveDown btnSortable" data-pluginindex="' + index + '"><span class="material-icons keyboard_arrow_down"></span></button>'), html += '</div>';
});
html += '</div>';
html += '<div class="fieldDescription">' + globalize.translate('LabelMetadataDownloadersHelp') + '</div>';
html += '</div>';
@ -292,11 +293,15 @@ define(['globalize', 'dom', 'emby-checkbox', 'emby-select', 'emby-input'], funct
function showImageOptionsForType(type) {
require(['imageoptionseditor'], function(ImageOptionsEditor) {
var typeOptions = getTypeOptions(currentLibraryOptions, type);
typeOptions || (typeOptions = {
if (!typeOptions) {
typeOptions = {
Type: type
}, currentLibraryOptions.TypeOptions.push(typeOptions));
};
currentLibraryOptions.TypeOptions.push(typeOptions);
}
var availableOptions = getTypeOptions(currentAvailableOptions || {}, type);
(new ImageOptionsEditor).show(type, typeOptions, availableOptions);
var imageOptionsEditor = new ImageOptionsEditor();
imageOptionsEditor.show(type, typeOptions, availableOptions);
});
}
@ -315,10 +320,16 @@ define(['globalize', 'dom', 'emby-checkbox', 'emby-select', 'emby-input'], funct
var list = dom.parentWithClass(li, 'paperList');
if (btnSortable.classList.contains('btnSortableMoveDown')) {
var next = li.nextSibling;
next && (li.parentNode.removeChild(li), next.parentNode.insertBefore(li, next.nextSibling));
if (next) {
li.parentNode.removeChild(li);
next.parentNode.insertBefore(li, next.nextSibling);
}
} else {
var prev = li.previousSibling;
prev && (li.parentNode.removeChild(li), prev.parentNode.insertBefore(li, prev));
if (prev) {
li.parentNode.removeChild(li);
prev.parentNode.insertBefore(li, prev);
}
}
Array.prototype.forEach.call(list.querySelectorAll('.sortableOption'), adjustSortableListElement);
}

View file

@ -426,7 +426,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
html += '<div class="' + cssClass + '">';
const moreIcon = 'more_horiz';
const moreIcon = 'more_vert';
html += getTextLinesHtml(textlines, isLargeStyle);

View file

@ -182,12 +182,12 @@ define(['loading', 'dialogHelper', 'dom', 'jQuery', 'components/libraryoptionsed
currentResolve = resolve;
hasChanges = false;
var xhr = new XMLHttpRequest();
xhr.open('GET', 'components/medialibrarycreator/medialibrarycreator.template.html', true);
xhr.open('GET', 'components/mediaLibraryCreator/mediaLibraryCreator.template.html', true);
xhr.onload = function (e) {
var template = this.response;
var dlg = dialogHelper.createDialog({
size: 'medium-tall',
size: 'small',
modal: false,
removeOnClose: true,
scrollY: false

View file

@ -199,12 +199,12 @@ define(['jQuery', 'loading', 'dialogHelper', 'dom', 'components/libraryoptionsed
currentDeferred = deferred;
hasChanges = false;
var xhr = new XMLHttpRequest();
xhr.open('GET', 'components/medialibraryeditor/medialibraryeditor.template.html', true);
xhr.open('GET', 'components/mediaLibraryEditor/mediaLibraryEditor.template.html', true);
xhr.onload = function (e) {
var template = this.response;
var dlg = dialogHelper.createDialog({
size: 'medium-tall',
size: 'small',
modal: false,
removeOnClose: true,
scrollY: false

View file

@ -490,26 +490,26 @@ define(['datetime', 'globalize', 'appRouter', 'itemHelper', 'indicators', 'mater
if (i.IsInterlaced) {
return '1440i';
}
return '1440P';
return '1440p';
}
if (width >= 1800 || height >= 1000) {
if (i.IsInterlaced) {
return '1080i';
}
return '1080P';
return '1080p';
}
if (width >= 1200 || height >= 700) {
if (i.IsInterlaced) {
return '720i';
}
return '720P';
return '720p';
}
if (width >= 700 || height >= 400) {
if (i.IsInterlaced) {
return '480i';
}
return '480P';
return '480p';
}
}

View file

@ -245,50 +245,6 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi
});
}
function showMoreMenu(context, button, user) {
require(['itemContextMenu'], function (itemContextMenu) {
var item = currentItem;
itemContextMenu.show({
item: item,
positionTo: button,
edit: false,
editImages: true,
editSubtitles: true,
sync: false,
share: false,
play: false,
queue: false,
user: user
}).then(function (result) {
if (result.deleted) {
afterDeleted(context, item);
} else if (result.updated) {
reload(context, item.Id, item.ServerId);
}
});
});
}
function afterDeleted(context, item) {
var parentId = item.ParentId || item.SeasonId || item.SeriesId;
if (parentId) {
reload(context, parentId, item.ServerId);
} else {
require(['appRouter'], function (appRouter) {
appRouter.goHome();
});
}
}
function onEditorClick(e) {
var btnRemoveFromEditorList = dom.parentWithClass(e.target, 'btnRemoveFromEditorList');
@ -307,6 +263,12 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi
return connectionManager.getApiClient(currentItem.ServerId);
}
function bindAll(elems, eventName, fn) {
for (var i = 0, length = elems.length; i < length; i++) {
elems[i].addEventListener(eventName, fn);
}
}
function init(context, apiClient) {
context.querySelector('.externalIds').addEventListener('click', function (e) {
@ -322,19 +284,16 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi
}
});
context.querySelector('.btnCancel').addEventListener('click', function () {
if (!layoutManager.desktop) {
context.querySelector('.btnBack').classList.remove('hide');
context.querySelector('.btnClose').classList.add('hide');
}
bindAll(context.querySelectorAll('.btnCancel'), 'click', function (event) {
event.preventDefault();
closeDialog(false);
});
context.querySelector('.btnMore').addEventListener('click', function (e) {
getApiClient().getCurrentUser().then(function (user) {
showMoreMenu(context, e.target, user);
});
});
context.querySelector('.btnHeaderSave').addEventListener('click', function (e) {
context.querySelector('.btnSave').click();
@ -349,8 +308,8 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi
}
});
context.removeEventListener('click', onEditorClick);
context.addEventListener('click', onEditorClick);
context.removeEventListener('submit', onEditorClick);
context.addEventListener('submit', onEditorClick);
var form = context.querySelector('form');
form.removeEventListener('submit', onSubmit);
@ -791,11 +750,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi
return a.Name;
}).join(';');
if (item.Type === 'Series') {
context.querySelector('#selectDisplayOrder').value = item.DisplayOrder || '';
} else {
context.querySelector('#selectDisplayOrder').value = item.DisplayOrder || '';
}
context.querySelector('#txtArtist').value = (item.ArtistItems || []).map(function (a) {
return a.Name;
@ -1071,7 +1026,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi
function show(itemId, serverId, resolve, reject) {
loading.show();
require(['text!./metadataeditor.template.html'], function (template) {
require(['text!./metadataEditor.template.html'], function (template) {
var dialogOptions = {
removeOnClose: true,
@ -1081,7 +1036,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi
if (layoutManager.tv) {
dialogOptions.size = 'fullscreen';
} else {
dialogOptions.size = 'medium-tall';
dialogOptions.size = 'small';
}
var dlg = dialogHelper.createDialog(dialogOptions);
@ -1128,7 +1083,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi
loading.show();
require(['text!./metadataeditor.template.html'], function (template) {
require(['text!./metadataEditor.template.html'], function (template) {
elem.innerHTML = globalize.translateDocument(template, 'core');

View file

@ -1,5 +1,5 @@
<div class="formDialogHeader">
<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><span class="material-icons arrow_back"></span></button>
<button is="paper-icon-button-light" class="btnCancel btnBack autoSize hide" tabindex="-1"><span class="material-icons arrow_back"></span></button>
<h3 class="formDialogHeaderTitle">
${Edit}
</h3>
@ -8,8 +8,8 @@
<span class="material-icons check"></span>
<span>${Save}</span>
</button>
<button is="paper-icon-button-light" class="btnMore autoSize" tabindex="-1">
<span class="material-icons more_horiz"></span>
<button is="paper-icon-button-light" class="btnCancel btnClose autoSize" tabindex="-1">
<span class="material-icons close"></span>
</button>
</div>
</div>
@ -57,12 +57,14 @@
<div id="fldAlbum" class="hide inputContainer">
<input is="emby-input" id="txtAlbum" type="text" label="${LabelAlbum}" />
</div>
<div class="inlineForm">
<div id="fldParentIndexNumber" class="hide inputContainer">
<input is="emby-input" id="txtParentIndexNumber" type="number" />
</div>
<div id="fldIndexNumber" class="hide inputContainer">
<input is="emby-input" id="txtIndexNumber" type="number" pattern="[0-9]*" />
</div>
</div>
<div id="fldCommunityRating" class="hide inputContainer">
<input is="emby-input" id="txtCommunityRating" type="number" step=".1" min="0" max="10" label="${LabelCommunityRating}" />
</div>
@ -129,12 +131,15 @@
<div id="fldSeriesRuntime" class="inputContainer hide">
<input is="emby-input" id="txtSeriesRuntime" type="number" label="${LabelRuntimeMinutes}" />
</div>
<div class="inlineForm">
<div id="fldOfficialRating" class="selectContainer hide">
<select is="emby-select" id="selectOfficialRating" label="${LabelParentalRating}"></select>
</div>
<div id="fldCustomRating" class="selectContainer hide">
<select is="emby-select" id="selectCustomRating" label="${LabelCustomRating}"></select>
</div>
</div>
<div class="inlineForm">
<div id="fldOriginalAspectRatio" class="inputContainer hide">
<input is="emby-input" id="txtOriginalAspectRatio" type="text" label="${LabelOriginalAspectRatio}" />
</div>
@ -148,6 +153,7 @@
<option value="MVC">MVC</option>
</select>
</div>
</div>
<div id="fldDisplayOrder" class="fldDisplaySetting selectContainer hide">
<select is="emby-select" id="selectDisplayOrder" label="${LabelDisplayOrder}"></select>
@ -160,6 +166,7 @@
<h2>
${HeaderSpecialEpisodeInfo}
</h2>
<div class="inlineForm">
<div class="inputContainer">
<input is="emby-input" id="txtAirsBeforeSeason" type="number" pattern="[0-9]*" label="${LabelAirsBeforeSeason}" />
</div>
@ -170,6 +177,7 @@
<input is="emby-input" id="txtAirsBeforeEpisode" type="number" pattern="[0-9]*" label="${LabelAirsBeforeEpisode}" />
</div>
</div>
</div>
<div class="detailSection externalIdsSection hide">
<h2>
@ -240,8 +248,11 @@
</div>
<br />
<div class="formDialogFooter">
<button is="emby-button" class="raised button-cancel block btnCancel formDialogFooterItem">
<span>${Cancel}</span>
</button>
<button is="emby-button" type="submit" class="raised button-submit block btnSave formDialogFooterItem">
<span>${Save}</span>
<span>${SaveChanges}</span>
</button>
</div>

View file

@ -11,7 +11,7 @@ define(['dialogHelper', 'layoutManager', 'globalize', 'require', 'paper-icon-but
function show(person) {
return new Promise(function (resolve, reject) {
require(['text!./personeditor.template.html'], function (template) {
require(['text!./personEditor.template.html'], function (template) {
var dialogOptions = {
removeOnClose: true,
@ -21,7 +21,7 @@ define(['dialogHelper', 'layoutManager', 'globalize', 'require', 'paper-icon-but
if (layoutManager.tv) {
dialogOptions.size = 'fullscreen';
} else {
dialogOptions.size = 'medium-tall';
dialogOptions.size = 'small';
}
var dlg = dialogHelper.createDialog(dialogOptions);

View file

@ -1,4 +1,4 @@
define(['browser', 'appStorage', 'apphost', 'loading', 'connectionManager', 'globalize', 'appRouter', 'dom', 'css!./multiselect'], function (browser, appStorage, appHost, loading, connectionManager, globalize, appRouter, dom) {
define(['browser', 'appStorage', 'apphost', 'loading', 'connectionManager', 'globalize', 'appRouter', 'dom', 'css!./multiSelect'], function (browser, appStorage, appHost, loading, connectionManager, globalize, appRouter, dom) {
'use strict';
var selectedItems = [];
@ -129,7 +129,7 @@ define(['browser', 'appStorage', 'apphost', 'loading', 'connectionManager', 'glo
html += '<button is="paper-icon-button-light" class="btnCloseSelectionPanel autoSize"><span class="material-icons close"></span></button>';
html += '<h1 class="itemSelectionCount"></h1>';
const moreIcon = 'more_horiz';
const moreIcon = 'more_vert';
html += '<button is="paper-icon-button-light" class="btnSelectionPanelOptions autoSize" style="margin-left:auto;"><span class="material-icons ' + moreIcon + '"></span></button>';
selectionCommandsPanel.innerHTML = html;

View file

@ -244,7 +244,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader',
return new Promise(function (resolve, reject) {
require(['appFooter-shared', 'itemShortcuts', 'css!./nowplayingbar.css', 'emby-slider'], function (appfooter, itemShortcuts) {
require(['appFooter-shared', 'itemShortcuts', 'css!./nowPlayingBar.css', 'emby-slider'], function (appfooter, itemShortcuts) {
var parentContainer = appfooter.element;
nowPlayingBarElement = parentContainer.querySelector('.nowPlayingBar');

View file

@ -314,13 +314,11 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
if (codecProfile.Type === 'Audio') {
(codecProfile.Conditions || []).map(function (condition) {
if (condition.Condition === 'LessThanEqual' && condition.Property === 'AudioBitDepth') {
maxAudioBitDepth = condition.Value;
}
if (condition.Condition === 'LessThanEqual' && condition.Property === 'AudioSampleRate') {
maxAudioSampleRate = condition.Value;
}
if (condition.Condition === 'LessThanEqual' && condition.Property === 'AudioBitrate') {
maxAudioBitrate = condition.Value;
return maxAudioBitDepth = condition.Value;
} else if (condition.Condition === 'LessThanEqual' && condition.Property === 'AudioSampleRate') {
return maxAudioSampleRate = condition.Value;
} else if (condition.Condition === 'LessThanEqual' && condition.Property === 'AudioBitrate') {
return maxAudioBitrate = condition.Value;
}
});
}

View file

@ -7,11 +7,13 @@ define(['connectionManager', 'actionsheet', 'datetime', 'playbackManager', 'glob
return stream.Type === 'Video';
})[0];
var videoWidth = videoStream ? videoStream.Width : null;
var videoHeight = videoStream ? videoStream.Height : null;
var options = qualityoptions.getVideoQualityOptions({
currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player),
isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player),
videoWidth: videoWidth,
videoHeight: videoHeight,
enableAuto: true
});
@ -91,11 +93,13 @@ define(['connectionManager', 'actionsheet', 'datetime', 'playbackManager', 'glob
})[0];
var videoWidth = videoStream ? videoStream.Width : null;
var videoHeight = videoStream ? videoStream.Height : null;
var options = qualityoptions.getVideoQualityOptions({
currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player),
isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player),
videoWidth: videoWidth,
videoHeight: videoHeight,
enableAuto: true
});

View file

@ -204,6 +204,9 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality
fillChromecastQuality(context.querySelector('.selectChromecastVideoQuality'));
var selectChromecastVersion = context.querySelector('.selectChromecastVersion');
selectChromecastVersion.value = userSettings.chromecastVersion();
var selectSkipForwardLength = context.querySelector('.selectSkipForwardLength');
fillSkipLengths(selectSkipForwardLength);
selectSkipForwardLength.value = userSettings.skipForwardLength();
@ -234,6 +237,7 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality
userSettingsInstance.enableCinemaMode(context.querySelector('.chkEnableCinemaMode').checked);
userSettingsInstance.enableNextVideoInfoOverlay(context.querySelector('.chkEnableNextVideoOverlay').checked);
userSettingsInstance.chromecastVersion(context.querySelector('.selectChromecastVersion').value);
userSettingsInstance.skipForwardLength(context.querySelector('.selectSkipForwardLength').value);
userSettingsInstance.skipBackLength(context.querySelector('.selectSkipBackLength').value);
@ -285,7 +289,7 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality
function embed(options, self) {
require(['text!./playbacksettings.template.html'], function (template) {
require(['text!./playbackSettings.template.html'], function (template) {
options.element.innerHTML = globalize.translateDocument(template, 'core');

View file

@ -1,12 +1,13 @@
<form style="margin: 0 auto;">
<div class="verticalSection verticalSection-extrabottompadding">
<h2 class="sectionTitle">
${HeaderAudioSettings}
</h2>
<div class="selectContainer">
<select is="emby-select" id="selectAudioLanguage" label="${LabelAudioLanguagePreference}"></select>
</div>
<label class="checkboxContainer">
<input type="checkbox" is="emby-checkbox" class="chkPlayDefaultAudioTrack" />
<span>${LabelPlayDefaultAudioTrack}</span>
@ -18,12 +19,15 @@
<h2 class="sectionTitle">
${HeaderVideoQuality}
</h2>
<div class="selectContainer fldVideoInNetworkQuality hide">
<select is="emby-select" class="selectVideoInNetworkQuality" label="${LabelHomeNetworkQuality}"></select>
</div>
<div class="selectContainer fldVideoInternetQuality hide">
<select is="emby-select" class="selectVideoInternetQuality" label="${LabelInternetQuality}"></select>
</div>
<div class="selectContainer fldChromecastQuality hide">
<select is="emby-select" class="selectChromecastVideoQuality" label="${LabelMaxChromecastBitrate}"></select>
</div>
@ -33,6 +37,7 @@
<h2>
${HeaderMusicQuality}
</h2>
<div class="selectContainer">
<select is="emby-select" class="selectMusicInternetQuality" label="${LabelInternetQuality}"></select>
</div>
@ -43,6 +48,7 @@
<h2 class="sectionTitle">
${TabAdvanced}
</h2>
<div class="checkboxContainer checkboxContainer-withDescription cinemaModeOptions">
<label>
<input type="checkbox" is="emby-checkbox" class="chkEnableCinemaMode" />
@ -50,12 +56,14 @@
</label>
<div class="fieldDescription checkboxFieldDescription">${CinemaModeConfigurationHelp}</div>
</div>
<div class="checkboxContainer fldEpisodeAutoPlay hide">
<label>
<input type="checkbox" is="emby-checkbox" class="chkEpisodeAutoPlay" />
<span>${PlayNextEpisodeAutomatically}</span>
</label>
</div>
<div class="checkboxContainer checkboxContainer-withDescription fldEnableNextVideoOverlay hide">
<label>
<input type="checkbox" is="emby-checkbox" class="chkEnableNextVideoOverlay" />
@ -74,6 +82,13 @@
</div>
</div>
<div class="selectContainer">
<select is="emby-select" class="selectChromecastVersion" label="${LabelChromecastVersion}">
<option value="stable">${LabelStable}</option>
<option value="nightly">${LabelNightly}</option>
</select>
</div>
<div class="selectContainer">
<select is="emby-select" class="selectSkipForwardLength" label="${LabelSkipForwardLength}"></select>
</div>

View file

@ -1,23 +1,20 @@
define(['actionsheet', 'datetime', 'playbackManager', 'globalize', 'appSettings'], function (actionsheet, datetime, playbackManager, globalize, appSettings) {
'use strict';
import actionsheet from 'actionsheet';
import datetime from 'datetime';
import playbackManager from 'playbackManager';
import globalize from 'globalize';
function show(options) {
export function show(options) {
var item = options.item;
var itemType = item.Type;
var isFolder = item.IsFolder;
var itemId = item.Id;
var channelId = item.ChannelId;
var serverId = item.ServerId;
var resumePositionTicks = item.UserData ? item.UserData.PlaybackPositionTicks : null;
var playableItemId = itemType === 'Program' ? channelId : itemId;
var playableItemId = item.Type === 'Program' ? item.ChannelId : item.Id;
if (!resumePositionTicks || isFolder) {
if (!resumePositionTicks || item.IsFolder) {
playbackManager.play({
ids: [playableItemId],
serverId: serverId
serverId: item.ServerId
});
return;
}
@ -45,14 +42,14 @@ define(['actionsheet', 'datetime', 'playbackManager', 'globalize', 'appSettings'
case 'play':
playbackManager.play({
ids: [playableItemId],
serverId: serverId
serverId: item.ServerId
});
break;
case 'resume':
playbackManager.play({
ids: [playableItemId],
startPositionTicks: resumePositionTicks,
serverId: serverId
serverId: item.ServerId
});
break;
case 'queue':
@ -69,7 +66,6 @@ define(['actionsheet', 'datetime', 'playbackManager', 'globalize', 'appSettings'
});
}
return {
export default {
show: show
};
});

View file

@ -1,10 +1,10 @@
define(['events'], function (events) {
define(['events', 'globalize'], function (events, globalize) {
'use strict';
// TODO: replace with each plugin version
var cacheParam = new Date().getTime();
function loadStrings(plugin, globalize) {
function loadStrings(plugin) {
var strings = plugin.getTranslations ? plugin.getTranslations() : [];
return globalize.loadStrings({
name: plugin.id || plugin.packageName,
@ -25,48 +25,11 @@ define(['events'], function (events) {
this.pluginsList = [];
}
PluginManager.prototype.loadPlugin = function (url) {
PluginManager.prototype.loadPlugin = function(pluginSpec) {
console.debug('Loading plugin: ' + url);
var instance = this;
return new Promise(function (resolve, reject) {
require([url, 'globalize', 'appRouter'], function (pluginFactory, globalize, appRouter) {
var plugin = new pluginFactory();
// See if it's already installed
var existing = instance.pluginsList.filter(function (p) {
return p.id === plugin.id;
})[0];
if (existing) {
resolve(url);
return;
}
plugin.installUrl = url;
var urlLower = url.toLowerCase();
if (urlLower.indexOf('http:') === -1 && urlLower.indexOf('https:') === -1 && urlLower.indexOf('file:') === -1) {
if (url.indexOf(appRouter.baseUrl()) !== 0) {
url = appRouter.baseUrl() + '/' + url;
}
}
var separatorIndex = Math.max(url.lastIndexOf('/'), url.lastIndexOf('\\'));
plugin.baseUrl = url.substring(0, separatorIndex);
var paths = {};
paths[plugin.id] = plugin.baseUrl;
requirejs.config({
waitSeconds: 0,
paths: paths
});
function registerPlugin(plugin) {
instance.register(plugin);
if (plugin.getRoutes) {
@ -78,15 +41,62 @@ define(['events'], function (events) {
if (plugin.type === 'skin') {
// translations won't be loaded for skins until needed
resolve(plugin);
return Promise.resolve(plugin);
} else {
loadStrings(plugin, globalize).then(function () {
return new Promise((resolve, reject) => {
loadStrings(plugin)
.then(function () {
resolve(plugin);
}, reject);
})
.catch(reject);
});
}
}
if (typeof pluginSpec === 'string') {
console.debug('Loading plugin (via deprecated requirejs method): ' + pluginSpec);
return new Promise(function (resolve, reject) {
require([pluginSpec], (pluginFactory) => {
var plugin = new pluginFactory();
// See if it's already installed
var existing = instance.pluginsList.filter(function (p) {
return p.id === plugin.id;
})[0];
if (existing) {
resolve(pluginSpec);
}
plugin.installUrl = pluginSpec;
var separatorIndex = Math.max(pluginSpec.lastIndexOf('/'), pluginSpec.lastIndexOf('\\'));
plugin.baseUrl = pluginSpec.substring(0, separatorIndex);
var paths = {};
paths[plugin.id] = plugin.baseUrl;
requirejs.config({
waitSeconds: 0,
paths: paths
});
registerPlugin(plugin).then(resolve).catch(reject);
});
});
} else if (pluginSpec.then) {
return pluginSpec.then(pluginBuilder => {
return pluginBuilder();
}).then(plugin => {
console.debug(`Plugin loaded: ${plugin.id}`);
return registerPlugin(plugin);
});
} else {
const err = new Error('Plugins have to be a Promise that resolves to a plugin builder function or a requirejs urls (deprecated)');
console.error(err);
return Promise.reject(err);
}
};
// In lieu of automatic discovery, plugins will register dynamic objects

View file

@ -5,6 +5,13 @@ define(['globalize'], function (globalize) {
var maxStreamingBitrate = options.currentMaxBitrate;
var videoWidth = options.videoWidth;
var videoHeight = options.videoHeight;
// If the aspect ratio is less than 16/9 (1.77), set the width as if it were pillarboxed.
// 4:3 1440x1080 -> 1920x1080
if (videoWidth / videoHeight < 16 / 9) {
videoWidth = videoHeight * (16 / 9);
}
var maxAllowedWidth = videoWidth || 4096;
//var maxAllowedHeight = videoHeight || 2304;

View file

@ -1,4 +1,4 @@
define(['apphost', 'userSettings', 'browser', 'events', 'pluginManager', 'backdrop', 'globalize', 'require', 'appSettings'], function (appHost, userSettings, browser, events, pluginManager, backdrop, globalize, require, appSettings) {
define(['apphost', 'userSettings', 'browser', 'events', 'backdrop', 'globalize', 'require', 'appSettings'], function (appHost, userSettings, browser, events, backdrop, globalize, require, appSettings) {
'use strict';
var themeStyleElement;
@ -137,6 +137,8 @@ define(['apphost', 'userSettings', 'browser', 'events', 'pluginManager', 'backdr
function onViewBeforeShow(e) {
if (e.detail && e.detail.type === 'video-osd') {
// This removes the space that the scrollbar takes while playing a video
document.body.classList.remove('force-scroll');
return;
}
@ -155,6 +157,9 @@ define(['apphost', 'userSettings', 'browser', 'events', 'pluginManager', 'backdr
}
}
}
// This keeps the scrollbar always present in all pages, so we avoid clipping while switching between pages
// that need the scrollbar and pages that don't.
document.body.classList.add('force-scroll');
}
document.addEventListener('viewshow', onViewBeforeShow);

View file

@ -438,6 +438,9 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
inputManager.off(window, onInputCommand);
document.removeEventListener((window.PointerEvent ? 'pointermove' : 'mousemove'), onPointerMove);
// Shows page scrollbar
document.body.classList.remove('hide-scroll');
document.body.classList.add('force-scroll');
}
/**
@ -603,6 +606,9 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
*/
self.show = function () {
createElements(options);
// Hides page scrollbar
document.body.classList.remove('force-scroll');
document.body.classList.add('hide-scroll');
};
/**

View file

@ -232,11 +232,7 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
html += '</div>';
}
html += '<h2>' + provider + '</h2>';
if (layoutManager.tv) {
html += '<div>';
} else {
html += '<div>';
}
lastProvider = provider;
}

View file

@ -2,8 +2,7 @@
<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><span class="material-icons arrow_back"></span></button>
<h3 class="formDialogHeaderTitle">${Subtitles}</h3>
<a is="emby-linkbutton" data-autohide="true" class="button-link btnHelp flex align-items-center" href="https://docs.jellyfin.org/general/server/media/subtitles.html" target="_blank" style="margin-left:auto;margin-right:.5em;padding:.25em;" title="${Help}"><span class="material-icons info"></span><span style="margin-left:.25em;">${Help}</span></a>
<a is="emby-linkbutton" rel="noopener noreferrer" data-autohide="true" class="button-link btnHelp flex align-items-center" href="https://docs.jellyfin.org/general/server/media/subtitles.html" target="_blank" style="margin-left:auto;margin-right:.5em;padding:.25em;" title="${Help}"><span class="material-icons info"></span><span style="margin-left:.25em;">${Help}</span></a>
</div>
<div class="formDialogContent smoothScrollY">
<div class="dialogContentInner dialog-content-centered">

View file

@ -1,7 +1,7 @@
<div class="verticalSection">
<div class="sectionTitleContainer flex align-items-center">
<h1 class="sectionTitle">Schedules Direct</h1>
<a is="emby-linkbutton" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/live-tv/setup-guide.html#adding-guide-data">${Help}</a>
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/live-tv/setup-guide.html#adding-guide-data">${Help}</a>
</div>
<p class="createAccountHelp"></p>
</div>

View file

@ -1,7 +1,7 @@
<div class="verticalSection">
<div class="sectionTitleContainer flex align-items-center">
<h1 class="sectionTitle">Xml TV</h1>
<a is="emby-linkbutton" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/live-tv/setup-guide.html#adding-guide-data">${Help}</a>
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/live-tv/setup-guide.html#adding-guide-data">${Help}</a>
</div>
</div>

View file

@ -63,8 +63,8 @@
height: auto;
width: 100%;
box-shadow: 0 0.0725em 0.29em 0 rgba(0, 0, 0, 0.37);
border: 0;
user-drag: none;
border: 0;
user-select: none;
-moz-user-select: none;
-webkit-user-drag: none;

View file

@ -179,12 +179,6 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa
view.querySelector('#operatingSystem').innerHTML = globalize.translate('DashboardOperatingSystem', systemInfo.OperatingSystem);
view.querySelector('#architecture').innerHTML = globalize.translate('DashboardArchitecture', systemInfo.SystemArchitecture);
if (systemInfo.CanSelfRestart) {
view.querySelector('#btnRestartServer').classList.remove('hide');
} else {
view.querySelector('#btnRestartServer').classList.add('hide');
}
view.querySelector('#cachePath').innerHTML = systemInfo.CachePath;
view.querySelector('#logPath').innerHTML = systemInfo.LogPath;
view.querySelector('#transcodePath').innerHTML = systemInfo.TranscodingTempPath;

View file

@ -89,7 +89,7 @@ define(['loading', 'dom', 'libraryMenu', 'globalize', 'scripts/imagehelper', 'da
if (canEdit || canDelete(device.Id)) {
deviceHtml += '<div style="text-align:right; float:right;padding-top:5px;">';
deviceHtml += '<button type="button" is="paper-icon-button-light" data-id="' + device.Id + '" title="' + globalize.translate('Menu') + '" class="btnDeviceMenu"><span class="material-icons more_horiz"></span></button>';
deviceHtml += '<button type="button" is="paper-icon-button-light" data-id="' + device.Id + '" title="' + globalize.translate('Menu') + '" class="btnDeviceMenu"><span class="material-icons more_vert"></span></button>';
deviceHtml += '</div>';
}

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