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

merge branch master into books

This commit is contained in:
dkanada 2020-05-21 11:58:25 +09:00
commit 09e051bfab
78 changed files with 714 additions and 664 deletions

View file

@ -197,7 +197,9 @@ export function show(options) {
menuItemClass += ' actionsheet-xlargeFont';
}
for (let [i, item] of options.items.entries()) {
// '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) {

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

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

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

@ -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

@ -253,7 +253,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

@ -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

@ -109,6 +109,8 @@ 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) {

View file

@ -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

@ -87,7 +87,7 @@ define(['globalize', 'dom', 'dialogHelper', 'emby-checkbox', 'emby-select', 'emb
xhr.onload = function (e) {
var template = this.response;
var dlg = dialogHelper.createDialog({
size: 'medium-tall',
size: 'small',
removeOnClose: true,
scrollY: false
});

View file

@ -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

@ -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

@ -187,7 +187,7 @@ define(['loading', 'dialogHelper', 'dom', 'jQuery', 'components/libraryoptionsed
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

@ -204,7 +204,7 @@ define(['jQuery', 'loading', 'dialogHelper', 'dom', 'components/libraryoptionsed
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

@ -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);
@ -1077,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);

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_vert"></span>
<button is="paper-icon-button-light" class="btnCancel btnClose autoSize" tabindex="-1">
<span class="material-icons close"></span>
</button>
</div>
</div>
@ -57,11 +57,13 @@
<div id="fldAlbum" class="hide inputContainer">
<input is="emby-input" id="txtAlbum" type="text" label="${LabelAlbum}" />
</div>
<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 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}" />
@ -129,24 +131,28 @@
<div id="fldSeriesRuntime" class="inputContainer hide">
<input is="emby-input" id="txtSeriesRuntime" type="number" label="${LabelRuntimeMinutes}" />
</div>
<div id="fldOfficialRating" class="selectContainer hide">
<select is="emby-select" id="selectOfficialRating" label="${LabelParentalRating}"></select>
<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 id="fldCustomRating" class="selectContainer hide">
<select is="emby-select" id="selectCustomRating" label="${LabelCustomRating}"></select>
</div>
<div id="fldOriginalAspectRatio" class="inputContainer hide">
<input is="emby-input" id="txtOriginalAspectRatio" type="text" label="${LabelOriginalAspectRatio}" />
</div>
<div id="fld3dFormat" class="selectContainer hide">
<select is="emby-select" id="select3dFormat" label="${Label3DFormat}">
<option value=""></option>
<option value="HalfSideBySide">HSBS</option>
<option value="HalfTopAndBottom">HTAB</option>
<option value="FullSideBySide">FSBS</option>
<option value="FullTopAndBottom">FTAB</option>
<option value="MVC">MVC</option>
</select>
<div class="inlineForm">
<div id="fldOriginalAspectRatio" class="inputContainer hide">
<input is="emby-input" id="txtOriginalAspectRatio" type="text" label="${LabelOriginalAspectRatio}" />
</div>
<div id="fld3dFormat" class="selectContainer hide">
<select is="emby-select" id="select3dFormat" label="${Label3DFormat}">
<option value=""></option>
<option value="HalfSideBySide">HSBS</option>
<option value="HalfTopAndBottom">HTAB</option>
<option value="FullSideBySide">FSBS</option>
<option value="FullTopAndBottom">FTAB</option>
<option value="MVC">MVC</option>
</select>
</div>
</div>
<div id="fldDisplayOrder" class="fldDisplaySetting selectContainer hide">
@ -160,14 +166,16 @@
<h2>
${HeaderSpecialEpisodeInfo}
</h2>
<div class="inputContainer">
<input is="emby-input" id="txtAirsBeforeSeason" type="number" pattern="[0-9]*" label="${LabelAirsBeforeSeason}" />
</div>
<div class="inputContainer">
<input is="emby-input" id="txtAirsAfterSeason" type="number" pattern="[0-9]*" label="${LabelAirsAfterSeason}" />
</div>
<div class="inputContainer">
<input is="emby-input" id="txtAirsBeforeEpisode" type="number" pattern="[0-9]*" label="${LabelAirsBeforeEpisode}" />
<div class="inlineForm">
<div class="inputContainer">
<input is="emby-input" id="txtAirsBeforeSeason" type="number" pattern="[0-9]*" label="${LabelAirsBeforeSeason}" />
</div>
<div class="inputContainer">
<input is="emby-input" id="txtAirsAfterSeason" type="number" pattern="[0-9]*" label="${LabelAirsAfterSeason}" />
</div>
<div class="inputContainer">
<input is="emby-input" id="txtAirsBeforeEpisode" type="number" pattern="[0-9]*" label="${LabelAirsBeforeEpisode}" />
</div>
</div>
</div>
@ -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

@ -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,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,68 +25,78 @@ 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) {
function registerPlugin(plugin) {
instance.register(plugin);
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
if (plugin.getRoutes) {
plugin.getRoutes().forEach(function (route) {
definePluginRoute(instance, route, plugin);
});
}
instance.register(plugin);
if (plugin.type === 'skin') {
if (plugin.getRoutes) {
plugin.getRoutes().forEach(function (route) {
definePluginRoute(instance, route, plugin);
// translations won't be loaded for skins until needed
return Promise.resolve(plugin);
} else {
return new Promise((resolve, reject) => {
loadStrings(plugin)
.then(function () {
resolve(plugin);
})
.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
});
}
if (plugin.type === 'skin') {
// translations won't be loaded for skins until needed
resolve(plugin);
} else {
loadStrings(plugin, globalize).then(function () {
resolve(plugin);
}, reject);
}
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

@ -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');
};
/**