diff --git a/.ci/azure-pipelines-build.yml b/.ci/azure-pipelines-build.yml
index 026afe76a6..128fe54605 100644
--- a/.ci/azure-pipelines-build.yml
+++ b/.ci/azure-pipelines-build.yml
@@ -8,6 +8,8 @@ jobs:
BuildConfiguration: development
Production:
BuildConfiguration: production
+ Standalone:
+ BuildConfiguration: standalone
pool:
vmImage: 'ubuntu-latest'
@@ -37,6 +39,10 @@ jobs:
displayName: 'Build Production'
condition: eq(variables['BuildConfiguration'], 'production')
+ - script: 'yarn build:standalone'
+ displayName: 'Build Standalone'
+ condition: eq(variables['BuildConfiguration'], 'standalone')
+
- script: 'test -d dist'
displayName: 'Check Build'
diff --git a/.eslintrc.js b/.eslintrc.js
index ab53f0f03d..baf6d0e084 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -193,4 +193,4 @@ module.exports = {
}
}
]
-}
+};
diff --git a/deployment/build.portable b/deployment/build.portable
index 7161ae6c2d..873c145fab 100755
--- a/deployment/build.portable
+++ b/deployment/build.portable
@@ -15,8 +15,8 @@ fi
# build archives
npx yarn install
-mv dist jellyfin-web-${version}
-tar -czf jellyfin-web-${version}-portable.tar.gz jellyfin-web-${version}
+mv dist jellyfin-web_${version}
+tar -czf jellyfin-web_${version}_portable.tar.gz jellyfin-web_${version}
rm -rf dist
# move the artifacts
diff --git a/gulpfile.js b/gulpfile.js
index 03826e8b6e..84f4558e6a 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -16,6 +16,7 @@ const stream = require('webpack-stream');
const inject = require('gulp-inject');
const postcss = require('gulp-postcss');
const sass = require('gulp-sass');
+const gulpif = require('gulp-if');
const lazypipe = require('lazypipe');
sass.compiler = require('node-sass');
@@ -67,7 +68,7 @@ function serve() {
}
});
- watch(options.apploader.query, apploader());
+ watch(options.apploader.query, apploader(true));
watch('src/bundle.js', webpack);
@@ -130,12 +131,18 @@ function javascript(query) {
.pipe(browserSync.stream());
}
-function apploader() {
- return src(options.apploader.query, { base: './src/' })
- .pipe(concat('scripts/apploader.js'))
- .pipe(pipelineJavascript())
- .pipe(dest('dist/'))
- .pipe(browserSync.stream());
+function apploader(standalone) {
+ function task() {
+ return src(options.apploader.query, { base: './src/' })
+ .pipe(gulpif(standalone, concat('scripts/apploader.js')))
+ .pipe(pipelineJavascript())
+ .pipe(dest('dist/'))
+ .pipe(browserSync.stream());
+ }
+
+ task.displayName = 'apploader';
+
+ return task;
}
function webpack() {
@@ -183,5 +190,10 @@ function injectBundle() {
.pipe(browserSync.stream());
}
-exports.default = series(clean, parallel(javascript, apploader, webpack, css, html, images, copy), injectBundle);
-exports.serve = series(exports.default, serve);
+function build(standalone) {
+ return series(clean, parallel(javascript, apploader(standalone), webpack, css, html, images, copy));
+}
+
+exports.default = series(build(false), injectBundle);
+exports.standalone = series(build(true), injectBundle);
+exports.serve = series(exports.standalone, serve);
diff --git a/package.json b/package.json
index 675e7a6b35..81c627f085 100644
--- a/package.json
+++ b/package.json
@@ -5,13 +5,13 @@
"repository": "https://github.com/jellyfin/jellyfin-web",
"license": "GPL-2.0-or-later",
"devDependencies": {
- "@babel/core": "^7.10.2",
+ "@babel/core": "^7.10.3",
"@babel/plugin-proposal-class-properties": "^7.10.1",
"@babel/plugin-proposal-private-methods": "^7.10.1",
"@babel/plugin-transform-modules-amd": "^7.9.6",
"@babel/polyfill": "^7.8.7",
- "@babel/preset-env": "^7.10.2",
- "autoprefixer": "^9.8.0",
+ "@babel/preset-env": "^7.10.3",
+ "autoprefixer": "^9.8.2",
"babel-eslint": "^11.0.0-beta.2",
"babel-loader": "^8.0.6",
"browser-sync": "^2.26.7",
@@ -44,7 +44,7 @@
"postcss-loader": "^3.0.0",
"postcss-preset-env": "^6.7.0",
"style-loader": "^1.1.3",
- "stylelint": "^13.6.0",
+ "stylelint": "^13.6.1",
"stylelint-config-rational-order": "^0.1.2",
"stylelint-no-browser-hacks": "^1.2.1",
"stylelint-order": "^4.1.0",
@@ -60,7 +60,7 @@
"date-fns": "^2.14.0",
"document-register-element": "^1.14.3",
"epubjs": "^0.3.85",
- "fast-text-encoding": "^1.0.1",
+ "fast-text-encoding": "^1.0.3",
"flv.js": "^1.5.0",
"headroom.js": "^0.11.0",
"hls.js": "^0.13.1",
@@ -69,7 +69,7 @@
"jellyfin-apiclient": "^1.2.2",
"jellyfin-noto": "https://github.com/jellyfin/jellyfin-noto",
"jquery": "^3.5.1",
- "jstree": "^3.3.7",
+ "jstree": "^3.3.10",
"libass-wasm": "https://github.com/jellyfin/JavascriptSubtitlesOctopus#4.0.0-jf-smarttv",
"material-design-icons-iconfont": "^5.0.1",
"native-promise-only": "^0.8.0-a",
@@ -77,9 +77,9 @@
"query-string": "^6.13.1",
"resize-observer-polyfill": "^1.5.1",
"screenfull": "^5.0.2",
- "shaka-player": "^2.5.13",
+ "shaka-player": "^3.0.1",
"sortablejs": "^1.10.2",
- "swiper": "^5.4.3",
+ "swiper": "^5.4.5",
"webcomponents.js": "^0.7.24",
"whatwg-fetch": "^3.0.0"
},
@@ -92,14 +92,25 @@
"test": [
"src/components/accessSchedule/accessSchedule.js",
"src/components/actionSheet/actionSheet.js",
+ "src/components/alphaPicker/alphaPicker.js",
"src/components/autoFocuser.js",
"src/components/cardbuilder/cardBuilder.js",
"src/components/cardbuilder/chaptercardbuilder.js",
"src/components/cardbuilder/peoplecardbuilder.js",
"src/components/directorybrowser/directorybrowser.js",
+ "src/components/collectionEditor/collectionEditor.js",
+ "src/components/dialog/dialog.js",
+ "src/components/dialogHelper/dialogHelper.js",
+ "src/components/channelMapper/channelMapper.js",
"src/components/images/imageLoader.js",
+ "src/components/imageUploader/imageUploader.js",
"src/components/indicators/indicators.js",
+ "src/components/itemidentifier/itemidentifier.js",
+ "src/components/itemMediaInfo/itemMediaInfo.js",
"src/components/lazyLoader/lazyLoaderIntersectionObserver.js",
+ "src/components/mediaLibraryCreator/mediaLibraryCreator.js",
+ "src/components/mediaLibraryEditor/mediaLibraryEditor.js",
+ "src/components/listview/listview.js",
"src/components/playback/brightnessosd.js",
"src/components/playback/mediasession.js",
"src/components/playback/nowplayinghelper.js",
@@ -109,14 +120,23 @@
"src/components/playback/playmethodhelper.js",
"src/components/playback/remotecontrolautoplay.js",
"src/components/playback/volumeosd.js",
+ "src/components/playlisteditor/playlisteditor.js",
+ "src/components/groupedcards.js",
+ "src/components/htmlMediaHelper.js",
"src/components/playmenu.js",
"src/components/sanatizefilename.js",
"src/components/scrollManager.js",
+ "src/components/settingshelper.js",
+ "src/components/subtitlesettings/subtitlesettings.js",
+ "src/components/subtitlesettings/subtitleappearancehelper.js",
+ "src/components/shortcuts.js",
"src/components/syncPlay/groupSelectionMenu.js",
"src/components/syncPlay/playbackPermissionManager.js",
"src/components/syncPlay/syncPlayManager.js",
"src/components/syncPlay/timeSyncManager.js",
"src/controllers/dashboard/logs.js",
+ "src/controllers/user/subtitles.js",
+ "src/controllers/dashboard/plugins/repositories.js",
"src/plugins/bookPlayer/plugin.js",
"src/plugins/bookPlayer/tableOfContents.js",
"src/plugins/photoPlayer/plugin.js",
@@ -133,6 +153,7 @@
"src/scripts/keyboardNavigation.js",
"src/scripts/settings/appSettings.js",
"src/scripts/settings/userSettings.js",
+ "src/scripts/themeLoader.js",
"src/scripts/settings/webSettings.js"
],
"plugins": [
@@ -163,6 +184,7 @@
"prepare": "gulp --production",
"build:development": "gulp --development",
"build:production": "gulp --production",
+ "build:standalone": "gulp standalone --development",
"lint": "eslint \".\"",
"stylelint": "stylelint \"src/**/*.css\""
}
diff --git a/src/assets/css/site.css b/src/assets/css/site.css
index d489f77f01..9fbd8a4fca 100644
--- a/src/assets/css/site.css
+++ b/src/assets/css/site.css
@@ -121,6 +121,11 @@ div[data-role=page] {
transform: translateY(-100%);
}
+.drawerContent {
+ /* make sure the bottom of the drawer is visible when music is playing */
+ padding-bottom: 4em;
+}
+
.force-scroll {
overflow-y: scroll;
}
diff --git a/src/components/alert.js b/src/components/alert.js
index 8a37ac1845..97b580f8f6 100644
--- a/src/components/alert.js
+++ b/src/components/alert.js
@@ -31,7 +31,7 @@ define(['browser', 'dialog', 'globalize'], function (browser, dialog, globalize)
options.buttons = items;
- return dialog(options).then(function (result) {
+ return dialog.show(options).then(function (result) {
if (result === 'ok') {
return Promise.resolve();
}
diff --git a/src/components/alphaPicker/alphaPicker.js b/src/components/alphaPicker/alphaPicker.js
index 79f74879e5..bd11afc7f1 100644
--- a/src/components/alphaPicker/alphaPicker.js
+++ b/src/components/alphaPicker/alphaPicker.js
@@ -1,11 +1,22 @@
-define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-button-light', 'material-icons'], function (focusManager, layoutManager, dom) {
- 'use strict';
+/* eslint-disable indent */
- var selectedButtonClass = 'alphaPickerButton-selected';
+/**
+ * Module alphaPicker.
+ * @module components/alphaPicker/alphaPicker
+ */
+
+import focusManager from 'focusManager';
+import layoutManager from 'layoutManager';
+import dom from 'dom';
+import 'css!./style.css';
+import 'paper-icon-button-light';
+import 'material-icons';
+
+ const selectedButtonClass = 'alphaPickerButton-selected';
function focus() {
- var scope = this;
- var selected = scope.querySelector('.' + selectedButtonClass);
+ const scope = this;
+ const selected = scope.querySelector(`.${selectedButtonClass}`);
if (selected) {
focusManager.focus(selected);
@@ -16,7 +27,7 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
function getAlphaPickerButtonClassName(vertical) {
- var alphaPickerButtonClassName = 'alphaPickerButton';
+ let alphaPickerButtonClassName = 'alphaPickerButton';
if (layoutManager.tv) {
alphaPickerButtonClassName += ' alphaPickerButton-tv';
@@ -30,12 +41,12 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
}
function getLetterButton(l, vertical) {
- return '';
+ return ``;
}
function mapLetters(letters, vertical) {
- return letters.map(function (l) {
+ return letters.map(l => {
return getLetterButton(l, vertical);
});
}
@@ -48,26 +59,26 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
element.classList.add('alphaPicker-tv');
}
- var vertical = element.classList.contains('alphaPicker-vertical');
+ const vertical = element.classList.contains('alphaPicker-vertical');
if (!vertical) {
element.classList.add('focuscontainer-x');
}
- var html = '';
- var letters;
+ let html = '';
+ let letters;
- var alphaPickerButtonClassName = getAlphaPickerButtonClassName(vertical);
+ const alphaPickerButtonClassName = getAlphaPickerButtonClassName(vertical);
- var rowClassName = 'alphaPickerRow';
+ let rowClassName = 'alphaPickerRow';
if (vertical) {
rowClassName += ' alphaPickerRow-vertical';
}
- html += '
';
+ html += `
`;
if (options.mode === 'keyboard') {
- html += '';
+ html += ``;
} else {
letters = ['#'];
html += mapLetters(letters, vertical).join('');
@@ -77,11 +88,11 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
html += mapLetters(letters, vertical).join('');
if (options.mode === 'keyboard') {
- html += '';
+ html += ``;
html += '
';
letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
- html += '
';
+ html += `
`;
html += '
';
html += mapLetters(letters, vertical).join('');
html += '
';
@@ -95,227 +106,228 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
element.focus = focus;
}
- function AlphaPicker(options) {
+ export class AlphaPicker {
+ constructor(options) {
- var self = this;
- this.options = options;
+ this.options = options;
- var element = options.element;
- var itemsContainer = options.itemsContainer;
- var itemClass = options.itemClass;
+ const element = options.element;
+ const itemsContainer = options.itemsContainer;
+ const itemClass = options.itemClass;
- var itemFocusValue;
- var itemFocusTimeout;
+ let itemFocusValue;
+ let itemFocusTimeout;
- function onItemFocusTimeout() {
- itemFocusTimeout = null;
- self.value(itemFocusValue);
- }
-
- var alphaFocusedElement;
- var alphaFocusTimeout;
-
- function onAlphaFocusTimeout() {
-
- alphaFocusTimeout = null;
-
- if (document.activeElement === alphaFocusedElement) {
- var value = alphaFocusedElement.getAttribute('data-value');
- self.value(value, true);
+ function onItemFocusTimeout() {
+ itemFocusTimeout = null;
+ this.value(itemFocusValue);
}
- }
- function onAlphaPickerInKeyboardModeClick(e) {
+ let alphaFocusedElement;
+ let alphaFocusTimeout;
- var alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton');
+ function onAlphaFocusTimeout() {
- if (alphaPickerButton) {
- var value = alphaPickerButton.getAttribute('data-value');
-
- element.dispatchEvent(new CustomEvent('alphavalueclicked', {
- cancelable: false,
- detail: {
- value: value
- }
- }));
- }
- }
-
- function onAlphaPickerClick(e) {
-
- var alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton');
-
- if (alphaPickerButton) {
- var value = alphaPickerButton.getAttribute('data-value');
- if ((this._currentValue || '').toUpperCase() === value.toUpperCase()) {
- self.value(null, true);
- } else {
- self.value(value, true);
- }
- }
- }
-
- function onAlphaPickerFocusIn(e) {
-
- if (alphaFocusTimeout) {
- clearTimeout(alphaFocusTimeout);
alphaFocusTimeout = null;
+
+ if (document.activeElement === alphaFocusedElement) {
+ const value = alphaFocusedElement.getAttribute('data-value');
+ this.value(value, true);
+ }
}
- var alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton');
+ function onAlphaPickerInKeyboardModeClick(e) {
- if (alphaPickerButton) {
- alphaFocusedElement = alphaPickerButton;
- alphaFocusTimeout = setTimeout(onAlphaFocusTimeout, 600);
+ const alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton');
+
+ if (alphaPickerButton) {
+ const value = alphaPickerButton.getAttribute('data-value');
+
+ element.dispatchEvent(new CustomEvent('alphavalueclicked', {
+ cancelable: false,
+ detail: {
+ value
+ }
+ }));
+ }
}
- }
- function onItemsFocusIn(e) {
+ function onAlphaPickerClick(e) {
- var item = dom.parentWithClass(e.target, itemClass);
+ const alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton');
- if (item) {
- var prefix = item.getAttribute('data-prefix');
- if (prefix && prefix.length) {
-
- itemFocusValue = prefix[0];
- if (itemFocusTimeout) {
- clearTimeout(itemFocusTimeout);
+ if (alphaPickerButton) {
+ const value = alphaPickerButton.getAttribute('data-value');
+ if ((this._currentValue || '').toUpperCase() === value.toUpperCase()) {
+ this.value(null, true);
+ } else {
+ this.value(value, true);
}
- itemFocusTimeout = setTimeout(onItemFocusTimeout, 100);
}
}
- }
- self.enabled = function (enabled) {
+ function onAlphaPickerFocusIn(e) {
- if (enabled) {
-
- if (itemsContainer) {
- itemsContainer.addEventListener('focus', onItemsFocusIn, true);
+ if (alphaFocusTimeout) {
+ clearTimeout(alphaFocusTimeout);
+ alphaFocusTimeout = null;
}
- if (options.mode === 'keyboard') {
- element.addEventListener('click', onAlphaPickerInKeyboardModeClick);
- }
+ const alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton');
+
+ if (alphaPickerButton) {
+ alphaFocusedElement = alphaPickerButton;
+ alphaFocusTimeout = setTimeout(onAlphaFocusTimeout, 600);
+ }
+ }
+
+ function onItemsFocusIn(e) {
+
+ const item = dom.parentWithClass(e.target, itemClass);
+
+ if (item) {
+ const prefix = item.getAttribute('data-prefix');
+ if (prefix && prefix.length) {
+
+ itemFocusValue = prefix[0];
+ if (itemFocusTimeout) {
+ clearTimeout(itemFocusTimeout);
+ }
+ itemFocusTimeout = setTimeout(onItemFocusTimeout, 100);
+ }
+ }
+ }
+
+ this.enabled = function (enabled) {
+
+ if (enabled) {
+
+ if (itemsContainer) {
+ itemsContainer.addEventListener('focus', onItemsFocusIn, true);
+ }
+
+ if (options.mode === 'keyboard') {
+ element.addEventListener('click', onAlphaPickerInKeyboardModeClick);
+ }
+
+ if (options.valueChangeEvent !== 'click') {
+ element.addEventListener('focus', onAlphaPickerFocusIn, true);
+ } else {
+ element.addEventListener('click', onAlphaPickerClick.bind(this));
+ }
- if (options.valueChangeEvent !== 'click') {
- element.addEventListener('focus', onAlphaPickerFocusIn, true);
} else {
- element.addEventListener('click', onAlphaPickerClick.bind(this));
- }
- } else {
-
- if (itemsContainer) {
- itemsContainer.removeEventListener('focus', onItemsFocusIn, true);
- }
-
- element.removeEventListener('click', onAlphaPickerInKeyboardModeClick);
- element.removeEventListener('focus', onAlphaPickerFocusIn, true);
- element.removeEventListener('click', onAlphaPickerClick.bind(this));
- }
- };
-
- render(element, options);
-
- this.enabled(true);
- this.visible(true);
- }
-
- AlphaPicker.prototype.value = function (value, applyValue) {
-
- var element = this.options.element;
- var btn;
- var selected;
-
- if (value !== undefined) {
- if (value != null) {
-
- value = value.toUpperCase();
- this._currentValue = value;
-
- if (this.options.mode !== 'keyboard') {
- selected = element.querySelector('.' + selectedButtonClass);
-
- try {
- btn = element.querySelector('.alphaPickerButton[data-value=\'' + value + '\']');
- } catch (err) {
- console.error('error in querySelector: ' + err);
+ if (itemsContainer) {
+ itemsContainer.removeEventListener('focus', onItemsFocusIn, true);
}
- if (btn && btn !== selected) {
- btn.classList.add(selectedButtonClass);
+ element.removeEventListener('click', onAlphaPickerInKeyboardModeClick);
+ element.removeEventListener('focus', onAlphaPickerFocusIn, true);
+ element.removeEventListener('click', onAlphaPickerClick.bind(this));
+ }
+ };
+
+ render(element, options);
+
+ this.enabled(true);
+ this.visible(true);
+ }
+
+ value(value, applyValue) {
+
+ const element = this.options.element;
+ let btn;
+ let selected;
+
+ if (value !== undefined) {
+ if (value != null) {
+
+ value = value.toUpperCase();
+ this._currentValue = value;
+
+ if (this.options.mode !== 'keyboard') {
+ selected = element.querySelector(`.${selectedButtonClass}`);
+
+ try {
+ btn = element.querySelector(`.alphaPickerButton[data-value='${value}']`);
+ } catch (err) {
+ console.error('error in querySelector:', err);
+ }
+
+ if (btn && btn !== selected) {
+ btn.classList.add(selectedButtonClass);
+ }
+ if (selected && selected !== btn) {
+ selected.classList.remove(selectedButtonClass);
+ }
}
- if (selected && selected !== btn) {
+ } else {
+ this._currentValue = value;
+
+ selected = element.querySelector(`.${selectedButtonClass}`);
+ if (selected) {
selected.classList.remove(selectedButtonClass);
}
}
- } else {
- this._currentValue = value;
-
- selected = element.querySelector('.' + selectedButtonClass);
- if (selected) {
- selected.classList.remove(selectedButtonClass);
- }
}
+
+ if (applyValue) {
+ element.dispatchEvent(new CustomEvent('alphavaluechanged', {
+ cancelable: false,
+ detail: {
+ value
+ }
+ }));
+ }
+
+ return this._currentValue;
}
- if (applyValue) {
- element.dispatchEvent(new CustomEvent('alphavaluechanged', {
- cancelable: false,
- detail: {
- value: value
- }
- }));
+ on(name, fn) {
+ const element = this.options.element;
+ element.addEventListener(name, fn);
}
- return this._currentValue;
- };
-
- AlphaPicker.prototype.on = function (name, fn) {
- var element = this.options.element;
- element.addEventListener(name, fn);
- };
-
- AlphaPicker.prototype.off = function (name, fn) {
- var element = this.options.element;
- element.removeEventListener(name, fn);
- };
-
- AlphaPicker.prototype.visible = function (visible) {
-
- var element = this.options.element;
- element.style.visibility = visible ? 'visible' : 'hidden';
- };
-
- AlphaPicker.prototype.values = function () {
-
- var element = this.options.element;
- var elems = element.querySelectorAll('.alphaPickerButton');
- var values = [];
- for (var i = 0, length = elems.length; i < length; i++) {
-
- values.push(elems[i].getAttribute('data-value'));
-
+ off(name, fn) {
+ const element = this.options.element;
+ element.removeEventListener(name, fn);
}
- return values;
- };
+ visible(visible) {
- AlphaPicker.prototype.focus = function () {
+ const element = this.options.element;
+ element.style.visibility = visible ? 'visible' : 'hidden';
+ }
- var element = this.options.element;
- focusManager.autoFocus(element, true);
- };
+ values() {
- AlphaPicker.prototype.destroy = function () {
+ const element = this.options.element;
+ const elems = element.querySelectorAll('.alphaPickerButton');
+ const values = [];
+ for (let i = 0, length = elems.length; i < length; i++) {
- var element = this.options.element;
- this.enabled(false);
- element.classList.remove('focuscontainer-x');
- this.options = null;
- };
+ values.push(elems[i].getAttribute('data-value'));
- return AlphaPicker;
-});
+ }
+
+ return values;
+ }
+
+ focus() {
+
+ const element = this.options.element;
+ focusManager.autoFocus(element, true);
+ }
+
+ destroy() {
+
+ const element = this.options.element;
+ this.enabled(false);
+ element.classList.remove('focuscontainer-x');
+ this.options = null;
+ }
+ }
+
+/* eslint-enable indent */
+export default AlphaPicker;
diff --git a/src/components/cardbuilder/card.css b/src/components/cardbuilder/card.css
index c24fcf6ba6..d77fe5660c 100644
--- a/src/components/cardbuilder/card.css
+++ b/src/components/cardbuilder/card.css
@@ -192,9 +192,14 @@ button::-moz-focus-inner {
/* Needed in case this is a button */
display: block;
-
- /* Needed in case this is a button */
margin: 0 !important;
+ border: 0 !important;
+ padding: 0 !important;
+ cursor: pointer;
+ color: inherit;
+ width: 100%;
+ font-family: inherit;
+ font-size: inherit;
/* Needed in safari */
height: 100%;
@@ -203,19 +208,12 @@ button::-moz-focus-inner {
contain: strict;
}
-.cardContent-button {
- border: 0 !important;
- padding: 0 !important;
- cursor: pointer;
- color: inherit;
- width: 100%;
- vertical-align: middle;
- font-family: inherit;
- font-size: inherit;
+.cardContent:not(.defaultCardBackground) {
+ background-color: transparent;
}
-.cardContent-button:not(.defaultCardBackground) {
- background-color: transparent;
+.cardBox:not(.visualCardBox) .cardPadder {
+ background-color: #242424;
}
.visualCardBox .cardContent {
@@ -223,7 +221,8 @@ button::-moz-focus-inner {
border-bottom-right-radius: 0;
}
-.cardContent-shadow {
+.cardContent-shadow,
+.cardBox:not(.visualCardBox) .cardPadder {
box-shadow: 0 0.0725em 0.29em 0 rgba(0, 0, 0, 0.37);
}
diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js
index e540a40211..52d5ed202a 100644
--- a/src/components/cardbuilder/cardBuilder.js
+++ b/src/components/cardbuilder/cardBuilder.js
@@ -368,9 +368,7 @@ import 'programStyles';
let apiClient;
let lastServerId;
- for (let i = 0; i < items.length; i++) {
-
- let item = items[i];
+ for (const [i, item] of items.entries()) {
let serverId = item.ServerId || options.serverId;
if (serverId !== lastServerId) {
@@ -541,7 +539,7 @@ import 'programStyles';
imgType = 'Backdrop';
imgTag = item.ParentBackdropImageTags[0];
itemId = item.ParentBackdropItemId;
- } else if (item.ImageTags && item.ImageTags.Primary) {
+ } else if (item.ImageTags && item.ImageTags.Primary && (item.Type !== 'Episode' || item.ChildCount !== 0)) {
imgType = 'Primary';
imgTag = item.ImageTags.Primary;
height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null;
@@ -556,7 +554,10 @@ import 'programStyles';
coverImage = (Math.abs(primaryImageAspectRatio - uiAspect) / uiAspect) <= 0.2;
}
}
-
+ } else if (item.SeriesPrimaryImageTag) {
+ imgType = 'Primary';
+ imgTag = item.SeriesPrimaryImageTag;
+ itemId = item.SeriesId;
} else if (item.PrimaryImageTag) {
imgType = 'Primary';
imgTag = item.PrimaryImageTag;
@@ -577,10 +578,6 @@ import 'programStyles';
imgType = 'Primary';
imgTag = item.ParentPrimaryImageTag;
itemId = item.ParentPrimaryImageItemId;
- } else if (item.SeriesPrimaryImageTag) {
- imgType = 'Primary';
- imgTag = item.SeriesPrimaryImageTag;
- itemId = item.SeriesId;
} else if (item.AlbumId && item.AlbumPrimaryImageTag) {
imgType = 'Primary';
imgTag = item.AlbumPrimaryImageTag;
@@ -1370,9 +1367,6 @@ import 'programStyles';
let cardScalableClose = '';
let cardContentClass = 'cardContent';
- if (!options.cardLayout) {
- cardContentClass += ' cardContent-shadow';
- }
let blurhashAttrib = '';
if (blurhash && blurhash.length > 0) {
@@ -1380,21 +1374,20 @@ import 'programStyles';
}
if (layoutManager.tv) {
-
// Don't use the IMG tag with safari because it puts a white border around it
cardImageContainerOpen = imgUrl ? ('
') : ('
');
cardImageContainerClose = '
';
} else {
// Don't use the IMG tag with safari because it puts a white border around it
- cardImageContainerOpen = imgUrl ? ('